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.

§Parsing sizes from text

The Size::from_str() function can be used to convert the most commonly encountered textual representations of file sizes into properly typed Size objects, with flexible support for various input whitespace formatting, abbreviated/full unit names, mixed upper/lower-case representation, etc.

§Crate features

The following crate features may be chosen:

  • std (enabled by default)
  • serde

If compiled without the std feature (i.e. 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.
  • The ability to parse strings into Size objects (Size::from_str() and the FromStr impl) are removed.

§Base-2 and Base-10 constants

You can individually use constants like size::KiB or size::GB directly or import all constants into scope with use size::consts::* (or just use size::*, but that also imports the types and traits defined by this crate, too).

§Serialization support

If the crate is compiled with the optional (default: disabled) serde feature, the Size type may be serialized/deserialized directly to/from payloads via the serde crate. The Size type is treated as a transparent new-type around u64 for serialization purposes (i.e. it serializes directly to the number of bytes, not as a struct with the number of bytes as a member/field); this allows deserializing payloads from various APIs or other languages that typically do not use strongly-typed Size objects to denote (file) size.

As an example, struct File { name: String, size: Size } will serialize to { name: "name", size: 1234 } instead of { name: "name", size: { bytes: 1234 }.

Re-exports§

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

Modules§

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

Structs§

ParseSizeError
Represents an error parsing a Size from a string representation.
Size
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.