From 69164688addd77bc257624fa7e7c4e64811fd1d7 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Mar 2025 20:35:53 +0100 Subject: [PATCH] uucore: format: Make Formatter a generic Using an associated type in Formatter trait was quite nice, but, in a follow-up change, we'd like to pass a _reference_ to the Float Formatter, while just passing i64/u64 as a value to the Int formatters. Associated type doesn't allow for that, so we turn it into a generic instead. This makes Format<> a bit more complicated though, as we need to specify both the Formatter, _and_ the type to be formatted. --- src/uu/csplit/src/split_name.rs | 4 ++-- src/uu/seq/src/seq.rs | 4 ++-- src/uucore/src/lib/features/format/mod.rs | 14 +++++++---- .../src/lib/features/format/num_format.rs | 23 +++++++------------ 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/uu/csplit/src/split_name.rs b/src/uu/csplit/src/split_name.rs index 29b626efd..a4bd968e5 100644 --- a/src/uu/csplit/src/split_name.rs +++ b/src/uu/csplit/src/split_name.rs @@ -12,7 +12,7 @@ use crate::csplit_error::CsplitError; /// format. pub struct SplitName { prefix: Vec, - format: Format, + format: Format, } impl SplitName { @@ -52,7 +52,7 @@ impl SplitName { None => format!("%0{n_digits}u"), }; - let format = match Format::::parse(format_string) { + let format = match Format::::parse(format_string) { Ok(format) => Ok(format), Err(FormatError::TooManySpecs(_)) => Err(CsplitError::SuffixFormatTooManyPercents), Err(_) => Err(CsplitError::SuffixFormatIncorrect), diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 0c19a28c1..4e136f6a7 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -149,7 +149,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let format = options .format - .map(Format::::parse) + .map(Format::::parse) .transpose()?; let result = print_seq( @@ -258,7 +258,7 @@ fn print_seq( terminator: &str, pad: bool, padding: usize, - format: Option<&Format>, + format: Option<&Format>, ) -> std::io::Result<()> { let stdout = stdout().lock(); let mut stdout = BufWriter::new(stdout); diff --git a/src/uucore/src/lib/features/format/mod.rs b/src/uucore/src/lib/features/format/mod.rs index 059558e49..e44ef4bc0 100644 --- a/src/uucore/src/lib/features/format/mod.rs +++ b/src/uucore/src/lib/features/format/mod.rs @@ -2,6 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +// spell-checker:ignore extendedbigdecimal //! `printf`-style formatting //! @@ -45,6 +46,7 @@ use std::{ error::Error, fmt::Display, io::{stdout, Write}, + marker::PhantomData, ops::ControlFlow, }; @@ -308,20 +310,21 @@ pub fn sprintf<'a>( Ok(writer) } -/// A parsed format for a single float value +/// A parsed format for a single numerical value of type T /// -/// This is used by `seq`. It can be constructed with [`Format::parse`] +/// This is used by `seq` and `csplit`. It can be constructed with [`Format::parse`] /// and can write a value with [`Format::fmt`]. /// /// It can only accept a single specification without any asterisk parameters. /// If it does get more specifications, it will return an error. -pub struct Format { +pub struct Format, T> { prefix: Vec, suffix: Vec, formatter: F, + _marker: PhantomData, } -impl Format { +impl, T> Format { pub fn parse(format_string: impl AsRef<[u8]>) -> Result { let mut iter = parse_spec_only(format_string.as_ref()); @@ -362,10 +365,11 @@ impl Format { prefix, suffix, formatter, + _marker: PhantomData, }) } - pub fn fmt(&self, mut w: impl Write, f: F::Input) -> std::io::Result<()> { + pub fn fmt(&self, mut w: impl Write, f: T) -> std::io::Result<()> { w.write_all(&self.prefix)?; self.formatter.fmt(&mut w, f)?; w.write_all(&self.suffix)?; diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index 0a4e47528..3430ca674 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -13,9 +13,8 @@ use super::{ FormatError, }; -pub trait Formatter { - type Input; - fn fmt(&self, writer: impl Write, x: Self::Input) -> std::io::Result<()>; +pub trait Formatter { + fn fmt(&self, writer: impl Write, x: T) -> std::io::Result<()>; fn try_from_spec(s: Spec) -> Result where Self: Sized; @@ -75,10 +74,8 @@ pub struct SignedInt { pub alignment: NumberAlignment, } -impl Formatter for SignedInt { - type Input = i64; - - fn fmt(&self, writer: impl Write, x: Self::Input) -> std::io::Result<()> { +impl Formatter for SignedInt { + fn fmt(&self, writer: impl Write, x: i64) -> std::io::Result<()> { let s = if self.precision > 0 { format!("{:0>width$}", x.abs(), width = self.precision) } else { @@ -129,10 +126,8 @@ pub struct UnsignedInt { pub alignment: NumberAlignment, } -impl Formatter for UnsignedInt { - type Input = u64; - - fn fmt(&self, mut writer: impl Write, x: Self::Input) -> std::io::Result<()> { +impl Formatter for UnsignedInt { + fn fmt(&self, mut writer: impl Write, x: u64) -> std::io::Result<()> { let mut s = match self.variant { UnsignedIntVariant::Decimal => format!("{x}"), UnsignedIntVariant::Octal(_) => format!("{x:o}"), @@ -236,10 +231,8 @@ impl Default for Float { } } -impl Formatter for Float { - type Input = f64; - - fn fmt(&self, writer: impl Write, f: Self::Input) -> std::io::Result<()> { +impl Formatter for Float { + fn fmt(&self, writer: impl Write, f: f64) -> std::io::Result<()> { let x = f.abs(); let s = if x.is_finite() { match self.variant {