mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 20:17:45 +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:
parent
84e5ee4b86
commit
77d66bab47
3 changed files with 44 additions and 23 deletions
|
@ -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).
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue