human-units 0.5.4

Units serialization and formatting library designed for configuration files and command line arguments
Documentation
# human-units

[![Crates.io Version](https://img.shields.io/crates/v/human-units)](https://crates.io/crates/human-units)
[![Docs](https://docs.rs/human-units/badge.svg)](https://docs.rs/human-units)
[![dependency status](https://deps.rs/repo/github/igankevich/human-units/status.svg)](https://deps.rs/repo/github/igankevich/human-units)

Size, duration and other SI units serialization and formatting library designed for configuration files and command line arguments.


## Introduction

`human-units` is a library with `Size`, `Duration` and other SI-related types specifically designed to be used in configuration files and as command line arguments.
These types serialize sizes and durations in _exact_ but human-readable form.

The library also provides `FormatSize`, `FormatDuration` traits
to print _approximate_ values in a short human-readable form.

- No floating point operations.
- No dependencies by default.
- Supports [serde]https://docs.rs/serde/latest/serde/.
- Supports [clap]https://docs.rs/clap/latest/clap/.
- Supports [`no_std`]https://docs.rust-embedded.org/book/intro/no-std.html.
- Tested with [Miri]https://github.com/rust-lang/miri.
- **72–85%** faster than similar libraries (see benchmarks below).
- **50–87%** less binary size compared to similar libraries (see benchmarks below).


## Examples

### Exact human-readable size/duration

```rust
use human_units::{Duration, Size};
assert_eq!("1k", Size(1024).to_string());
assert_eq!("1025", Size(1025).to_string());
assert_eq!("1m", Duration(core::time::Duration::from_secs(60)).to_string());
assert_eq!("61s", Duration(core::time::Duration::from_secs(61)).to_string());
```

### Inexact short human-readable size/duration

```rust
use core::time::Duration;
use human_units::{FormatDuration, FormatSize};
assert_eq!("1 KiB", 1024_u64.format_size().to_string());
assert_eq!("1 m", Duration::from_secs(60).format_duration().to_string());
```

### Custom output

```rust
use colored::Colorize;
use core::time::Duration;
use human_units::{FormatDuration, FormattedDuration};

/// Prints the unit in cyan.
struct ColoredDuration(FormattedDuration);

impl core::fmt::Display for ColoredDuration {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        write!(f, "{}", self.0.integer)?;
        if self.0.fraction != 0 {
            write!(f, ".{}", self.0.fraction)?;
        }
        write!(f, " {}", self.0.unit.cyan())
    }
}

// prints "1 m ago", "m" is printed with cyan color
println!("{} ago", ColoredDuration(Duration::from_secs(60).format_duration()));
```

### Serde integration

```rust
use human_units::Size;
use serde::Serialize;

#[derive(Serialize, PartialEq, Eq, Debug)]
struct SizeWrapper {
    size: Size,
}

let object = SizeWrapper{ size: Size(1024) };
assert_eq!(r#"size = "1k""#, toml::to_string(&object).unwrap().trim());
```

### Clap integration

```rust
use clap::Parser;
use human_units::{Duration, Size};

#[derive(Parser, Debug)]
struct Args {
    #[arg(long, value_parser=clap::value_parser!(Duration))]
    timeout: Duration,
    #[arg(long, value_parser=clap::value_parser!(Size))]
    size: Size,
}

let args = Args::parse_from(["test-clap", "--timeout", "1m", "--size", "1g"]);
assert_eq!(args.timeout, Duration(core::time::Duration::from_secs(60)));
assert_eq!(args.size, Size(1024_u64.pow(3)));
```


### SI units

```rust
use human_units::si::{Frequency, Prefix};

// Convert from hertz, internal representation is nHz (nanohertz).
let cpu_freq = Frequency::with_si_prefix(2200, Prefix::Mega);
assert_eq!("2200 MHz", cpu_freq.to_string());
assert_eq!("2.2 GHz", cpu_freq.format_si().to_string());
```


### IEC units

```rust
use human_units::iec::{Byte, Prefix};

let size = Byte::with_iec_prefix(1536, Prefix::Kibi);
assert_eq!("1536 KiB", size.to_string());
assert_eq!("1.5 MiB", size.format_iec().to_string());
```


### Custom units

```rust
use human_units::si::si_unit;
use human_units::iec::iec_unit;

#[si_unit(symbol = "l")]
struct Volume(pub u64);

let volume = Volume(2_200_000_000);
assert_eq!("2200 ml", volume.to_string());

#[iec_unit(symbol = "B/s")]
struct Throughput(pub u64);

let throughput = Throughput(100 * 1024);
assert_eq!("100 KiB/s", throughput.to_string());
```

## Performance benchmarks

Benchmarks were done with Rust 1.80.1 on a x86\_64 laptop.

### Format size

| Library | Version | Features | Benchmark | Time |
|---------|---------|----------|-----------|------|
| `human_bytes` | 0.4.3 | `fast`       | `format_size_then_to_string` | 88.40 ns ± 5.02 ns|
| `human-repr`  | 1.1.0 | `1024,space` | `format_size_then_to_string` | 161.38 ns ± 13.29 ns|
| `human-units` | 0.1.3 |              | `format_size_then_to_string` | **24.24 ns ± 1.23 ns** |

### Format duration

| Library | Version | Features | Benchmark | Time |
|---------|---------|----------|-----------|------|
| `human-repr`  | 1.1.0 | `1024,space` | `format_duration_then_to_string` | 229.47 ns ± 11.90 ns|
| `human-units` | 0.1.3 |              | `format_duration_then_to_string` | **41.55 ns ± 2.77 ns** |


## Executable size benchmarks

Benchmarks were done with Rust 1.80.1 on a x86\_64 laptop.

### Format size

| Library | Version | Features | Benchmark | Executable size, B |
|---------|---------|----------|-----------|--------------------|
| `human_bytes` | 0.4.3 | `fast`       | print formatted size | 8192  |
| `human-repr`  | 1.1.0 | `1024,space` | print formatted size | 28672 |
| `human-units` | 0.1.3 |              | print formatted size | 4096  |

### Format duration

| Library | Version | Features | Benchmark | Executable size, B |
|---------|---------|----------|-----------|--------------------|
| `human-repr`  | 1.1.0 | `1024,space` | print formatted duration | 28672 |
| `human-units` | 0.1.3 |              | print formatted duration | 4096  |