diff --git a/src/uucore/src/lib/features/format/argument.rs b/src/uucore/src/lib/features/format/argument.rs index d02ffde04..105092ebf 100644 --- a/src/uucore/src/lib/features/format/argument.rs +++ b/src/uucore/src/lib/features/format/argument.rs @@ -172,7 +172,7 @@ impl<'a> FormatArguments<'a> { } } -fn extract_value(p: Result>, input: &str) -> T { +fn extract_value(p: Result>, input: &str) -> T { match p { Ok(v) => v, Err(e) => { diff --git a/src/uucore/src/lib/features/parser/num_parser.rs b/src/uucore/src/lib/features/parser/num_parser.rs index 751373654..5f7d89538 100644 --- a/src/uucore/src/lib/features/parser/num_parser.rs +++ b/src/uucore/src/lib/features/parser/num_parser.rs @@ -109,12 +109,12 @@ impl Base { /// Type returned if a number could not be parsed in its entirety #[derive(Debug, PartialEq)] -pub enum ExtendedParserError<'a, T> { +pub enum ExtendedParserError { /// The input as a whole makes no sense NotNumeric, /// The beginning of the input made sense and has been parsed, /// while the remaining doesn't. - PartialMatch(T, &'a str), + PartialMatch(T, String), /// The value has overflowed the type storage. The returned value /// is saturated (e.g. positive or negative infinity, or min/max /// value for the integer type). @@ -124,7 +124,7 @@ pub enum ExtendedParserError<'a, T> { Underflow(T), } -impl<'a, T> ExtendedParserError<'a, T> +impl ExtendedParserError where T: Zero, { @@ -143,12 +143,12 @@ where /// conversion. fn map( self, - f: impl FnOnce(T) -> Result>, - ) -> ExtendedParserError<'a, U> + f: impl FnOnce(T) -> Result>, + ) -> ExtendedParserError where U: Zero, { - fn extract(v: Result>) -> U + fn extract(v: Result>) -> U where U: Zero, { @@ -172,15 +172,15 @@ where /// and `f64` float, where octal and binary formats are not allowed. pub trait ExtendedParser { // We pick a hopefully different name for our parser, to avoid clash with standard traits. - fn extended_parse(input: &str) -> Result> + fn extended_parse(input: &str) -> Result> where Self: Sized; } impl ExtendedParser for i64 { /// Parse a number as i64. No fractional part is allowed. - fn extended_parse(input: &str) -> Result> { - fn into_i64<'a>(ebd: ExtendedBigDecimal) -> Result> { + fn extended_parse(input: &str) -> Result> { + fn into_i64(ebd: ExtendedBigDecimal) -> Result> { match ebd { ExtendedBigDecimal::BigDecimal(bd) => { let (digits, scale) = bd.into_bigint_and_scale(); @@ -214,8 +214,8 @@ impl ExtendedParser for i64 { impl ExtendedParser for u64 { /// Parse a number as u64. No fractional part is allowed. - fn extended_parse(input: &str) -> Result> { - fn into_u64<'a>(ebd: ExtendedBigDecimal) -> Result> { + fn extended_parse(input: &str) -> Result> { + fn into_u64(ebd: ExtendedBigDecimal) -> Result> { match ebd { ExtendedBigDecimal::BigDecimal(bd) => { let (digits, scale) = bd.into_bigint_and_scale(); @@ -251,8 +251,8 @@ impl ExtendedParser for u64 { impl ExtendedParser for f64 { /// Parse a number as f64 - fn extended_parse(input: &str) -> Result> { - fn into_f64<'a>(ebd: ExtendedBigDecimal) -> Result> { + fn extended_parse(input: &str) -> Result> { + fn into_f64(ebd: ExtendedBigDecimal) -> Result> { // TODO: _Some_ of this is generic, so this should probably be implemented as an ExtendedBigDecimal trait (ToPrimitive). let v = match ebd { ExtendedBigDecimal::BigDecimal(bd) => { @@ -285,7 +285,7 @@ impl ExtendedParser for ExtendedBigDecimal { /// Parse a number as an ExtendedBigDecimal fn extended_parse( input: &str, - ) -> Result> { + ) -> Result> { parse(input, ParseTarget::Decimal, &[]) } } @@ -349,11 +349,11 @@ fn parse_suffix_multiplier<'a>(str: &'a str, allowed_suffixes: &[(char, u32)]) - (1, str) } -fn parse_special_value<'a>( - input: &'a str, +fn parse_special_value( + input: &str, negative: bool, allowed_suffixes: &[(char, u32)], -) -> Result> { +) -> Result> { let input_lc = input.to_ascii_lowercase(); // Array of ("String to match", return value when sign positive, when sign negative) @@ -376,7 +376,7 @@ fn parse_special_value<'a>( return if rest.is_empty() { Ok(special) } else { - Err(ExtendedParserError::PartialMatch(special, rest)) + Err(ExtendedParserError::PartialMatch(special, rest.to_string())) }; } } @@ -386,7 +386,7 @@ fn parse_special_value<'a>( /// Underflow/Overflow errors always contain 0 or infinity. /// overflow: true for overflow, false for underflow. -fn make_error<'a>(overflow: bool, negative: bool) -> ExtendedParserError<'a, ExtendedBigDecimal> { +fn make_error(overflow: bool, negative: bool) -> ExtendedParserError { let mut v = if overflow { ExtendedBigDecimal::Infinity } else { @@ -468,13 +468,13 @@ fn pow_with_context(bd: &BigDecimal, exp: i64, ctx: &Context) -> BigDecimal { } /// Construct an [`ExtendedBigDecimal`] based on parsed data -fn construct_extended_big_decimal<'a>( +fn construct_extended_big_decimal( digits: BigUint, negative: bool, base: Base, scale: i64, exponent: BigInt, -) -> Result> { +) -> Result> { if digits == BigUint::zero() { // Return return 0 if the digits are zero. In particular, we do not ever // return Overflow/Underflow errors in that case. @@ -541,11 +541,11 @@ pub(crate) enum ParseTarget { Duration, } -pub(crate) fn parse<'a>( - input: &'a str, +pub(crate) fn parse( + input: &str, target: ParseTarget, allowed_suffixes: &[(char, u32)], -) -> Result> { +) -> Result> { // Note: literals with ' and " prefixes are parsed earlier on in argument parsing, // before UTF-8 conversion. @@ -604,7 +604,7 @@ pub(crate) fn parse<'a>( } else { ExtendedBigDecimal::zero() }; - return Err(ExtendedParserError::PartialMatch(ebd, partial)); + return Err(ExtendedParserError::PartialMatch(ebd, partial.to_string())); } return if target == ParseTarget::Integral { @@ -628,7 +628,7 @@ pub(crate) fn parse<'a>( } else { Err(ExtendedParserError::PartialMatch( ebd_result.unwrap_or_else(|e| e.extract()), - rest, + rest.to_string(), )) } } @@ -674,14 +674,14 @@ mod tests { u64::extended_parse(""), Err(ExtendedParserError::NotNumeric) )); - assert!(matches!( + assert_eq!( u64::extended_parse("123.15"), - Err(ExtendedParserError::PartialMatch(123, ".15")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(123, ".15".to_string())) + ); + assert_eq!( u64::extended_parse("123e10"), - Err(ExtendedParserError::PartialMatch(123, "e10")) - )); + Err(ExtendedParserError::PartialMatch(123, "e10".to_string())) + ); } #[test] @@ -695,18 +695,18 @@ mod tests { )); assert_eq!(Ok(i64::MAX), i64::extended_parse(&format!("{}", i64::MAX))); assert_eq!(Ok(i64::MIN), i64::extended_parse(&format!("{}", i64::MIN))); - assert!(matches!( + assert_eq!( i64::extended_parse(&format!("{}", u64::MAX)), Err(ExtendedParserError::Overflow(i64::MAX)) - )); + ); assert!(matches!( i64::extended_parse(&format!("{}", i64::MAX as u64 + 1)), Err(ExtendedParserError::Overflow(i64::MAX)) )); - assert!(matches!( + assert_eq!( i64::extended_parse("-123e10"), - Err(ExtendedParserError::PartialMatch(-123, "e10")) - )); + Err(ExtendedParserError::PartialMatch(-123, "e10".to_string())) + ); assert!(matches!( i64::extended_parse(&format!("{}", -(u64::MAX as i128))), Err(ExtendedParserError::Overflow(i64::MIN)) @@ -758,20 +758,34 @@ mod tests { Ok(0.15), f64::extended_parse(".150000000000000000000000000231313") ); - assert!(matches!(f64::extended_parse("123.15e"), - Err(ExtendedParserError::PartialMatch(f, "e")) if f == 123.15)); - assert!(matches!(f64::extended_parse("123.15E"), - Err(ExtendedParserError::PartialMatch(f, "E")) if f == 123.15)); - assert!(matches!(f64::extended_parse("123.15e-"), - Err(ExtendedParserError::PartialMatch(f, "e-")) if f == 123.15)); - assert!(matches!(f64::extended_parse("123.15e+"), - Err(ExtendedParserError::PartialMatch(f, "e+")) if f == 123.15)); - assert!(matches!(f64::extended_parse("123.15e."), - Err(ExtendedParserError::PartialMatch(f, "e.")) if f == 123.15)); - assert!(matches!(f64::extended_parse("1.2.3"), - Err(ExtendedParserError::PartialMatch(f, ".3")) if f == 1.2)); - assert!(matches!(f64::extended_parse("123.15p5"), - Err(ExtendedParserError::PartialMatch(f, "p5")) if f == 123.15)); + assert_eq!( + f64::extended_parse("123.15e"), + Err(ExtendedParserError::PartialMatch(123.15, "e".to_string())) + ); + assert_eq!( + f64::extended_parse("123.15E"), + Err(ExtendedParserError::PartialMatch(123.15, "E".to_string())) + ); + assert_eq!( + f64::extended_parse("123.15e-"), + Err(ExtendedParserError::PartialMatch(123.15, "e-".to_string())) + ); + assert_eq!( + f64::extended_parse("123.15e+"), + Err(ExtendedParserError::PartialMatch(123.15, "e+".to_string())) + ); + assert_eq!( + f64::extended_parse("123.15e."), + Err(ExtendedParserError::PartialMatch(123.15, "e.".to_string())) + ); + assert_eq!( + f64::extended_parse("1.2.3"), + Err(ExtendedParserError::PartialMatch(1.2, ".3".to_string())) + ); + assert_eq!( + f64::extended_parse("123.15p5"), + Err(ExtendedParserError::PartialMatch(123.15, "p5".to_string())) + ); // Minus zero. 0.0 == -0.0 so we explicitly check the sign. assert_eq!(Ok(0.0), f64::extended_parse("-0.0")); assert!(f64::extended_parse("-0.0").unwrap().is_sign_negative()); @@ -794,10 +808,20 @@ mod tests { assert!(f64::extended_parse("nan").unwrap().is_sign_positive()); assert!(f64::extended_parse("NAN").unwrap().is_nan()); assert!(f64::extended_parse("NAN").unwrap().is_sign_positive()); - assert!(matches!(f64::extended_parse("-infinit"), - Err(ExtendedParserError::PartialMatch(f, "init")) if f == f64::NEG_INFINITY)); - assert!(matches!(f64::extended_parse("-infinity00"), - Err(ExtendedParserError::PartialMatch(f, "00")) if f == f64::NEG_INFINITY)); + assert_eq!( + f64::extended_parse("-infinit"), + Err(ExtendedParserError::PartialMatch( + f64::NEG_INFINITY, + "init".to_string() + )) + ); + assert_eq!( + f64::extended_parse("-infinity00"), + Err(ExtendedParserError::PartialMatch( + f64::NEG_INFINITY, + "00".to_string() + )) + ); assert!(f64::extended_parse(&format!("{}", u64::MAX)).is_ok()); assert!(f64::extended_parse(&format!("{}", i64::MIN)).is_ok()); @@ -982,14 +1006,22 @@ mod tests { // but we can check that the number still gets parsed properly: 0x0.8e5 is 0x8e5 / 16**3 assert_eq!(Ok(0.555908203125), f64::extended_parse("0x0.8e5")); - assert!(matches!(f64::extended_parse("0x0.1p"), - Err(ExtendedParserError::PartialMatch(f, "p")) if f == 0.0625)); - assert!(matches!(f64::extended_parse("0x0.1p-"), - Err(ExtendedParserError::PartialMatch(f, "p-")) if f == 0.0625)); - assert!(matches!(f64::extended_parse("0x.1p+"), - Err(ExtendedParserError::PartialMatch(f, "p+")) if f == 0.0625)); - assert!(matches!(f64::extended_parse("0x.1p."), - Err(ExtendedParserError::PartialMatch(f, "p.")) if f == 0.0625)); + assert_eq!( + f64::extended_parse("0x0.1p"), + Err(ExtendedParserError::PartialMatch(0.0625, "p".to_string())) + ); + assert_eq!( + f64::extended_parse("0x0.1p-"), + Err(ExtendedParserError::PartialMatch(0.0625, "p-".to_string())) + ); + assert_eq!( + f64::extended_parse("0x.1p+"), + Err(ExtendedParserError::PartialMatch(0.0625, "p+".to_string())) + ); + assert_eq!( + f64::extended_parse("0x.1p."), + Err(ExtendedParserError::PartialMatch(0.0625, "p.".to_string())) + ); assert_eq!( Ok(ExtendedBigDecimal::BigDecimal( @@ -1049,40 +1081,58 @@ mod tests { )); // Not actually hex numbers, but the prefixes look like it. - assert!(matches!(f64::extended_parse("0x"), - Err(ExtendedParserError::PartialMatch(f, "x")) if f == 0.0)); - assert!(matches!(f64::extended_parse("0x."), - Err(ExtendedParserError::PartialMatch(f, "x.")) if f == 0.0)); - assert!(matches!(f64::extended_parse("0xp"), - Err(ExtendedParserError::PartialMatch(f, "xp")) if f == 0.0)); - assert!(matches!(f64::extended_parse("0xp-2"), - Err(ExtendedParserError::PartialMatch(f, "xp-2")) if f == 0.0)); - assert!(matches!(f64::extended_parse("0x.p-2"), - Err(ExtendedParserError::PartialMatch(f, "x.p-2")) if f == 0.0)); - assert!(matches!(f64::extended_parse("0X"), - Err(ExtendedParserError::PartialMatch(f, "X")) if f == 0.0)); - assert!(matches!(f64::extended_parse("-0x"), - Err(ExtendedParserError::PartialMatch(f, "x")) if f == -0.0)); - assert!(matches!(f64::extended_parse("+0x"), - Err(ExtendedParserError::PartialMatch(f, "x")) if f == 0.0)); - assert!(matches!(f64::extended_parse("-0x."), - Err(ExtendedParserError::PartialMatch(f, "x.")) if f == -0.0)); - assert!(matches!( + assert_eq!( + f64::extended_parse("0x"), + Err(ExtendedParserError::PartialMatch(0.0, "x".to_string())) + ); + assert_eq!( + f64::extended_parse("0x."), + Err(ExtendedParserError::PartialMatch(0.0, "x.".to_string())) + ); + assert_eq!( + f64::extended_parse("0xp"), + Err(ExtendedParserError::PartialMatch(0.0, "xp".to_string())) + ); + assert_eq!( + f64::extended_parse("0xp-2"), + Err(ExtendedParserError::PartialMatch(0.0, "xp-2".to_string())) + ); + assert_eq!( + f64::extended_parse("0x.p-2"), + Err(ExtendedParserError::PartialMatch(0.0, "x.p-2".to_string())) + ); + assert_eq!( + f64::extended_parse("0X"), + Err(ExtendedParserError::PartialMatch(0.0, "X".to_string())) + ); + assert_eq!( + f64::extended_parse("-0x"), + Err(ExtendedParserError::PartialMatch(0.0, "x".to_string())) + ); + assert_eq!( + f64::extended_parse("+0x"), + Err(ExtendedParserError::PartialMatch(0.0, "x".to_string())) + ); + assert_eq!( + f64::extended_parse("-0x."), + Err(ExtendedParserError::PartialMatch(-0.0, "x.".to_string())) + ); + assert_eq!( u64::extended_parse("0x"), - Err(ExtendedParserError::PartialMatch(0, "x")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "x".to_string())) + ); + assert_eq!( u64::extended_parse("-0x"), - Err(ExtendedParserError::PartialMatch(0, "x")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "x".to_string())) + ); + assert_eq!( i64::extended_parse("0x"), - Err(ExtendedParserError::PartialMatch(0, "x")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "x".to_string())) + ); + assert_eq!( i64::extended_parse("-0x"), - Err(ExtendedParserError::PartialMatch(0, "x")) - )); + Err(ExtendedParserError::PartialMatch(0, "x".to_string())) + ); } #[test] @@ -1093,18 +1143,18 @@ mod tests { assert_eq!(Ok(-0o123), i64::extended_parse("-0123")); assert_eq!(Ok(0o123), u64::extended_parse("00123")); assert_eq!(Ok(0), u64::extended_parse("00")); - assert!(matches!( + assert_eq!( u64::extended_parse("008"), - Err(ExtendedParserError::PartialMatch(0, "8")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "8".to_string())) + ); + assert_eq!( u64::extended_parse("08"), - Err(ExtendedParserError::PartialMatch(0, "8")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "8".to_string())) + ); + assert_eq!( u64::extended_parse("0."), - Err(ExtendedParserError::PartialMatch(0, ".")) - )); + Err(ExtendedParserError::PartialMatch(0, ".".to_string())) + ); // No float tests, leading zeros get parsed as decimal anyway. } @@ -1116,51 +1166,62 @@ mod tests { assert_eq!(Ok(0b1011), u64::extended_parse("+0b1011")); assert_eq!(Ok(-0b1011), i64::extended_parse("-0b1011")); - assert!(matches!( + assert_eq!( u64::extended_parse("0b"), - Err(ExtendedParserError::PartialMatch(0, "b")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "b".to_string())) + ); + assert_eq!( u64::extended_parse("0b."), - Err(ExtendedParserError::PartialMatch(0, "b.")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "b.".to_string())) + ); + assert_eq!( u64::extended_parse("-0b"), - Err(ExtendedParserError::PartialMatch(0, "b")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "b".to_string())) + ); + assert_eq!( i64::extended_parse("0b"), - Err(ExtendedParserError::PartialMatch(0, "b")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0, "b".to_string())) + ); + assert_eq!( i64::extended_parse("-0b"), - Err(ExtendedParserError::PartialMatch(0, "b")) - )); + Err(ExtendedParserError::PartialMatch(0, "b".to_string())) + ); // Binary not allowed for floats - assert!(matches!( + assert_eq!( f64::extended_parse("0b100"), - Err(ExtendedParserError::PartialMatch(0f64, "b100")) - )); - assert!(matches!( + Err(ExtendedParserError::PartialMatch(0f64, "b100".to_string())) + ); + assert_eq!( f64::extended_parse("0b100.1"), - Err(ExtendedParserError::PartialMatch(0f64, "b100.1")) - )); + Err(ExtendedParserError::PartialMatch( + 0f64, + "b100.1".to_string() + )) + ); - assert!(match ExtendedBigDecimal::extended_parse("0b100.1") { - Err(ExtendedParserError::PartialMatch(ebd, "b100.1")) => - ebd == ExtendedBigDecimal::zero(), - _ => false, - }); + assert_eq!( + ExtendedBigDecimal::extended_parse("0b100.1"), + Err(ExtendedParserError::PartialMatch( + ExtendedBigDecimal::zero(), + "b100.1".to_string() + )) + ); - assert!(match ExtendedBigDecimal::extended_parse("0b") { - Err(ExtendedParserError::PartialMatch(ebd, "b")) => ebd == ExtendedBigDecimal::zero(), - _ => false, - }); - assert!(match ExtendedBigDecimal::extended_parse("0b.") { - Err(ExtendedParserError::PartialMatch(ebd, "b.")) => ebd == ExtendedBigDecimal::zero(), - _ => false, - }); + assert_eq!( + ExtendedBigDecimal::extended_parse("0b"), + Err(ExtendedParserError::PartialMatch( + ExtendedBigDecimal::zero(), + "b".to_string() + )) + ); + assert_eq!( + ExtendedBigDecimal::extended_parse("0b."), + Err(ExtendedParserError::PartialMatch( + ExtendedBigDecimal::zero(), + "b.".to_string() + )) + ); } #[test] @@ -1173,15 +1234,15 @@ mod tests { // Ensure that trailing whitespace is still a partial match assert_eq!( - Err(ExtendedParserError::PartialMatch(6, " ")), + Err(ExtendedParserError::PartialMatch(6, " ".to_string())), u64::extended_parse("0x6 ") ); assert_eq!( - Err(ExtendedParserError::PartialMatch(7, "\t")), + Err(ExtendedParserError::PartialMatch(7, "\t".to_string())), u64::extended_parse("0x7\t") ); assert_eq!( - Err(ExtendedParserError::PartialMatch(8, "\n")), + Err(ExtendedParserError::PartialMatch(8, "\n".to_string())), u64::extended_parse("0x8\n") );