From 36ea367007dab4c70e659deb182daafd2f8b7b81 Mon Sep 17 00:00:00 2001 From: nobbele Date: Sun, 9 Nov 2025 23:14:32 +0100 Subject: [PATCH 1/2] Implement fmt::Display via core instead of std --- README.md | 4 +-- src/fmt.rs | 84 +++++++++++++++++++++++++++++++----------------------- src/lib.rs | 8 +----- 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index c54182b..7dee7e4 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ what this crate can do and how to use it. * definitions for the base-two and base-ten file size units defined as `pub const` in the `size::consts` namespace, available both in abbreviated and unabridged forms (i.e. `consts::KiB` and `consts::KIBIBYTE` or `consts::GB` and `consts::GIGABYTE`), -* an `std::Display` impl for `Size` to automatically display sizes in a human-readable +* an `fmt::Display` impl for `Size` to automatically display sizes in a human-readable format, automatically choosing the best size unit and numeric precision to give the nicest results (you can also use `Size::to_string()` instead). * a `Size.format()` method that gives you more control over how sizes are converted @@ -100,7 +100,7 @@ fn main() { The `size` crate supports parsing textual representations of file sizes into strongly typed `Size` objects, both via the `Size::from_str()` function and its `FromStr` implementation that lets you call `"1234 kilobytes".parse()`. -The `Size` type implements `std::fmt::Display` (in addition to many other traits), which provides a facility to generate properly formatted textual representations of file sizes via the `Size::to_string()` impl of the `ToString` trait or when used in a `format!(..., Size)` context. +The `Size` type implements `fmt::Display` (in addition to many other traits), which provides a facility to generate properly formatted textual representations of file sizes via the `Size::to_string()` impl of the `ToString` trait or when used in a `format!(..., Size)` context. By default, `Size` objects are formatted as base-2 (KiB, MiB, etc) with heuristically chosen precision and units. The member function `Size::format()` can be used to override the unit base (e.g. MB vs MiB) and whether or not abbreviated unit names are used (e.g. KiB vs Kebibyte). diff --git a/src/fmt.rs b/src/fmt.rs index 54a4c9c..3bc397e 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -142,7 +142,7 @@ impl Style { pub const FullLowerCase: Style = Style::FullLowercase; } -impl std::fmt::Display for Size { +impl fmt::Display for Size { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.format()) } @@ -163,7 +163,8 @@ mod sealed { /// approach, but it may come in handy when you have many sizes and all need to be formatted in an /// identical and manually-specified fashion. /// -/// ``` +#[cfg_attr(not(feature = "std"), doc = "```ignore")] +#[cfg_attr(feature = "std", doc = "```")] /// use size::{Base, Size, SizeFormatter, Style}; /// /// let formatter = SizeFormatter::new() @@ -208,25 +209,6 @@ impl Default for SizeFormatter<()> { } } -/// Makes it possible to obtain a string from an `fmt(f: &mut Formatter)` function by initializing -/// this type as a wrapper around said format function, then using `format!("{}", foo)` on the -/// resulting object. -struct FmtRenderer fmt::Result> { - formatter: F, -} - -impl fmt::Result> FmtRenderer { - pub fn new(formatter: F) -> Self { - Self { formatter } - } -} - -impl fmt::Result> fmt::Display for FmtRenderer { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.formatter)(f) - } -} - impl SizeFormatter { /// Specify the base of the units to be used when generating the textual description of the /// `Size`. @@ -252,7 +234,8 @@ impl SizeFormatter { /// /// # Examples /// - /// ``` + #[cfg_attr(not(feature = "std"), doc = "```ignore")] + #[cfg_attr(feature = "std", doc = "```")] /// use size::Size; /// /// let formatted = Size::from_bytes(42_000) @@ -263,7 +246,8 @@ impl SizeFormatter { /// ``` /// /// Sizes that are printed as a whole number of bytes do not have a scale: - /// ``` + #[cfg_attr(not(feature = "std"), doc = "```ignore")] + #[cfg_attr(feature = "std", doc = "```")] /// use size::SizeFormatter; /// /// let bytes = SizeFormatter::new() @@ -323,15 +307,6 @@ impl SizeFormatter<()> { scale: DEFAULT_SCALE, } } - - /// Formats a provided size in bytes as a string, per the configuration of the current - /// `SizeFormatter` instance. - pub fn format(&self, bytes: i64) -> String { - format!( - "{}", - FmtRenderer::new(|fmt: &mut fmt::Formatter| { self.inner_fmt(fmt, bytes) }) - ) - } } /// Result of [`Size::format()`], allowing customization of size pretty printing. @@ -342,7 +317,7 @@ impl SizeFormatter<()> { /// configuration (via the `.with_` functions). /// /// After configuration, a `FormattableSize` may be passed directly to the `println!()` or -/// `format!()` macros and their friends because it implements [`Display`](std::fmt::Display), or +/// `format!()` macros and their friends because it implements [`Display`](core::fmt::Display), or /// [`FormattableSize::to_string()`](ToString::to_string) can be used to retrieve a `String` /// containing the formatted result. /// @@ -350,7 +325,8 @@ impl SizeFormatter<()> { /// [`SizeFormatter`] instead of using `Size::format()`. /// /// Example: -/// ``` +#[cfg_attr(not(feature = "std"), doc = "```ignore")] +#[cfg_attr(feature = "std", doc = "```")] /// use size::{Base, Size, Style}; /// /// let size = Size::from_mib(1.907349); @@ -380,7 +356,8 @@ impl Size { /// [`Base::Base10`]), and the style used to express the determined unit (see [`Style`]). /// /// Example: - /// ``` + #[cfg_attr(not(feature = "std"), doc = "```ignore")] + #[cfg_attr(feature = "std", doc = "```")] /// use size::{Base, Size, Style}; /// /// let size = Size::from_mib(1.907349); @@ -395,7 +372,7 @@ impl Size { /// /// It is not necessary to call `.to_string()` if you are passing the formatted size to a /// `format!()` macro or similar (e.g. `println!` and friends), as the result implements - /// [`Display`](std::fmt::Display) and will resolve to the same text. + /// [`Display`](core::fmt::Display) and will resolve to the same text. pub fn format(&self) -> FormattableSize { FormattableSize { size: self, @@ -651,3 +628,38 @@ const BASE2_RULES: [FormatRule; 17] = [ unit: Unit::Exbibyte, }, ]; + +#[cfg(feature = "std")] +mod std { + use super::*; + + /// Makes it possible to obtain a string from an `fmt(f: &mut Formatter)` function by initializing + /// this type as a wrapper around said format function, then using `format!("{}", foo)` on the + /// resulting object. + struct FmtRenderer fmt::Result> { + formatter: F, + } + + impl fmt::Result> FmtRenderer { + pub fn new(formatter: F) -> Self { + Self { formatter } + } + } + + impl fmt::Result> fmt::Display for FmtRenderer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.formatter)(f) + } + } + + impl SizeFormatter<()> { + /// Formats a provided size in bytes as a string, per the configuration of the current + /// `SizeFormatter` instance. + pub fn format(&self, bytes: i64) -> String { + format!( + "{}", + FmtRenderer::new(|fmt: &mut fmt::Formatter| { self.inner_fmt(fmt, bytes) }) + ) + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 220572b..2bb9bae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ //! //! The majority of users will be interested in this crate for its ability to "pretty print" sizes //! with little ceremony and great results. All `Size` instances implement both -//! [`std::fmt::Display`] and [`std::fmt::Debug`], so you can just directly `format!(...)` or +//! [`core::fmt::Display`] and [`core::fmt::Debug`], so you can just directly `format!(...)` or //! `println!(...)` with whatever `Size` you have on hand: #![cfg_attr(not(feature = "std"), doc = "```ignore")] #![cfg_attr(feature = "std", doc = "```")] @@ -141,7 +141,6 @@ //! mode, the following restrictions and limitations are observed: //! //! * All formatting/stringification of `Size` types is disabled. -//! * `Size` no longer implements [`std::fmt::Display`] (`core::fmt::Debug` is still implemented). //! * The intermediate type used for mathematical operations on `Size` types is changed from `f64` //! to `i64` so that no implicit floating-point math is performed. To prevent inadvertent loss of //! precision, it is forbidden to pass in floating point values to the `Size` API under `no_std` @@ -167,7 +166,6 @@ //! As an example, `struct File { name: String, size: Size } ` will serialize to `{ name: "name", //! size: 1234 }` instead of `{ name: "name", size: { bytes: 1234 }`. -#[cfg(feature = "std")] pub mod fmt; #[cfg(feature = "std")] mod from_str; @@ -180,7 +178,6 @@ mod tests; mod tests_nostd; pub use crate::consts::*; -#[cfg(feature = "std")] pub use crate::fmt::{Base, SizeFormatter, Style}; #[cfg(feature = "std")] pub use crate::from_str::ParseSizeError; @@ -191,11 +188,8 @@ type Intermediate = f64; #[cfg(not(feature = "std"))] type Intermediate = i64; -#[cfg(feature = "std")] const DEFAULT_BASE: Base = Base::Base2; -#[cfg(feature = "std")] const DEFAULT_STYLE: Style = Style::Default; -#[cfg(feature = "std")] const DEFAULT_SCALE: Option = None; mod sealed { From 33294323f079295305f8eb0184466c29f37debaf Mon Sep 17 00:00:00 2001 From: nobbele Date: Sun, 9 Nov 2025 23:15:25 +0100 Subject: [PATCH 2/2] Fix hidden lifetime warning --- src/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fmt.rs b/src/fmt.rs index 3bc397e..c47c46e 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -373,7 +373,7 @@ impl Size { /// It is not necessary to call `.to_string()` if you are passing the formatted size to a /// `format!()` macro or similar (e.g. `println!` and friends), as the result implements /// [`Display`](core::fmt::Display) and will resolve to the same text. - pub fn format(&self) -> FormattableSize { + pub fn format(&self) -> FormattableSize<'_> { FormattableSize { size: self, base: DEFAULT_BASE,