1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 20:47:46 +00:00

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.
This commit is contained in:
Nicolas Boichat 2025-03-22 10:42:55 +01:00 committed by Daniel Hofstetter
parent 84e5ee4b86
commit 77d66bab47
3 changed files with 44 additions and 23 deletions

View file

@ -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<usize>,
}
impl PreciseNumber {
pub fn new(
number: ExtendedBigDecimal,
num_integral_digits: usize,
num_fractional_digits: usize,
num_fractional_digits: Option<usize>,
) -> 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).

View file

@ -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::<PreciseNumber>().unwrap().num_fractional_digits
s.parse::<PreciseNumber>()
.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::<PreciseNumber>()
.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]

View file

@ -74,11 +74,15 @@ fn split_short_args_with_value(args: impl uucore::Args) -> impl uucore::Args {
}
fn select_precision(
first: Option<usize>,
increment: Option<usize>,
last: Option<usize>,
first: &PreciseNumber,
increment: &PreciseNumber,
last: &PreciseNumber,
) -> Option<usize> {
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::<String>(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<usize>) = {
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.