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 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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue