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:
parent
25c492ee19
commit
f31ba2bd28
2 changed files with 58 additions and 82 deletions
|
@ -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::<num_format::Float, &ExtendedBigDecimal>::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::<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(
|
||||
(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<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
|
||||
fn print_seq(
|
||||
range: RangeFloat,
|
||||
precision: Option<usize>,
|
||||
separator: &str,
|
||||
terminator: &str,
|
||||
pad: bool,
|
||||
padding: usize,
|
||||
format: Option<&Format<num_format::Float, &ExtendedBigDecimal>>,
|
||||
format: &Format<num_format::Float, &ExtendedBigDecimal>,
|
||||
) -> 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;
|
||||
|
|
|
@ -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<F: Formatter<T>, T> {
|
||||
prefix: Vec<u8>,
|
||||
|
@ -325,6 +325,15 @@ pub struct Format<F: Formatter<T>, 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> {
|
||||
let mut iter = parse_spec_only(format_string.as_ref());
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue