Crate size

source · []
Expand description

This crate provides an ergonomic, type-safe, and aesthetically-pleasing Size type that can be used to express, format, or operate on sizes. While it was initially created to make it painless to “pretty print” file sizes (by automatically determining which unit and with what precision a file size should be textually “written out” or formatted), it has expanded in scope to make it easier and safer to perform the different types of operations that would arise when dealing with sizes.

For almost all users, the only surface of interaction with this crate will take place via the Size type, which can be used to create a strongly-typed representation of a file size (or any other “size” you need to deal with in the abstract). This crate’s API is intended to be as natural and intuitive as possible, providing sensible defaults with zero boilerplate but also allowing the developer to manually control aspects how sizes are expressed as text if needed.

The core Size type is a simple wrapper around a signed numeric value - it can be initialized using whatever primitive numeric type you wish, e.g. constructing a Size from an i64 or from a foo: f64 number of kilobytes.

Using this crate and creating a Size object

To use this crate, you only need to place use size::Size at the top of your rust code, then create a Size from a constructor/initializer that matches the size you have on hand. Both base-2 (KiB, MiB, etc) and base-10 (KB, MB, etc) units are supported and are exposed via the same API. You can either use the abbreviated form of the unit to instantiate your type, or use the full unit name to be more expressive. Here’s an example:

use size::Size;

// Create a strongly-typed size object. We don't even need to specify a numeric type!
let file1_size = Size::from_bytes(200);
// Create another Size instance, this time from a floating-point literal:
let file2_size = Size::from_kb(20.1);

You can obtain a scalar i64 value equal to the total number of bytes described by a Size instance by calling Size::bytes() (see link for more info):

use size::Size;

let file_size = Size::from_gibibytes(4);
assert_eq!(file_size.bytes(), 4_294_967_296);

All Size types can be directly compared (both for order and equality) to one another (or to references of one another), regardless of their original type:

use size::Size;

let size1 = Size::from_kib(4 as u8);
let size2 = Size::from_bytes(4096 as i64);
assert_eq!(size1, size2);

let size1 = Size::from_kib(7);
let size2 = Size::from_kb(7);
assert!(&size2 < &size1);

Textual representation

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 println!(...) with whatever Size you have on hand:

use size::Size;

let file_size = Size::from_bytes(1_340_249);
let textual = format!("{}", file_size); // "1.28 MiB"
assert_eq!(textual.as_str(), "1.28 MiB");

Size::to_string() can be used to directly return a String containing the formatted, human-readable size, instead of needing to use the format!() macro or similar:

use size::Size;

let file_size = Size::from_bytes(1_340_249);
assert_eq!(file_size.to_string(), "1.28 MiB".to_string());

For fine-grained control over how a size is formatted and displayed, you can manually use the Size::format() function, which returns a FormattableSize implementing the builder model to allow you to change one or more properties of how a Size is formatted:

use size::{Size, Base, Style};

let file_size = Size::from_bytes(1_340_249); // same as before
let textual_size = file_size.format()
    .with_base(Base::Base10)
    .with_style(Style::FullLowercase)
    .to_string();
assert_eq!(textual_size, "1.34 megabytes".to_string());

It is also possible to create and configure a standalone SizeFormatter that can be reused to format many sizes in a single, consistent style. This should not be seen as an alternative to wrapping file sizes in strongly-typed Size structs, which should always be the initial instinct.

Mathematical operations

You can perform mathematical operations on Size types and the type safety makes sure that what you’re doing makes sense:

use size::Size;

let sum = Size::from_mib(2) + Size::from_kib(200);
assert_eq!(sum, Size::from_mb(2.301_952));

let size = Size::from_gb(4.2) / 2;
assert_eq!(size, Size::from_gb(2.1));

See the documentation of the ops module for more on this topic.

Crate features

This crate currently has one feature (std), enabled by default. If compiled with --no-default-features or used as a dependency with default features disabled, the crate becomes no_std compatible. When used in no_std 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 mode.

Re-exports

pub use crate::fmt::Base;
pub use crate::fmt::SizeFormatter;
pub use crate::fmt::Style;

Modules

A collection of constants for base-2 and base-10 units.
The fmt module contains SizeFormatter and other types pertaining to formatting a size as human-readable text.
Implementations of basic arithmetic operations on/between Size values.

Structs

Size is the core type exposed by this crate and allows the developer to express a file size (or the general concept of a “size”) as a strongly-typed, convertible type that can be used for textual formatting (“pretty printing”) and mathematical operations.