Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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).

Expand Down
86 changes: 49 additions & 37 deletions src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand All @@ -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()
Expand Down Expand Up @@ -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<F: Fn(&mut fmt::Formatter) -> fmt::Result> {
formatter: F,
}

impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> FmtRenderer<F> {
pub fn new(formatter: F) -> Self {
Self { formatter }
}
}

impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> fmt::Display for FmtRenderer<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.formatter)(f)
}
}

impl<T: sealed::FormatterSize> SizeFormatter<T> {
/// Specify the base of the units to be used when generating the textual description of the
/// `Size`.
Expand All @@ -252,7 +234,8 @@ impl<T: sealed::FormatterSize> SizeFormatter<T> {
///
/// # Examples
///
/// ```
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
#[cfg_attr(feature = "std", doc = "```")]
/// use size::Size;
///
/// let formatted = Size::from_bytes(42_000)
Expand All @@ -263,7 +246,8 @@ impl<T: sealed::FormatterSize> SizeFormatter<T> {
/// ```
///
/// 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()
Expand Down Expand Up @@ -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.
Expand All @@ -342,15 +317,16 @@ 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.
///
/// To configure once and repeatedly print sizes in the same format, create a standalone
/// [`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);
Expand Down Expand Up @@ -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);
Expand All @@ -395,8 +372,8 @@ 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.
pub fn format(&self) -> FormattableSize {
/// [`Display`](core::fmt::Display) and will resolve to the same text.
pub fn format(&self) -> FormattableSize<'_> {
FormattableSize {
size: self,
base: DEFAULT_BASE,
Expand Down Expand Up @@ -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<F: Fn(&mut fmt::Formatter) -> fmt::Result> {
formatter: F,
}

impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> FmtRenderer<F> {
pub fn new(formatter: F) -> Self {
Self { formatter }
}
}

impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> fmt::Display for FmtRenderer<F> {
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) })
)
}
}
}
8 changes: 1 addition & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "```")]
Expand Down Expand Up @@ -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`
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<usize> = None;

mod sealed {
Expand Down