mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 15:22:38 +00:00
printf rewrite: fix compilation
This commit is contained in:
parent
28810906a3
commit
f117fc1bab
7 changed files with 85 additions and 49 deletions
|
@ -13,8 +13,8 @@ use std::io::Write;
|
|||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
|
||||
use uucore::error::UResult;
|
||||
use uucore::format::sprintf;
|
||||
use uucore::{error::UResult, format::FormatArgument};
|
||||
|
||||
use crate::numbers::{to_magnitude_and_suffix, SuffixType};
|
||||
|
||||
|
@ -152,7 +152,9 @@ impl ProgUpdate {
|
|||
let (carriage_return, newline) = if rewrite { ("\r", "") } else { ("", "\n") };
|
||||
|
||||
// The duration should be formatted as in `printf %g`.
|
||||
let duration_str = sprintf("%g", &[duration.to_string()])?;
|
||||
// TODO: remove unwrap and make FormatError implement UError
|
||||
let duration_str = sprintf("%g", &[FormatArgument::Float(duration)])?;
|
||||
let duration_str = std::str::from_utf8(&duration_str).unwrap();
|
||||
|
||||
// If the number of bytes written is sufficiently large, then
|
||||
// print a more concise representation of the number, like
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
use clap::{crate_version, Arg, ArgAction, Command};
|
||||
use uucore::error::{UResult, UUsageError};
|
||||
use uucore::format::printf;
|
||||
use uucore::format::{printf, FormatArgument};
|
||||
use uucore::{format_usage, help_about, help_section, help_usage};
|
||||
|
||||
const VERSION: &str = "version";
|
||||
|
@ -30,12 +30,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
let format_string = matches
|
||||
.get_one::<String>(options::FORMATSTRING)
|
||||
.ok_or_else(|| UUsageError::new(1, "missing operand"))?;
|
||||
let values: Vec<String> = match matches.get_many::<String>(options::ARGUMENT) {
|
||||
Some(s) => s.map(|s| s.to_string()).collect(),
|
||||
let values: Vec<_> = match matches.get_many::<String>(options::ARGUMENT) {
|
||||
Some(s) => s.map(|s| FormatArgument::Unparsed(s.to_string())).collect(),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
printf(format_string, &values[..])?;
|
||||
printf(format_string, &values)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use clap::{crate_version, Arg, ArgAction, Command};
|
|||
use num_traits::Zero;
|
||||
|
||||
use uucore::error::UResult;
|
||||
use uucore::format::printf;
|
||||
use uucore::format::{printf, FormatArgument};
|
||||
use uucore::{format_usage, help_about, help_usage};
|
||||
|
||||
mod error;
|
||||
|
@ -144,8 +144,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
};
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()),
|
||||
Err(e) => Err(e.map_err_context(|| "write error".into())),
|
||||
_ => todo!(),
|
||||
// Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()),
|
||||
// Err(e) => Err(e.map_err_context(|| "write error".into())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,7 +271,7 @@ fn print_seq(
|
|||
match format {
|
||||
Some(f) => {
|
||||
let s = format!("{value}");
|
||||
printf(f, &[s])?;
|
||||
printf(f, &[FormatArgument::String(s)])?;
|
||||
}
|
||||
None => write_value_float(&mut stdout, &value, padding, largest_dec)?,
|
||||
}
|
||||
|
@ -326,7 +327,7 @@ fn print_seq_integers(
|
|||
match format {
|
||||
Some(f) => {
|
||||
let s = format!("{value}");
|
||||
printf(f, &[s])?;
|
||||
printf(f, &[FormatArgument::String(s)])?;
|
||||
}
|
||||
None => write_value_int(&mut stdout, &value, padding, pad)?,
|
||||
}
|
||||
|
|
|
@ -8,16 +8,14 @@
|
|||
pub mod backup_control;
|
||||
#[cfg(feature = "encoding")]
|
||||
pub mod encoding;
|
||||
#[cfg(feature = "format")]
|
||||
pub mod format;
|
||||
#[cfg(feature = "fs")]
|
||||
pub mod fs;
|
||||
#[cfg(feature = "fsext")]
|
||||
pub mod fsext;
|
||||
#[cfg(feature = "lines")]
|
||||
pub mod lines;
|
||||
#[cfg(feature = "format")]
|
||||
pub mod format;
|
||||
#[cfg(feature = "memo")]
|
||||
pub mod memo;
|
||||
#[cfg(feature = "quoting-style")]
|
||||
pub mod quoting_style;
|
||||
#[cfg(feature = "ranges")]
|
||||
|
@ -26,8 +24,6 @@ pub mod ranges;
|
|||
pub mod ringbuffer;
|
||||
#[cfg(feature = "sum")]
|
||||
pub mod sum;
|
||||
#[cfg(feature = "memo")]
|
||||
mod tokenize;
|
||||
#[cfg(feature = "update-control")]
|
||||
pub mod update_control;
|
||||
#[cfg(feature = "version-cmp")]
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! The [`printf`] and [`sprintf`] closely match the behavior of the
|
||||
//! corresponding C functions: the former renders a formatted string
|
||||
//! to stdout, the latter renders to a new [`String`] object.
|
||||
//!
|
||||
//!
|
||||
//! In addition to the [`printf`] and [`sprintf`] functions, we expose the
|
||||
//! [`Format`] struct, which represents a parsed format string. This reduces
|
||||
//! the need for parsing a format string multiple times and assures that no
|
||||
|
@ -14,8 +14,15 @@
|
|||
mod spec;
|
||||
|
||||
use spec::Spec;
|
||||
use std::io::{stdout, Write};
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::Display,
|
||||
io::{stdout, Write},
|
||||
};
|
||||
|
||||
use crate::error::UError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FormatError {
|
||||
SpecError,
|
||||
IoError(std::io::Error),
|
||||
|
@ -23,6 +30,21 @@ pub enum FormatError {
|
|||
InvalidArgument(FormatArgument),
|
||||
}
|
||||
|
||||
impl Error for FormatError {}
|
||||
impl UError for FormatError {}
|
||||
|
||||
impl Display for FormatError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// TODO: Be more precise about these
|
||||
match self {
|
||||
FormatError::SpecError => write!(f, "invalid spec"),
|
||||
FormatError::IoError(_) => write!(f, "io error"),
|
||||
FormatError::NoMoreArguments => write!(f, "no more arguments"),
|
||||
FormatError::InvalidArgument(_) => write!(f, "invalid argument"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single item to format
|
||||
enum FormatItem {
|
||||
/// A format specifier
|
||||
|
@ -30,21 +52,28 @@ enum FormatItem {
|
|||
/// Some plain text
|
||||
Text(Vec<u8>),
|
||||
/// A single character
|
||||
///
|
||||
///
|
||||
/// Added in addition to `Text` as an optimization.
|
||||
Char(u8),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FormatArgument {
|
||||
Char(char),
|
||||
String(String),
|
||||
UnsignedInt(u64),
|
||||
SignedInt(i64),
|
||||
Float(f64),
|
||||
// Special argument that gets coerced into the other variants
|
||||
Unparsed(String),
|
||||
}
|
||||
|
||||
impl FormatItem {
|
||||
fn write<'a>(&self, mut writer: impl Write, args: &mut impl Iterator<Item = FormatArgument>) -> Result<(), FormatError> {
|
||||
fn write<'a>(
|
||||
&self,
|
||||
mut writer: impl Write,
|
||||
args: &mut impl Iterator<Item = &'a FormatArgument>,
|
||||
) -> Result<(), FormatError> {
|
||||
match self {
|
||||
FormatItem::Spec(spec) => spec.write(writer, args),
|
||||
FormatItem::Text(bytes) => writer.write_all(bytes).map_err(FormatError::IoError),
|
||||
|
@ -110,13 +139,20 @@ fn parse_iter(fmt: &[u8]) -> impl Iterator<Item = Result<FormatItem, FormatError
|
|||
/// printf("hello %s", &["world".to_string()]).unwrap();
|
||||
/// // prints "hello world"
|
||||
/// ```
|
||||
pub fn printf(format_string: &[u8], arguments: impl IntoIterator<Item = FormatArgument>) -> Result<(), FormatError> {
|
||||
pub fn printf<'a>(
|
||||
format_string: impl AsRef<[u8]>,
|
||||
arguments: impl IntoIterator<Item = &'a FormatArgument>,
|
||||
) -> Result<(), FormatError> {
|
||||
printf_writer(stdout(), format_string, arguments)
|
||||
}
|
||||
|
||||
fn printf_writer(mut writer: impl Write, format_string: &[u8], args: impl IntoIterator<Item = FormatArgument>) -> Result<(), FormatError> {
|
||||
fn printf_writer<'a>(
|
||||
mut writer: impl Write,
|
||||
format_string: impl AsRef<[u8]>,
|
||||
args: impl IntoIterator<Item = &'a FormatArgument>,
|
||||
) -> Result<(), FormatError> {
|
||||
let mut args = args.into_iter();
|
||||
for item in parse_iter(format_string) {
|
||||
for item in parse_iter(format_string.as_ref()) {
|
||||
item?.write(&mut writer, &mut args)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -137,7 +173,10 @@ fn printf_writer(mut writer: impl Write, format_string: &[u8], args: impl IntoIt
|
|||
/// let s = sprintf("hello %s", &["world".to_string()]).unwrap();
|
||||
/// assert_eq!(s, "hello world".to_string());
|
||||
/// ```
|
||||
pub fn sprintf(format_string: &[u8], arguments: impl IntoIterator<Item = FormatArgument>) -> Result<Vec<u8>, FormatError> {
|
||||
pub fn sprintf<'a>(
|
||||
format_string: impl AsRef<[u8]>,
|
||||
arguments: impl IntoIterator<Item = &'a FormatArgument>,
|
||||
) -> Result<Vec<u8>, FormatError> {
|
||||
let mut writer = Vec::new();
|
||||
printf_writer(&mut writer, format_string, arguments)?;
|
||||
Ok(writer)
|
||||
|
|
|
@ -257,7 +257,7 @@ impl Spec {
|
|||
pub fn write<'a>(
|
||||
&self,
|
||||
mut writer: impl Write,
|
||||
mut args: impl Iterator<Item = FormatArgument>,
|
||||
mut args: impl Iterator<Item = &'a FormatArgument>,
|
||||
) -> Result<(), FormatError> {
|
||||
match self {
|
||||
&Spec::Char { width, align_left } => {
|
||||
|
@ -265,7 +265,7 @@ impl Spec {
|
|||
let arg = next_arg(&mut args)?;
|
||||
match arg {
|
||||
FormatArgument::Char(c) => write_padded(writer, c, width, false, align_left),
|
||||
_ => Err(FormatError::InvalidArgument(arg)),
|
||||
_ => Err(FormatError::InvalidArgument(arg.clone())),
|
||||
}
|
||||
}
|
||||
&Spec::String { width, align_left } => {
|
||||
|
@ -273,7 +273,7 @@ impl Spec {
|
|||
let arg = next_arg(&mut args)?;
|
||||
match arg {
|
||||
FormatArgument::String(s) => write_padded(writer, s, width, false, align_left),
|
||||
_ => Err(FormatError::InvalidArgument(arg)),
|
||||
_ => Err(FormatError::InvalidArgument(arg.clone())),
|
||||
}
|
||||
}
|
||||
&Spec::SignedInt {
|
||||
|
@ -285,10 +285,10 @@ impl Spec {
|
|||
|
||||
let arg = next_arg(&mut args)?;
|
||||
let FormatArgument::SignedInt(i) = arg else {
|
||||
return Err(FormatError::InvalidArgument(arg));
|
||||
return Err(FormatError::InvalidArgument(arg.clone()));
|
||||
};
|
||||
|
||||
if i >= 0 {
|
||||
if *i >= 0 {
|
||||
match positive_sign {
|
||||
PositiveSign::None => Ok(()),
|
||||
PositiveSign::Plus => write!(writer, "+"),
|
||||
|
@ -313,7 +313,7 @@ impl Spec {
|
|||
|
||||
let arg = next_arg(args)?;
|
||||
let FormatArgument::SignedInt(i) = arg else {
|
||||
return Err(FormatError::InvalidArgument(arg));
|
||||
return Err(FormatError::InvalidArgument(arg.clone()));
|
||||
};
|
||||
|
||||
let s = match variant {
|
||||
|
@ -355,7 +355,7 @@ impl Spec {
|
|||
|
||||
let arg = next_arg(args)?;
|
||||
let FormatArgument::Float(f) = arg else {
|
||||
return Err(FormatError::InvalidArgument(arg));
|
||||
return Err(FormatError::InvalidArgument(arg.clone()));
|
||||
};
|
||||
|
||||
if f.is_sign_positive() {
|
||||
|
@ -369,16 +369,16 @@ impl Spec {
|
|||
|
||||
let s = match variant {
|
||||
FloatVariant::Decimal => {
|
||||
format_float_decimal(f, precision, case, force_decimal)
|
||||
format_float_decimal(*f, precision, case, force_decimal)
|
||||
}
|
||||
FloatVariant::Scientific => {
|
||||
format_float_scientific(f, precision, case, force_decimal)
|
||||
format_float_scientific(*f, precision, case, force_decimal)
|
||||
}
|
||||
FloatVariant::Shortest => {
|
||||
format_float_shortest(f, precision, case, force_decimal)
|
||||
format_float_shortest(*f, precision, case, force_decimal)
|
||||
}
|
||||
FloatVariant::Hexadecimal => {
|
||||
format_float_hexadecimal(f, precision, case, force_decimal)
|
||||
format_float_hexadecimal(*f, precision, case, force_decimal)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -490,7 +490,7 @@ fn format_float_hexadecimal(
|
|||
let mut s = match (precision, force_decimal) {
|
||||
(0, ForceDecimal::No) => format!("0x{first_digit}p{exponent:+x}"),
|
||||
(0, ForceDecimal::Yes) => format!("0x{first_digit}.p{exponent:+x}"),
|
||||
_ => format!("0x{first_digit}.{mantissa:0>13x}p{exponent:+x}")
|
||||
_ => format!("0x{first_digit}.{mantissa:0>13x}p{exponent:+x}"),
|
||||
};
|
||||
|
||||
if case == Case::Uppercase {
|
||||
|
@ -500,29 +500,29 @@ fn format_float_hexadecimal(
|
|||
return s;
|
||||
}
|
||||
|
||||
fn resolve_asterisk(
|
||||
fn resolve_asterisk<'a>(
|
||||
option: Option<CanAsterisk<usize>>,
|
||||
args: impl Iterator<Item = FormatArgument>,
|
||||
args: impl Iterator<Item = &'a FormatArgument>,
|
||||
) -> Result<Option<usize>, FormatError> {
|
||||
Ok(match option {
|
||||
None => None,
|
||||
Some(CanAsterisk::Asterisk) => {
|
||||
let arg = next_arg(args)?;
|
||||
match arg {
|
||||
FormatArgument::UnsignedInt(u) => match usize::try_from(u) {
|
||||
FormatArgument::UnsignedInt(u) => match usize::try_from(*u) {
|
||||
Ok(u) => Some(u),
|
||||
Err(_) => return Err(FormatError::InvalidArgument(arg)),
|
||||
Err(_) => return Err(FormatError::InvalidArgument(arg.clone())),
|
||||
},
|
||||
_ => return Err(FormatError::InvalidArgument(arg)),
|
||||
_ => return Err(FormatError::InvalidArgument(arg.clone())),
|
||||
}
|
||||
}
|
||||
Some(CanAsterisk::Fixed(w)) => Some(w),
|
||||
})
|
||||
}
|
||||
|
||||
fn next_arg(
|
||||
mut arguments: impl Iterator<Item = FormatArgument>,
|
||||
) -> Result<FormatArgument, FormatError> {
|
||||
fn next_arg<'a>(
|
||||
mut arguments: impl Iterator<Item = &'a FormatArgument>,
|
||||
) -> Result<&'a FormatArgument, FormatError> {
|
||||
arguments.next().ok_or(FormatError::NoMoreArguments)
|
||||
}
|
||||
|
||||
|
|
|
@ -37,16 +37,14 @@ pub use crate::parser::shortcut_value_parser;
|
|||
pub use crate::features::backup_control;
|
||||
#[cfg(feature = "encoding")]
|
||||
pub use crate::features::encoding;
|
||||
#[cfg(feature = "format")]
|
||||
pub use crate::features::format;
|
||||
#[cfg(feature = "fs")]
|
||||
pub use crate::features::fs;
|
||||
#[cfg(feature = "fsext")]
|
||||
pub use crate::features::fsext;
|
||||
#[cfg(feature = "lines")]
|
||||
pub use crate::features::lines;
|
||||
#[cfg(feature = "format")]
|
||||
pub use crate::features::format;
|
||||
#[cfg(feature = "memo")]
|
||||
pub use crate::features::memo;
|
||||
#[cfg(feature = "quoting-style")]
|
||||
pub use crate::features::quoting_style;
|
||||
#[cfg(feature = "ranges")]
|
||||
|
|
Loading…
Reference in a new issue