1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 12:37:49 +00:00

seq: Make use of uucore::format to print in all cases

Now that uucore format functions take in an ExtendedBigDecimal,
we can use those in all cases.
This commit is contained in:
Nicolas Boichat 2025-03-16 19:46:39 +01:00 committed by Sylvestre Ledru
parent 25c492ee19
commit f31ba2bd28
2 changed files with 58 additions and 82 deletions

View file

@ -7,10 +7,11 @@ use std::ffi::OsString;
use std::io::{stdout, BufWriter, ErrorKind, Write}; use std::io::{stdout, BufWriter, ErrorKind, Write};
use clap::{Arg, ArgAction, Command}; use clap::{Arg, ArgAction, Command};
use num_traits::{ToPrimitive, Zero}; use num_traits::Zero;
use uucore::error::{FromIo, UResult}; 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}; use uucore::{format_usage, help_about, help_usage};
mod error; 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 precision = select_precision(first_precision, increment_precision, last_precision);
let format = options // If a format was passed on the command line, use that.
.format // If not, use some default format based on parameters precision.
.map(Format::<num_format::Float, &ExtendedBigDecimal>::parse) let format = match options.format {
.transpose()?; Some(str) => Format::<num_format::Float, &ExtendedBigDecimal>::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( let result = print_seq(
(first.number, increment.number, last.number), (first.number, increment.number, last.number),
precision,
&options.separator, &options.separator,
&options.terminator, &options.terminator,
options.equal_width, &format,
padding,
format.as_ref(),
); );
match result { match result {
Ok(()) => Ok(()), Ok(()) => Ok(()),
@ -218,84 +245,24 @@ fn done_printing<T: Zero + PartialOrd>(next: &T, increment: &T, last: &T) -> boo
} }
} }
fn format_bigdecimal(value: &bigdecimal::BigDecimal) -> Option<String> {
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<usize>,
) -> 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 /// Floating point based code path
fn print_seq( fn print_seq(
range: RangeFloat, range: RangeFloat,
precision: Option<usize>,
separator: &str, separator: &str,
terminator: &str, terminator: &str,
pad: bool, format: &Format<num_format::Float, &ExtendedBigDecimal>,
padding: usize,
format: Option<&Format<num_format::Float, &ExtendedBigDecimal>>,
) -> std::io::Result<()> { ) -> std::io::Result<()> {
let stdout = stdout().lock(); let stdout = stdout().lock();
let mut stdout = BufWriter::new(stdout); let mut stdout = BufWriter::new(stdout);
let (first, increment, last) = range; let (first, increment, last) = range;
let mut value = first; 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; let mut is_first_iteration = true;
while !done_printing(&value, &increment, &last) { while !done_printing(&value, &increment, &last) {
if !is_first_iteration { if !is_first_iteration {
write!(stdout, "{separator}")?; write!(stdout, "{separator}")?;
} }
// If there was an argument `-f FORMAT`, then use that format format.fmt(&mut stdout, &value)?;
// 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)?,
}
// TODO Implement augmenting addition. // TODO Implement augmenting addition.
value = value + increment.clone(); value = value + increment.clone();
is_first_iteration = false; is_first_iteration = false;

View file

@ -310,12 +310,12 @@ pub fn sprintf<'a>(
Ok(writer) 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`] /// This is used by `seq` and `csplit`. It can be constructed with [`Format::from_formatter`]
/// and can write a value with [`Format::fmt`]. /// 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. /// If it does get more specifications, it will return an error.
pub struct Format<F: Formatter<T>, T> { pub struct Format<F: Formatter<T>, T> {
prefix: Vec<u8>, prefix: Vec<u8>,
@ -325,6 +325,15 @@ pub struct Format<F: Formatter<T>, T> {
} }
impl<F: Formatter<T>, T> Format<F, T> { impl<F: Formatter<T>, T> Format<F, T> {
pub fn from_formatter(formatter: F) -> Self {
Self {
prefix: Vec::<u8>::new(),
suffix: Vec::<u8>::new(),
formatter,
_marker: PhantomData,
}
}
pub fn parse(format_string: impl AsRef<[u8]>) -> Result<Self, FormatError> { pub fn parse(format_string: impl AsRef<[u8]>) -> Result<Self, FormatError> {
let mut iter = parse_spec_only(format_string.as_ref()); let mut iter = parse_spec_only(format_string.as_ref());