si-scale
Format value with units according to SI (système international d’unités).
Version requirement: rustc 1.50+
toml
[dependencies]
si-scale = "0.1"
This crate parses and formats numbers using the SI Scales: from 1 y (yocto, i.e. 1e-24) to 1 Y (Yotta, i.e. 1e24). It is essentially agnostic of units per-se; you can totally keep representing units with strings or uom, or something else.
You can use one of the predefined helper functions to format numbers:
```rust use si_scale::helpers::{seconds, seconds3};
let actual = format!("{}", seconds(1.3e-5)); let expected = "13 µs"; assert_eq!(actual, expected);
let actual = format!("{}", seconds3(1.3e-5)); let expected = "13.000 µs"; assert_eq!(actual, expected); ```
Currently the helper functions are:
| helper fn | mantissa | prefix constraint | base | groupings | example |
| --- | -- | --- | --- | --- | --- |
| number_()
| "{}"
| UnitOnly
| B1000 | _
| 1.234567
, 16
|
| --- | -- | --- | --- | --- | --- |
| seconds()
| "{}"
| UnitAndBelow
| B1000 | none | 1.234567 µs
, 16 ms
|
| seconds3()
| "{:.3}"
| UnitAndBelow
| B1000 | none | 1.235 µs
, 16.000 ms
|
| --- | -- | --- | --- | --- | --- |
| bytes()
| "{}"
| UnitAndAbove
| B1000 | _
| 1.234_567 kB
|
| bytes_()
| "{}"
| UnitOnly
| B1000 | _
| 1_234_567 B
|
| bytes1()
| "{:.1}"
| UnitAndAbove
| B1000 | none | 2.3 TB
|
| --- | -- | --- | --- | --- | --- |
| bibytes()
| "{}"
| UnitAndAbove
| B1024 | _
| 1.234_567 MiB
|
| bibytes1()
| "{:.1}"
| UnitAndAbove
| B1024 | none | 1.2 GiB
|
UnitOnly
means the provided value won't be
scaled: if you provide a value larger than 1000, say 1234, it will be
printed as 1234."{}"
will
display the value with all its digits or no digits if it is round, and
"{:.3}"
for instance will always display one decimal.To define your own format function, use the
scale_fn!()
macro. All pre-defined helper
functions from this crate are defined using this macro.
For instance, let's define a formatting function for bits per sec which prints the mantissa with 2 decimals, and also uses base 1024 (where 1 ki = 1024). Note that although we define the function in a separate module, this is not a requirement.
```rust mod unitfmt { use siscale::scalefn; use siscale::prelude::Value;
// defines the `bits_per_sec()` function
scale_fn!(bits_per_sec,
base: B1024,
constraint: UnitAndAbove,
mantissa_fmt: "{:.2}",
groupings: '_',
unit: "bit/s");
}
use unitfmt::bitsper_sec;
fn main() { let x = 2.1 * 1024 as f32; let actual = format!("throughput: {:>15}", bitspersec(x)); let expected = "throughput: 2.10 kibit/s"; assert_eq!(actual, expected);
let x = 2;
let actual = format!("throughput: {}", bits_per_sec(x));
let expected = "throughput: 2.00 bit/s";
assert_eq!(actual, expected);
}
```
You can omit the groupings
argument of the macro to not sepearate
thousands.
With base = 1000, 1k = 1000, 1M = 1_000_000, 1m = 0.001, 1µ = 0.000_001, etc.
| min (incl.) | max (excl.) | magnitude | prefix |
| --- | --- | --- | ---- |
| .. | .. | -24 | Prefix::Yocto
|
| .. | .. | -21 | Prefix::Zepto
|
| .. | .. | -18 | Prefix::Atto
|
| .. | .. | -15 | Prefix::Femto
|
| .. | .. | -12 | Prefix::Pico
|
| .. | .. | -9 | Prefix::Nano
|
| 0.000_001 | 0.001 | -6 | Prefix::Micro
|
| 0.001 | 1 | -3 | Prefix::Milli
|
| 1 | 1_000 | 0 | Prefix::Unit
|
| 1000 | 1_000_000 | 3 | Prefix::Kilo
|
| 1_000_000 | 1_000_000_000 | 6 | Prefix::Mega
|
| .. | .. | 9 | Prefix::Giga
|
| .. | .. | 12 | Prefix::Tera
|
| .. | .. | 15 | Prefix::Peta
|
| .. | .. | 18 | Prefix::Exa
|
| .. | .. | 21 | Prefix::Zetta
|
| .. | .. | 24 | Prefix::Yotta
|
The base is usually 1000, but can also be 1024 (bibytes).
With base = 1024, 1ki = 1024, 1Mi = 1024 * 1024, etc.
The central representation is the Value
type,
which holds
This crate provides 2 APIs: a low-level API, and a high-level API for convenience.
For the low-level API, the typical use case is
first parse a number into a Value
. For doing
this, you have to specify the base, and maybe some constraint on the SI
scales. See Value::new()
and
Value::new_with()
then display the Value
either by yourself formatting the mantissa
and prefix (implements the fmt::Display
trait), or using the provided
Formatter.
For the high-level API, the typical use cases are
parse and display a number using the provided functions such as
bibytes()
, bytes()
or seconds()
, they will choose for each number
the most appropriate SI scale.
In case you want the same control granularity as the low-level API
(e.g. constraining the scale in some way, using some base, specific
mantissa formatting), then you can build a custom function using the
provided macro scale_fn!()
. The existing functions such as
bibytes()
, bytes()
, seconds()
are all built using this same
macro.
The seconds3()
function parses a number into a Value
and displays it
using 3 decimals and the appropriate scale for seconds (UnitAndBelow
),
so that non-sensical scales such as kilo-seconds can't be output. The
seconds()
function does the same but formats the mantissa with the
default "{}"
, so no decimals are printed for integer mantissa.
```rust use si_scale::helpers::{seconds, seconds3};
let actual = format!("result is {:>15}", seconds(1234.5678)); let expected = "result is 1234.5678 s"; assert_eq!(actual, expected);
let actual = format!("result is {:>10}", seconds3(12.3e-7)); let expected = "result is 1.230 µs"; assert_eq!(actual, expected); ```
The bytes()
function parses a number into a Value
using base 1000
and displays it using 1 decimal and the appropriate scale for bytes
(UnitAndAbove
), so that non-sensical scales such as milli-bytes may not
appear.
```rust use si_scale::helpers::{bytes, bytes1};
let actual = format!("result is {}", bytes1(12345678)); let expected = "result is 12.3 MB"; assert_eq!(actual, expected);
let actual = format!("result is {:>10}", bytes(16)); let expected = "result is 16 B"; assert_eq!(actual, expected);
let actual = format!("result is {}", bytes(0.12)); let expected = "result is 0.12 B"; assert_eq!(actual, expected); ```
The bibytes1()
function parses a number into a Value
using base 1024
and displays it using 1 decimal and the appropriate scale for bytes
(UnitAndAbove
), so that non-sensical scales such as milli-bytes may not
appear.
```rust use si_scale::helpers::{bibytes, bibytes1};
let actual = format!("result is {}", bibytes1(12345678)); let expected = "result is 11.8 MiB"; assert_eq!(actual, expected);