From 77d66bab47f4ee0d4dc0e0b26c693a1fdf73e642 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Sat, 22 Mar 2025 10:42:55 +0100 Subject: [PATCH] seq: Refactor to actually use PreciseNumber::num_fractional_digits The field was unused, and actually redundant with the precision computed separatedly. This simplifies the code, reintroduces testing. --- src/uu/seq/src/number.rs | 14 +++++++++----- src/uu/seq/src/numberparse.rs | 23 ++++++++++++++++++----- src/uu/seq/src/seq.rs | 30 +++++++++++++++++------------- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/uu/seq/src/number.rs b/src/uu/seq/src/number.rs index bbd5a9564..b70ba446e 100644 --- a/src/uu/seq/src/number.rs +++ b/src/uu/seq/src/number.rs @@ -13,22 +13,26 @@ use uucore::format::ExtendedBigDecimal; /// on how many significant digits to use when displaying the number. /// The [`PreciseNumber::num_integral_digits`] field also includes the width needed to /// display the "-" character for a negative number. +/// [`PreciseNumber::num_fractional_digits`] provides the number of decimal digits after +/// the decimal point (a.k.a. precision), or None if that number cannot intuitively be +/// obtained (i.e. hexadecimal floats). +/// Note: Those 2 fields should not necessarily be interpreted literally, but as matching +/// GNU `seq` behavior: the exact way of guessing desired precision from user input is a +/// matter of interpretation. /// /// You can get an instance of this struct by calling [`str::parse`]. #[derive(Debug)] pub struct PreciseNumber { pub number: ExtendedBigDecimal, pub num_integral_digits: usize, - - #[allow(dead_code)] - pub num_fractional_digits: usize, + pub num_fractional_digits: Option, } impl PreciseNumber { pub fn new( number: ExtendedBigDecimal, num_integral_digits: usize, - num_fractional_digits: usize, + num_fractional_digits: Option, ) -> Self { Self { number, @@ -42,7 +46,7 @@ impl PreciseNumber { // We would like to implement `num_traits::One`, but it requires // a multiplication implementation, and we don't want to // implement that here. - Self::new(ExtendedBigDecimal::one(), 1, 0) + Self::new(ExtendedBigDecimal::one(), 1, Some(0)) } /// Decide whether this number is zero (either positive or negative). diff --git a/src/uu/seq/src/numberparse.rs b/src/uu/seq/src/numberparse.rs index 731a43fa1..6d839eccb 100644 --- a/src/uu/seq/src/numberparse.rs +++ b/src/uu/seq/src/numberparse.rs @@ -13,7 +13,7 @@ use bigdecimal::BigDecimal; use num_traits::Zero; use uucore::format::num_parser::{ExtendedParser, ExtendedParserError}; -use crate::number::PreciseNumber; +use crate::{hexadecimalfloat, number::PreciseNumber}; use uucore::format::ExtendedBigDecimal; /// An error returned when parsing a number fails. @@ -91,7 +91,7 @@ impl FromStr for PreciseNumber { return Ok(PreciseNumber { number: ebd, num_integral_digits: 0, - num_fractional_digits: 0, + num_fractional_digits: Some(0), }); } ExtendedBigDecimal::Nan | ExtendedBigDecimal::MinusNan => { @@ -108,7 +108,7 @@ impl FromStr for PreciseNumber { Ok(PreciseNumber { number: ebd, num_integral_digits: compute_num_integral_digits(input, &bd), - num_fractional_digits: 0, // TODO: Re-implement + num_fractional_digits: hexadecimalfloat::parse_precision(input), }) } } @@ -133,7 +133,18 @@ mod tests { /// Convenience function for getting the number of fractional digits. fn num_fractional_digits(s: &str) -> usize { - s.parse::().unwrap().num_fractional_digits + s.parse::() + .unwrap() + .num_fractional_digits + .unwrap() + } + + /// Convenience function for making sure the number of fractional digits is "None" + fn num_fractional_digits_is_none(s: &str) -> bool { + s.parse::() + .unwrap() + .num_fractional_digits + .is_none() } #[test] @@ -302,7 +313,6 @@ mod tests { #[test] #[allow(clippy::cognitive_complexity)] - #[ignore = "Disable for now"] fn test_num_fractional_digits() { // no decimal, no exponent assert_eq!(num_fractional_digits("123"), 0); @@ -337,6 +347,9 @@ mod tests { assert_eq!(num_fractional_digits("-0.0"), 1); assert_eq!(num_fractional_digits("-0e-1"), 1); assert_eq!(num_fractional_digits("-0.0e-1"), 2); + // Hexadecimal numbers + assert_eq!(num_fractional_digits("0xff"), 0); + assert!(num_fractional_digits_is_none("0xff.1")); } #[test] diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 827a8335e..3c8a275d4 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -74,11 +74,15 @@ fn split_short_args_with_value(args: impl uucore::Args) -> impl uucore::Args { } fn select_precision( - first: Option, - increment: Option, - last: Option, + first: &PreciseNumber, + increment: &PreciseNumber, + last: &PreciseNumber, ) -> Option { - match (first, increment, last) { + match ( + first.num_fractional_digits, + increment.num_fractional_digits, + last.num_fractional_digits, + ) { (Some(0), Some(0), Some(0)) => Some(0), (Some(f), Some(i), Some(_)) => Some(f.max(i)), _ => None, @@ -111,37 +115,37 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { format: matches.get_one::(OPT_FORMAT).map(|s| s.as_str()), }; - let (first, first_precision) = if numbers.len() > 1 { + let first = if numbers.len() > 1 { match numbers[0].parse() { - Ok(num) => (num, hexadecimalfloat::parse_precision(numbers[0])), + Ok(num) => num, Err(e) => return Err(SeqError::ParseError(numbers[0].to_string(), e).into()), } } else { - (PreciseNumber::one(), Some(0)) + PreciseNumber::one() }; - let (increment, increment_precision) = if numbers.len() > 2 { + let increment = if numbers.len() > 2 { match numbers[1].parse() { - Ok(num) => (num, hexadecimalfloat::parse_precision(numbers[1])), + Ok(num) => num, Err(e) => return Err(SeqError::ParseError(numbers[1].to_string(), e).into()), } } else { - (PreciseNumber::one(), Some(0)) + PreciseNumber::one() }; if increment.is_zero() { return Err(SeqError::ZeroIncrement(numbers[1].to_string()).into()); } - let (last, last_precision): (PreciseNumber, Option) = { + let last: PreciseNumber = { // We are guaranteed that `numbers.len()` is greater than zero // and at most three because of the argument specification in // `uu_app()`. let n: usize = numbers.len(); match numbers[n - 1].parse() { - Ok(num) => (num, hexadecimalfloat::parse_precision(numbers[n - 1])), + Ok(num) => num, Err(e) => return Err(SeqError::ParseError(numbers[n - 1].to_string(), e).into()), } }; - let precision = select_precision(first_precision, increment_precision, last_precision); + let precision = select_precision(&first, &increment, &last); // If a format was passed on the command line, use that. // If not, use some default format based on parameters precision.