1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

env: Move to "thiserror" + added errors test case (#7584)

Solved Issue #7535 : Removed parse_errors to follow other commands standard with thiserror
This commit is contained in:
ValentinBoudevin 2025-03-30 11:21:57 +02:00 committed by GitHub
parent 903fa6ae88
commit aea23408fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 231 additions and 195 deletions

1
Cargo.lock generated
View file

@ -2727,6 +2727,7 @@ dependencies = [
"clap", "clap",
"nix", "nix",
"rust-ini", "rust-ini",
"thiserror 2.0.12",
"uucore", "uucore",
] ]

View file

@ -19,6 +19,7 @@ path = "src/env.rs"
[dependencies] [dependencies]
clap = { workspace = true } clap = { workspace = true }
rust-ini = { workspace = true } rust-ini = { workspace = true }
thiserror = { workspace = true }
uucore = { workspace = true, features = ["signals"] } uucore = { workspace = true, features = ["signals"] }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]

129
src/uu/env/src/env.rs vendored
View file

@ -6,7 +6,6 @@
// spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv spawnp SIGSEGV SIGBUS sigaction // spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv spawnp SIGSEGV SIGBUS sigaction
pub mod native_int_str; pub mod native_int_str;
pub mod parse_error;
pub mod split_iterator; pub mod split_iterator;
pub mod string_expander; pub mod string_expander;
pub mod string_parser; pub mod string_parser;
@ -40,6 +39,42 @@ use uucore::line_ending::LineEnding;
use uucore::signals::signal_by_name_or_value; use uucore::signals::signal_by_name_or_value;
use uucore::{format_usage, help_about, help_section, help_usage, show_warning}; use uucore::{format_usage, help_about, help_section, help_usage, show_warning};
use thiserror::Error;
#[derive(Debug, Error, PartialEq)]
pub enum EnvError {
#[error("no terminating quote in -S string")]
EnvMissingClosingQuote(usize, char),
#[error("invalid backslash at end of string in -S")]
EnvInvalidBackslashAtEndOfStringInMinusS(usize, String),
#[error("'\\c' must not appear in double-quoted -S string")]
EnvBackslashCNotAllowedInDoubleQuotes(usize),
#[error("invalid sequence '\\{}' in -S",.1)]
EnvInvalidSequenceBackslashXInMinusS(usize, char),
#[error("Missing closing brace")]
EnvParsingOfVariableMissingClosingBrace(usize),
#[error("Missing variable name")]
EnvParsingOfMissingVariable(usize),
#[error("Missing closing brace after default value at {}",.0)]
EnvParsingOfVariableMissingClosingBraceAfterValue(usize),
#[error("Unexpected character: '{}', expected variable name must not start with 0..9",.1)]
EnvParsingOfVariableUnexpectedNumber(usize, String),
#[error("Unexpected character: '{}', expected a closing brace ('}}') or colon (':')",.1)]
EnvParsingOfVariableExceptedBraceOrColon(usize, String),
#[error("")]
EnvReachedEnd,
#[error("")]
EnvContinueWithDelimiter,
#[error("{}{:?}",.0,.1)]
EnvInternalError(usize, string_parser::Error),
}
impl From<string_parser::Error> for EnvError {
fn from(value: string_parser::Error) -> Self {
EnvError::EnvInternalError(value.peek_position, value)
}
}
const ABOUT: &str = help_about!("env.md"); const ABOUT: &str = help_about!("env.md");
const USAGE: &str = help_usage!("env.md"); const USAGE: &str = help_usage!("env.md");
const AFTER_HELP: &str = help_section!("after help", "env.md"); const AFTER_HELP: &str = help_section!("after help", "env.md");
@ -273,20 +308,28 @@ pub fn uu_app() -> Command {
pub fn parse_args_from_str(text: &NativeIntStr) -> UResult<Vec<NativeIntString>> { pub fn parse_args_from_str(text: &NativeIntStr) -> UResult<Vec<NativeIntString>> {
split_iterator::split(text).map_err(|e| match e { split_iterator::split(text).map_err(|e| match e {
parse_error::ParseError::BackslashCNotAllowedInDoubleQuotes { pos: _ } => { EnvError::EnvBackslashCNotAllowedInDoubleQuotes(_) => USimpleError::new(125, e.to_string()),
USimpleError::new(125, "'\\c' must not appear in double-quoted -S string") EnvError::EnvInvalidBackslashAtEndOfStringInMinusS(_, _) => {
USimpleError::new(125, e.to_string())
} }
parse_error::ParseError::InvalidBackslashAtEndOfStringInMinusS { pos: _, quoting: _ } => { EnvError::EnvInvalidSequenceBackslashXInMinusS(_, _) => {
USimpleError::new(125, "invalid backslash at end of string in -S") USimpleError::new(125, e.to_string())
} }
parse_error::ParseError::InvalidSequenceBackslashXInMinusS { pos: _, c } => { EnvError::EnvMissingClosingQuote(_, _) => USimpleError::new(125, e.to_string()),
USimpleError::new(125, format!("invalid sequence '\\{c}' in -S")) EnvError::EnvParsingOfVariableMissingClosingBrace(pos) => {
USimpleError::new(125, format!("variable name issue (at {pos}): {}", e))
} }
parse_error::ParseError::MissingClosingQuote { pos: _, c: _ } => { EnvError::EnvParsingOfMissingVariable(pos) => {
USimpleError::new(125, "no terminating quote in -S string") USimpleError::new(125, format!("variable name issue (at {pos}): {}", e))
} }
parse_error::ParseError::ParsingOfVariableNameFailed { pos, msg } => { EnvError::EnvParsingOfVariableMissingClosingBraceAfterValue(pos) => {
USimpleError::new(125, format!("variable name issue (at {pos}): {msg}",)) USimpleError::new(125, format!("variable name issue (at {pos}): {}", e))
}
EnvError::EnvParsingOfVariableUnexpectedNumber(pos, _) => {
USimpleError::new(125, format!("variable name issue (at {pos}): {}", e))
}
EnvError::EnvParsingOfVariableExceptedBraceOrColon(pos, _) => {
USimpleError::new(125, format!("variable name issue (at {pos}): {}", e))
} }
_ => USimpleError::new(125, format!("Error: {e:?}")), _ => USimpleError::new(125, format!("Error: {e:?}")),
}) })
@ -771,4 +814,68 @@ mod tests {
parse_args_from_str(&NCvt::convert(r#"-i A='B \' C'"#)).unwrap() parse_args_from_str(&NCvt::convert(r#"-i A='B \' C'"#)).unwrap()
); );
} }
#[test]
fn test_error_cases() {
// Test EnvBackslashCNotAllowedInDoubleQuotes
let result = parse_args_from_str(&NCvt::convert(r#"sh -c "echo \c""#));
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"'\\c' must not appear in double-quoted -S string"
);
// Test EnvInvalidBackslashAtEndOfStringInMinusS
let result = parse_args_from_str(&NCvt::convert(r#"sh -c "echo \"#));
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"no terminating quote in -S string"
);
// Test EnvInvalidSequenceBackslashXInMinusS
let result = parse_args_from_str(&NCvt::convert(r#"sh -c "echo \x""#));
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("invalid sequence '\\x' in -S")
);
// Test EnvMissingClosingQuote
let result = parse_args_from_str(&NCvt::convert(r#"sh -c "echo "#));
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"no terminating quote in -S string"
);
// Test variable-related errors
let result = parse_args_from_str(&NCvt::convert(r#"echo ${FOO"#));
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("variable name issue (at 10): Missing closing brace")
);
let result = parse_args_from_str(&NCvt::convert(r#"echo ${FOO:-value"#));
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("variable name issue (at 17): Missing closing brace after default value")
);
let result = parse_args_from_str(&NCvt::convert(r#"echo ${1FOO}"#));
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("variable name issue (at 7): Unexpected character: '1', expected variable name must not start with 0..9"));
let result = parse_args_from_str(&NCvt::convert(r#"echo ${FOO?}"#));
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("variable name issue (at 10): Unexpected character: '?', expected a closing brace ('}') or colon (':')"));
}
} }

View file

@ -1,55 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::fmt;
use crate::string_parser;
/// An error returned when string arg splitting fails.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ParseError {
MissingClosingQuote {
pos: usize,
c: char,
},
InvalidBackslashAtEndOfStringInMinusS {
pos: usize,
quoting: String,
},
BackslashCNotAllowedInDoubleQuotes {
pos: usize,
},
InvalidSequenceBackslashXInMinusS {
pos: usize,
c: char,
},
ParsingOfVariableNameFailed {
pos: usize,
msg: String,
},
InternalError {
pos: usize,
sub_err: string_parser::Error,
},
ReachedEnd,
ContinueWithDelimiter,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(format!("{self:?}").as_str())
}
}
impl std::error::Error for ParseError {}
impl From<string_parser::Error> for ParseError {
fn from(value: string_parser::Error) -> Self {
Self::InternalError {
pos: value.peek_position,
sub_err: value,
}
}
}

View file

@ -20,11 +20,11 @@
use std::borrow::Cow; use std::borrow::Cow;
use crate::EnvError;
use crate::native_int_str::NativeCharInt; use crate::native_int_str::NativeCharInt;
use crate::native_int_str::NativeIntStr; use crate::native_int_str::NativeIntStr;
use crate::native_int_str::NativeIntString; use crate::native_int_str::NativeIntString;
use crate::native_int_str::from_native_int_representation; use crate::native_int_str::from_native_int_representation;
use crate::parse_error::ParseError;
use crate::string_expander::StringExpander; use crate::string_expander::StringExpander;
use crate::string_parser::StringParser; use crate::string_parser::StringParser;
use crate::variable_parser::VariableParser; use crate::variable_parser::VariableParser;
@ -62,14 +62,14 @@ impl<'a> SplitIterator<'a> {
} }
} }
fn skip_one(&mut self) -> Result<(), ParseError> { fn skip_one(&mut self) -> Result<(), EnvError> {
self.expander self.expander
.get_parser_mut() .get_parser_mut()
.consume_one_ascii_or_all_non_ascii()?; .consume_one_ascii_or_all_non_ascii()?;
Ok(()) Ok(())
} }
fn take_one(&mut self) -> Result<(), ParseError> { fn take_one(&mut self) -> Result<(), EnvError> {
Ok(self.expander.take_one()?) Ok(self.expander.take_one()?)
} }
@ -94,7 +94,7 @@ impl<'a> SplitIterator<'a> {
self.expander.get_parser_mut() self.expander.get_parser_mut()
} }
fn substitute_variable<'x>(&'x mut self) -> Result<(), ParseError> { fn substitute_variable<'x>(&'x mut self) -> Result<(), EnvError> {
let mut var_parse = VariableParser::<'a, '_> { let mut var_parse = VariableParser::<'a, '_> {
parser: self.get_parser_mut(), parser: self.get_parser_mut(),
}; };
@ -116,7 +116,7 @@ impl<'a> SplitIterator<'a> {
Ok(()) Ok(())
} }
fn check_and_replace_ascii_escape_code(&mut self, c: char) -> Result<bool, ParseError> { fn check_and_replace_ascii_escape_code(&mut self, c: char) -> Result<bool, EnvError> {
if let Some(replace) = REPLACEMENTS.iter().find(|&x| x.0 == c) { if let Some(replace) = REPLACEMENTS.iter().find(|&x| x.0 == c) {
self.skip_one()?; self.skip_one()?;
self.push_char_to_word(replace.1); self.push_char_to_word(replace.1);
@ -126,24 +126,24 @@ impl<'a> SplitIterator<'a> {
Ok(false) Ok(false)
} }
fn make_invalid_sequence_backslash_xin_minus_s(&self, c: char) -> ParseError { fn make_invalid_sequence_backslash_xin_minus_s(&self, c: char) -> EnvError {
ParseError::InvalidSequenceBackslashXInMinusS { EnvError::EnvInvalidSequenceBackslashXInMinusS(
pos: self.expander.get_parser().get_peek_position(), self.expander.get_parser().get_peek_position(),
c, c,
} )
} }
fn state_root(&mut self) -> Result<(), ParseError> { fn state_root(&mut self) -> Result<(), EnvError> {
loop { loop {
match self.state_delimiter() { match self.state_delimiter() {
Err(ParseError::ContinueWithDelimiter) => {} Err(EnvError::EnvContinueWithDelimiter) => {}
Err(ParseError::ReachedEnd) => return Ok(()), Err(EnvError::EnvReachedEnd) => return Ok(()),
result => return result, result => return result,
} }
} }
} }
fn state_delimiter(&mut self) -> Result<(), ParseError> { fn state_delimiter(&mut self) -> Result<(), EnvError> {
loop { loop {
match self.get_current_char() { match self.get_current_char() {
None => return Ok(()), None => return Ok(()),
@ -166,12 +166,12 @@ impl<'a> SplitIterator<'a> {
} }
} }
fn state_delimiter_backslash(&mut self) -> Result<(), ParseError> { fn state_delimiter_backslash(&mut self) -> Result<(), EnvError> {
match self.get_current_char() { match self.get_current_char() {
None => Err(ParseError::InvalidBackslashAtEndOfStringInMinusS { None => Err(EnvError::EnvInvalidBackslashAtEndOfStringInMinusS(
pos: self.get_parser().get_peek_position(), self.get_parser().get_peek_position(),
quoting: "Delimiter".into(), "Delimiter".into(),
}), )),
Some('_') | Some(NEW_LINE) => { Some('_') | Some(NEW_LINE) => {
self.skip_one()?; self.skip_one()?;
Ok(()) Ok(())
@ -181,18 +181,18 @@ impl<'a> SplitIterator<'a> {
self.take_one()?; self.take_one()?;
self.state_unquoted() self.state_unquoted()
} }
Some('c') => Err(ParseError::ReachedEnd), Some('c') => Err(EnvError::EnvReachedEnd),
Some(c) if self.check_and_replace_ascii_escape_code(c)? => self.state_unquoted(), Some(c) if self.check_and_replace_ascii_escape_code(c)? => self.state_unquoted(),
Some(c) => Err(self.make_invalid_sequence_backslash_xin_minus_s(c)), Some(c) => Err(self.make_invalid_sequence_backslash_xin_minus_s(c)),
} }
} }
fn state_unquoted(&mut self) -> Result<(), ParseError> { fn state_unquoted(&mut self) -> Result<(), EnvError> {
loop { loop {
match self.get_current_char() { match self.get_current_char() {
None => { None => {
self.push_word_to_words(); self.push_word_to_words();
return Err(ParseError::ReachedEnd); return Err(EnvError::EnvReachedEnd);
} }
Some(DOLLAR) => { Some(DOLLAR) => {
self.substitute_variable()?; self.substitute_variable()?;
@ -221,12 +221,12 @@ impl<'a> SplitIterator<'a> {
} }
} }
fn state_unquoted_backslash(&mut self) -> Result<(), ParseError> { fn state_unquoted_backslash(&mut self) -> Result<(), EnvError> {
match self.get_current_char() { match self.get_current_char() {
None => Err(ParseError::InvalidBackslashAtEndOfStringInMinusS { None => Err(EnvError::EnvInvalidBackslashAtEndOfStringInMinusS(
pos: self.get_parser().get_peek_position(), self.get_parser().get_peek_position(),
quoting: "Unquoted".into(), "Unquoted".into(),
}), )),
Some(NEW_LINE) => { Some(NEW_LINE) => {
self.skip_one()?; self.skip_one()?;
Ok(()) Ok(())
@ -234,11 +234,11 @@ impl<'a> SplitIterator<'a> {
Some('_') => { Some('_') => {
self.skip_one()?; self.skip_one()?;
self.push_word_to_words(); self.push_word_to_words();
Err(ParseError::ContinueWithDelimiter) Err(EnvError::EnvContinueWithDelimiter)
} }
Some('c') => { Some('c') => {
self.push_word_to_words(); self.push_word_to_words();
Err(ParseError::ReachedEnd) Err(EnvError::EnvReachedEnd)
} }
Some(DOLLAR) | Some(BACKSLASH) | Some(SINGLE_QUOTES) | Some(DOUBLE_QUOTES) => { Some(DOLLAR) | Some(BACKSLASH) | Some(SINGLE_QUOTES) | Some(DOUBLE_QUOTES) => {
self.take_one()?; self.take_one()?;
@ -249,14 +249,14 @@ impl<'a> SplitIterator<'a> {
} }
} }
fn state_single_quoted(&mut self) -> Result<(), ParseError> { fn state_single_quoted(&mut self) -> Result<(), EnvError> {
loop { loop {
match self.get_current_char() { match self.get_current_char() {
None => { None => {
return Err(ParseError::MissingClosingQuote { return Err(EnvError::EnvMissingClosingQuote(
pos: self.get_parser().get_peek_position(), self.get_parser().get_peek_position(),
c: '\'', '\'',
}); ));
} }
Some(SINGLE_QUOTES) => { Some(SINGLE_QUOTES) => {
self.skip_one()?; self.skip_one()?;
@ -273,12 +273,12 @@ impl<'a> SplitIterator<'a> {
} }
} }
fn split_single_quoted_backslash(&mut self) -> Result<(), ParseError> { fn split_single_quoted_backslash(&mut self) -> Result<(), EnvError> {
match self.get_current_char() { match self.get_current_char() {
None => Err(ParseError::MissingClosingQuote { None => Err(EnvError::EnvMissingClosingQuote(
pos: self.get_parser().get_peek_position(), self.get_parser().get_peek_position(),
c: '\'', '\'',
}), )),
Some(NEW_LINE) => { Some(NEW_LINE) => {
self.skip_one()?; self.skip_one()?;
Ok(()) Ok(())
@ -299,14 +299,14 @@ impl<'a> SplitIterator<'a> {
} }
} }
fn state_double_quoted(&mut self) -> Result<(), ParseError> { fn state_double_quoted(&mut self) -> Result<(), EnvError> {
loop { loop {
match self.get_current_char() { match self.get_current_char() {
None => { None => {
return Err(ParseError::MissingClosingQuote { return Err(EnvError::EnvMissingClosingQuote(
pos: self.get_parser().get_peek_position(), self.get_parser().get_peek_position(),
c: '"', '"',
}); ));
} }
Some(DOLLAR) => { Some(DOLLAR) => {
self.substitute_variable()?; self.substitute_variable()?;
@ -326,12 +326,12 @@ impl<'a> SplitIterator<'a> {
} }
} }
fn state_double_quoted_backslash(&mut self) -> Result<(), ParseError> { fn state_double_quoted_backslash(&mut self) -> Result<(), EnvError> {
match self.get_current_char() { match self.get_current_char() {
None => Err(ParseError::MissingClosingQuote { None => Err(EnvError::EnvMissingClosingQuote(
pos: self.get_parser().get_peek_position(), self.get_parser().get_peek_position(),
c: '"', '"',
}), )),
Some(NEW_LINE) => { Some(NEW_LINE) => {
self.skip_one()?; self.skip_one()?;
Ok(()) Ok(())
@ -340,18 +340,18 @@ impl<'a> SplitIterator<'a> {
self.take_one()?; self.take_one()?;
Ok(()) Ok(())
} }
Some('c') => Err(ParseError::BackslashCNotAllowedInDoubleQuotes { Some('c') => Err(EnvError::EnvBackslashCNotAllowedInDoubleQuotes(
pos: self.get_parser().get_peek_position(), self.get_parser().get_peek_position(),
}), )),
Some(c) if self.check_and_replace_ascii_escape_code(c)? => Ok(()), Some(c) if self.check_and_replace_ascii_escape_code(c)? => Ok(()),
Some(c) => Err(self.make_invalid_sequence_backslash_xin_minus_s(c)), Some(c) => Err(self.make_invalid_sequence_backslash_xin_minus_s(c)),
} }
} }
fn state_comment(&mut self) -> Result<(), ParseError> { fn state_comment(&mut self) -> Result<(), EnvError> {
loop { loop {
match self.get_current_char() { match self.get_current_char() {
None => return Err(ParseError::ReachedEnd), None => return Err(EnvError::EnvReachedEnd),
Some(NEW_LINE) => { Some(NEW_LINE) => {
self.skip_one()?; self.skip_one()?;
return Ok(()); return Ok(());
@ -363,13 +363,13 @@ impl<'a> SplitIterator<'a> {
} }
} }
pub fn split(mut self) -> Result<Vec<NativeIntString>, ParseError> { pub fn split(mut self) -> Result<Vec<NativeIntString>, EnvError> {
self.state_root()?; self.state_root()?;
Ok(self.words) Ok(self.words)
} }
} }
pub fn split(s: &NativeIntStr) -> Result<Vec<NativeIntString>, ParseError> { pub fn split(s: &NativeIntStr) -> Result<Vec<NativeIntString>, EnvError> {
let split_args = SplitIterator::new(s).split()?; let split_args = SplitIterator::new(s).split()?;
Ok(split_args) Ok(split_args)
} }

View file

@ -5,7 +5,8 @@
use std::ops::Range; use std::ops::Range;
use crate::{native_int_str::NativeIntStr, parse_error::ParseError, string_parser::StringParser}; use crate::EnvError;
use crate::{native_int_str::NativeIntStr, string_parser::StringParser};
pub struct VariableParser<'a, 'b> { pub struct VariableParser<'a, 'b> {
pub parser: &'b mut StringParser<'a>, pub parser: &'b mut StringParser<'a>,
@ -16,28 +17,26 @@ impl<'a> VariableParser<'a, '_> {
self.parser.peek().ok() self.parser.peek().ok()
} }
fn check_variable_name_start(&self) -> Result<(), ParseError> { fn check_variable_name_start(&self) -> Result<(), EnvError> {
if let Some(c) = self.get_current_char() { if let Some(c) = self.get_current_char() {
if c.is_ascii_digit() { if c.is_ascii_digit() {
return Err(ParseError::ParsingOfVariableNameFailed { return Err(EnvError::EnvParsingOfVariableUnexpectedNumber(
pos: self.parser.get_peek_position(), self.parser.get_peek_position(),
msg: format!( c.to_string(),
"Unexpected character: '{c}', expected variable name must not start with 0..9" ));
),
});
} }
} }
Ok(()) Ok(())
} }
fn skip_one(&mut self) -> Result<(), ParseError> { fn skip_one(&mut self) -> Result<(), EnvError> {
self.parser.consume_chunk()?; self.parser.consume_chunk()?;
Ok(()) Ok(())
} }
fn parse_braced_variable_name( fn parse_braced_variable_name(
&mut self, &mut self,
) -> Result<(&'a NativeIntStr, Option<&'a NativeIntStr>), ParseError> { ) -> Result<(&'a NativeIntStr, Option<&'a NativeIntStr>), EnvError> {
let pos_start = self.parser.get_peek_position(); let pos_start = self.parser.get_peek_position();
self.check_variable_name_start()?; self.check_variable_name_start()?;
@ -46,10 +45,9 @@ impl<'a> VariableParser<'a, '_> {
loop { loop {
match self.get_current_char() { match self.get_current_char() {
None => { None => {
return Err(ParseError::ParsingOfVariableNameFailed { return Err(EnvError::EnvParsingOfVariableMissingClosingBrace(
pos: self.parser.get_peek_position(), self.parser.get_peek_position(),
msg: "Missing closing brace".into(), ));
});
} }
Some(c) if !c.is_ascii() || c.is_ascii_alphanumeric() || c == '_' => { Some(c) if !c.is_ascii() || c.is_ascii_alphanumeric() || c == '_' => {
self.skip_one()?; self.skip_one()?;
@ -59,10 +57,11 @@ impl<'a> VariableParser<'a, '_> {
loop { loop {
match self.get_current_char() { match self.get_current_char() {
None => { None => {
return Err(ParseError::ParsingOfVariableNameFailed { return Err(
pos: self.parser.get_peek_position(), EnvError::EnvParsingOfVariableMissingClosingBraceAfterValue(
msg: "Missing closing brace after default value".into(), self.parser.get_peek_position(),
}); ),
);
} }
Some('}') => { Some('}') => {
default_end = Some(self.parser.get_peek_position()); default_end = Some(self.parser.get_peek_position());
@ -83,12 +82,10 @@ impl<'a> VariableParser<'a, '_> {
break; break;
} }
Some(c) => { Some(c) => {
return Err(ParseError::ParsingOfVariableNameFailed { return Err(EnvError::EnvParsingOfVariableExceptedBraceOrColon(
pos: self.parser.get_peek_position(), self.parser.get_peek_position(),
msg: format!( c.to_string(),
"Unexpected character: '{c}', expected a closing brace ('}}') or colon (':')" ));
),
});
} }
}; };
} }
@ -110,7 +107,7 @@ impl<'a> VariableParser<'a, '_> {
Ok((varname, default_opt)) Ok((varname, default_opt))
} }
fn parse_unbraced_variable_name(&mut self) -> Result<&'a NativeIntStr, ParseError> { fn parse_unbraced_variable_name(&mut self) -> Result<&'a NativeIntStr, EnvError> {
let pos_start = self.parser.get_peek_position(); let pos_start = self.parser.get_peek_position();
self.check_variable_name_start()?; self.check_variable_name_start()?;
@ -128,10 +125,7 @@ impl<'a> VariableParser<'a, '_> {
let pos_end = self.parser.get_peek_position(); let pos_end = self.parser.get_peek_position();
if pos_end == pos_start { if pos_end == pos_start {
return Err(ParseError::ParsingOfVariableNameFailed { return Err(EnvError::EnvParsingOfMissingVariable(pos_start));
pos: pos_start,
msg: "Missing variable name".into(),
});
} }
let varname = self.parser.substring(&Range { let varname = self.parser.substring(&Range {
@ -144,15 +138,14 @@ impl<'a> VariableParser<'a, '_> {
pub fn parse_variable( pub fn parse_variable(
&mut self, &mut self,
) -> Result<(&'a NativeIntStr, Option<&'a NativeIntStr>), ParseError> { ) -> Result<(&'a NativeIntStr, Option<&'a NativeIntStr>), EnvError> {
self.skip_one()?; self.skip_one()?;
let (name, default) = match self.get_current_char() { let (name, default) = match self.get_current_char() {
None => { None => {
return Err(ParseError::ParsingOfVariableNameFailed { return Err(EnvError::EnvParsingOfMissingVariable(
pos: self.parser.get_peek_position(), self.parser.get_peek_position(),
msg: "missing variable name".into(), ));
});
} }
Some('{') => { Some('{') => {
self.skip_one()?; self.skip_one()?;

View file

@ -1017,10 +1017,12 @@ mod tests_split_iterator {
use std::ffi::OsString; use std::ffi::OsString;
use env::native_int_str::{Convert, NCvt, from_native_int_representation_owned}; use env::{
use env::parse_error::ParseError; EnvError,
native_int_str::{Convert, NCvt, from_native_int_representation_owned},
};
fn split(input: &str) -> Result<Vec<OsString>, ParseError> { fn split(input: &str) -> Result<Vec<OsString>, EnvError> {
::env::split_iterator::split(&NCvt::convert(input)).map(|vec| { ::env::split_iterator::split(&NCvt::convert(input)).map(|vec| {
vec.into_iter() vec.into_iter()
.map(from_native_int_representation_owned) .map(from_native_int_representation_owned)
@ -1127,24 +1129,24 @@ mod tests_split_iterator {
fn split_trailing_backslash() { fn split_trailing_backslash() {
assert_eq!( assert_eq!(
split("\\"), split("\\"),
Err(ParseError::InvalidBackslashAtEndOfStringInMinusS { Err(EnvError::EnvInvalidBackslashAtEndOfStringInMinusS(
pos: 1, 1,
quoting: "Delimiter".into() "Delimiter".into()
}) ))
); );
assert_eq!( assert_eq!(
split(" \\"), split(" \\"),
Err(ParseError::InvalidBackslashAtEndOfStringInMinusS { Err(EnvError::EnvInvalidBackslashAtEndOfStringInMinusS(
pos: 2, 2,
quoting: "Delimiter".into() "Delimiter".into()
}) ))
); );
assert_eq!( assert_eq!(
split("a\\"), split("a\\"),
Err(ParseError::InvalidBackslashAtEndOfStringInMinusS { Err(EnvError::EnvInvalidBackslashAtEndOfStringInMinusS(
pos: 2, 2,
quoting: "Unquoted".into() "Unquoted".into()
}) ))
); );
} }
@ -1152,26 +1154,14 @@ mod tests_split_iterator {
fn split_errors() { fn split_errors() {
assert_eq!( assert_eq!(
split("'abc"), split("'abc"),
Err(ParseError::MissingClosingQuote { pos: 4, c: '\'' }) Err(EnvError::EnvMissingClosingQuote(4, '\''))
);
assert_eq!(
split("\""),
Err(ParseError::MissingClosingQuote { pos: 1, c: '"' })
);
assert_eq!(
split("'\\"),
Err(ParseError::MissingClosingQuote { pos: 2, c: '\'' })
);
assert_eq!(
split("'\\"),
Err(ParseError::MissingClosingQuote { pos: 2, c: '\'' })
); );
assert_eq!(split("\""), Err(EnvError::EnvMissingClosingQuote(1, '"')));
assert_eq!(split("'\\"), Err(EnvError::EnvMissingClosingQuote(2, '\'')));
assert_eq!(split("'\\"), Err(EnvError::EnvMissingClosingQuote(2, '\'')));
assert_eq!( assert_eq!(
split(r#""$""#), split(r#""$""#),
Err(ParseError::ParsingOfVariableNameFailed { Err(EnvError::EnvParsingOfMissingVariable(2)),
pos: 2,
msg: "Missing variable name".into()
}),
); );
} }
@ -1179,26 +1169,25 @@ mod tests_split_iterator {
fn split_error_fail_with_unknown_escape_sequences() { fn split_error_fail_with_unknown_escape_sequences() {
assert_eq!( assert_eq!(
split("\\a"), split("\\a"),
Err(ParseError::InvalidSequenceBackslashXInMinusS { pos: 1, c: 'a' }) Err(EnvError::EnvInvalidSequenceBackslashXInMinusS(1, 'a'))
); );
assert_eq!( assert_eq!(
split("\"\\a\""), split("\"\\a\""),
Err(ParseError::InvalidSequenceBackslashXInMinusS { pos: 2, c: 'a' }) Err(EnvError::EnvInvalidSequenceBackslashXInMinusS(2, 'a'))
); );
assert_eq!( assert_eq!(
split("'\\a'"), split("'\\a'"),
Err(ParseError::InvalidSequenceBackslashXInMinusS { pos: 2, c: 'a' }) Err(EnvError::EnvInvalidSequenceBackslashXInMinusS(2, 'a'))
); );
assert_eq!( assert_eq!(
split(r#""\a""#), split(r#""\a""#),
Err(ParseError::InvalidSequenceBackslashXInMinusS { pos: 2, c: 'a' }) Err(EnvError::EnvInvalidSequenceBackslashXInMinusS(2, 'a'))
); );
assert_eq!( assert_eq!(
split(r"\🦉"), split(r"\🦉"),
Err(ParseError::InvalidSequenceBackslashXInMinusS { Err(EnvError::EnvInvalidSequenceBackslashXInMinusS(
pos: 1, 1, '\u{FFFD}'
c: '\u{FFFD}' ))
})
); );
} }