diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 91dd091c4..777c77e1a 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -7,10 +7,11 @@ use std::ffi::OsString; use std::io::{stdout, BufWriter, ErrorKind, Write}; use clap::{Arg, ArgAction, Command}; -use num_traits::{ToPrimitive, Zero}; +use num_traits::Zero; use uucore::error::{FromIo, UResult}; -use uucore::format::{num_format, sprintf, ExtendedBigDecimal, Format, FormatArgument}; +use uucore::format::num_format::FloatVariant; +use uucore::format::{num_format, ExtendedBigDecimal, Format}; use uucore::{format_usage, help_about, help_usage}; mod error; @@ -140,26 +141,52 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } }; - let padding = first - .num_integral_digits - .max(increment.num_integral_digits) - .max(last.num_integral_digits); - let precision = select_precision(first_precision, increment_precision, last_precision); - let format = options - .format - .map(Format::::parse) - .transpose()?; + // If a format was passed on the command line, use that. + // If not, use some default format based on parameters precision. + let format = match options.format { + Some(str) => Format::::parse(str)?, + None => { + let padding = if options.equal_width { + let precision_value = precision.unwrap_or(0); + first + .num_integral_digits + .max(increment.num_integral_digits) + .max(last.num_integral_digits) + + if precision_value > 0 { + precision_value + 1 + } else { + 0 + } + } else { + 0 + }; + + let formatter = match precision { + // format with precision: decimal floats and integers + Some(precision) => num_format::Float { + variant: FloatVariant::Decimal, + width: padding, + alignment: num_format::NumberAlignment::RightZero, + precision, + ..Default::default() + }, + // format without precision: hexadecimal floats + None => num_format::Float { + variant: FloatVariant::Shortest, + ..Default::default() + }, + }; + Format::from_formatter(formatter) + } + }; let result = print_seq( (first.number, increment.number, last.number), - precision, &options.separator, &options.terminator, - options.equal_width, - padding, - format.as_ref(), + &format, ); match result { Ok(()) => Ok(()), @@ -218,84 +245,24 @@ fn done_printing(next: &T, increment: &T, last: &T) -> boo } } -fn format_bigdecimal(value: &bigdecimal::BigDecimal) -> Option { - let format_arguments = &[FormatArgument::Float(value.to_f64()?)]; - let value_as_bytes = sprintf("%g", format_arguments).ok()?; - String::from_utf8(value_as_bytes).ok() -} - -/// Write a big decimal formatted according to the given parameters. -fn write_value_float( - writer: &mut impl Write, - value: &ExtendedBigDecimal, - width: usize, - precision: Option, -) -> std::io::Result<()> { - let value_as_str = match precision { - // format with precision: decimal floats and integers - Some(precision) => match value { - ExtendedBigDecimal::Infinity | ExtendedBigDecimal::MinusInfinity => { - format!("{value:>width$.precision$}") - } - _ => format!("{value:>0width$.precision$}"), - }, - // format without precision: hexadecimal floats - None => match value { - ExtendedBigDecimal::BigDecimal(bd) => { - format_bigdecimal(bd).unwrap_or_else(|| "{value}".to_owned()) - } - _ => format!("{value:>0width$}"), - }, - }; - write!(writer, "{value_as_str}") -} - /// Floating point based code path fn print_seq( range: RangeFloat, - precision: Option, separator: &str, terminator: &str, - pad: bool, - padding: usize, - format: Option<&Format>, + format: &Format, ) -> std::io::Result<()> { let stdout = stdout().lock(); let mut stdout = BufWriter::new(stdout); let (first, increment, last) = range; let mut value = first; - let padding = if pad { - let precision_value = precision.unwrap_or(0); - padding - + if precision_value > 0 { - precision_value + 1 - } else { - 0 - } - } else { - 0 - }; + let mut is_first_iteration = true; while !done_printing(&value, &increment, &last) { if !is_first_iteration { write!(stdout, "{separator}")?; } - // If there was an argument `-f FORMAT`, then use that format - // template instead of the default formatting strategy. - // - // TODO The `printf()` method takes a string as its second - // parameter but we have an `ExtendedBigDecimal`. In order to - // satisfy the signature of the function, we convert the - // `ExtendedBigDecimal` into a string. The `printf()` - // logic will subsequently parse that string into something - // similar to an `ExtendedBigDecimal` again before rendering - // it as a string and ultimately writing to `stdout`. We - // shouldn't have to do so much converting back and forth via - // strings. - match &format { - Some(f) => f.fmt(&mut stdout, &value)?, - None => write_value_float(&mut stdout, &value, padding, precision)?, - } + format.fmt(&mut stdout, &value)?; // TODO Implement augmenting addition. value = value + increment.clone(); is_first_iteration = false; diff --git a/src/uucore/src/lib/features/format/mod.rs b/src/uucore/src/lib/features/format/mod.rs index e44ef4bc0..25a4449dc 100644 --- a/src/uucore/src/lib/features/format/mod.rs +++ b/src/uucore/src/lib/features/format/mod.rs @@ -310,12 +310,12 @@ pub fn sprintf<'a>( Ok(writer) } -/// A parsed format for a single numerical value of type T +/// A format for a single numerical value of type T /// -/// This is used by `seq` and `csplit`. It can be constructed with [`Format::parse`] -/// and can write a value with [`Format::fmt`]. +/// This is used by `seq` and `csplit`. It can be constructed with [`Format::from_formatter`] +/// or [`Format::parse`] and can write a value with [`Format::fmt`]. /// -/// It can only accept a single specification without any asterisk parameters. +/// [`Format::parse`] can only accept a single specification without any asterisk parameters. /// If it does get more specifications, it will return an error. pub struct Format, T> { prefix: Vec, @@ -325,6 +325,15 @@ pub struct Format, T> { } impl, T> Format { + pub fn from_formatter(formatter: F) -> Self { + Self { + prefix: Vec::::new(), + suffix: Vec::::new(), + formatter, + _marker: PhantomData, + } + } + pub fn parse(format_string: impl AsRef<[u8]>) -> Result { let mut iter = parse_spec_only(format_string.as_ref());