Generate beautiful human-readable representations of bytes, durations and even throughputs! Easily call them directly on primitive numbers and Duration
s!
```rust use human_repr::HumanCount;
// counts (bare, bytes, or any custom unit) asserteq!("43.21GB", 43214321123u64.humancountbytes()); asserteq!("123.5kPackets", 123456u32.humancount("Packets")); asserteq!("74.9M", 74893200.humancountbare()); asserteq!("48.1°C", 48.132323432.humancount("°C"));
use human_repr::HumanDuration;
// primitive durations asserteq!("15.6µs", 0.0000156.humanduration()); asserteq!("10ms", 0.01.humanduration()); asserteq!("3.44s", 3.435999.humanduration()); asserteq!("19:20.4", 1160.36.humanduration()); asserteq!("1:14:48", 4488.395.humanduration());
use std::time::Duration; asserteq!("15.6µs", Duration::new(0, 15600).humanduration()); asserteq!("10ms", Duration::fromsecsf64(0.01).humanduration()); asserteq!("1:14:48", Duration::new(4488, 395000000).human_duration());
use human_repr::HumanThroughput;
// throughputs (bare, bytes, or any custom unit) asserteq!("1.2MB/s", (1234567. / 1.).humanthroughputbytes()); asserteq!("6.1tests/m", (8. / 79.).humanthroughput("tests")); asserteq!("9/d", (125. / 1200000.).humanthroughputbare()); asserteq!("54°C/h", (24. / 1600.).humanthroughput("°C")); // the divisions above are just for the sake of clarity: they show // the very concept of a "throughput": number of items per elapsed time. ```
This crate implements a whole suite of:
- counts, supporting SI prefixes k
, M
, G
, T
, P
, E
, Z
, and Y
(including optional IEC prefixes and "mixed" ones, see Rust features);
- durations, supporting SI prefixes nanos (ns
), micros (µs
), millis (ms
), and seconds (s
), in addition to custom minutes:seconds (M:SS.m
) and even hours:minutes:seconds (H:MM:SS
);
- throughputs, supporting per day (/d
), per hour (/h
), per minute (/m
), and per second (/s
) -> per second even gets SI prefixes too.
This crate doesn't have any dependencies, is well-tested, and is blazing fast, taking only ~50 ns to generate a representation! Checked with criterion benchmarks.
This crate gets to 1.0! 🎉 Lots of improvements to get here...
Since 1.0, the
HumanRepr
trait was removed, now there are separate traits for each concept. I've realized that separate traits were better, so I could only implement them where practicable, as well as evolve them independently. The trait names also got simpler: HumanCount, HumanDuration, and HumanThroughput.
Since version 0.11, the
PartialEq
impls for&str
do not allocate any Strings too!
I've developed a particularly interestingWrite
impl, which compares partial sequences with what theDisplay
impl would be generating!
Since version 0.10, the
Debug
impl will show both the raw value and the final representation! Very, very cool: ```rustuse human_repr::{HumanDuration, HumanThroughput};
asserteq!("HumanDuration { val: 1.56e-5 } -> 15.6µs", format!("{:?}", 0.0000156.humanduration())); asserteq!(r#"HumanThroughput { val: 0.015, unit: "°C" } -> 54°C/h"#, format!("{:?}", 0.015.humanthroughput("°C"))); ```
Since version 0.4, I do not allocate any Strings to generate the output! I've returned structs that implement
Display
, so you can print them with no heap allocations at all! And if you do need the String, a simple.to_string()
will do.
They work on all Rust primitive number types: u8
, u16
, u32
, u64
, u128
, usize
, f32
,
f64
, i8
, i16
, i32
, i64
, i128
, isize
, as well as Duration
types.
Note
Duration
s do have aDebug
impl that does something similar, but it is not very human: ```rustuse human_repr::HumanDuration;
use std::time::Duration;
let default = format!("{:?}", Duration::new(0, 14184293)); asserteq!("14.184293ms", default); // 😫👎 asserteq!("14.2ms", Duration::new(0, 14184293).human_duration()); // 😃👍 ```
And of course, I have the minutes and hours views... ```rust
use human_repr::HumanDuration;
use std::time::Duration;
let default = format!("{:?}", Duration::new(10000, 1)); asserteq!("10000.000000001s", default); // 😫👎 asserteq!("2:46:40", Duration::new(10000, 1).human_duration()); // 😃👍 ```
The unit
parameter some methods make available means the entity you're dealing with, like "bytes", "actions", "iterations", "errors", whatever! You can send either the whole unit name, or a shortened one like "B", "it", etc, or even an empty str!
Bytes and bare units have dedicated methods for convenience.
Add this dependency to your Cargo.toml file:
toml
human-repr = "1"
Then just use
the needed traits!
```rust use human_repr::{HumanCount, HumanDuration, HumanThroughput};
3000u16.humancount("bytes"); -5i8.humancountbytes();
4244.32f32.humanduration(); 0.000000000004432f64.humanduration();
Duration::fromsecsf64(0.00432).human_duration();
8987isize.humanthroughput("transactions"); 93321usize.humanthroughput_bytes(); ```
According to the SI standard, there are 1000 bytes in a kilobyte
.
There is another standard called IEC that has 1024 bytes in a kibibyte
, but this is only useful when measuring things that are naturally a power of two, e.g. a stick of RAM.
Be careful to not render IEC quantities with SI scaling, which would be incorrect. But I still support it, if you really want to ;)
By default, human-repr
will have no features enabled, i.e. it will use SI prefixes, 1000
divisor, and no space between values and scales/units.
This crate supports these optional features:
- space
=> include a space between values and scales/units everywhere: 48 B
instead of 48B
, 15.6 µs
instead of 15.6µs
, and 12.4 kB/s
instead of 12.4kB/s
;
- 1024
=> use 1024
divisor — if iec
is not enabled, use prefixes: K
, M
, G
, T
, P
, E
, Z
, and Y
(note the upper 'K'
);
- iec
=> use IEC instead of SI: Ki
, Mi
, Gi
, Ti
, Pi
, Ei
, Zi
, Yi
(implies 1024
).
I've used just one key concept in designing the human duration features: cleanliness.
3.44s
is more meaningful than3.43584783784s
, and14.1µs
is much, much nicer than.0000141233333s
.
So what I do is: round values to at most two decimal places (larger scales have more decimals), and find the best scale to represent them, minimizing resulting values smaller than 1
. The search for the best scale considers even the rounding been applied!
0.000999999
does not end up as999.9µs
(truncate) nor1000µs
(bad scale), it is auto-upgraded to the next one1ms
!
The human duration scale changes seamlessly from nanoseconds to hours!
- values smaller than 60 seconds are rendered as D[.D]scale
, with up to two decimals;
- .0
and .00
are efficiently not generated instead of removed from the output -> it is handled directly in the format arguments;
- from 1 minute onward it changes to M:SS[.m]
;
- from 1 hour onward it changes to H:MM:SS
.
I've made the human throughput with a similar logic. It is funny how much trickier "throughput" is to the human brain!
If something took
1165263
seconds to handle123
items, how fast did it go? It's not obvious...
It doesn't help much even if we divide the duration by the number of items: 9473 seconds/item still does not seem that good. How fast was that? We can't say for sure.
Humm, how many items did we do per time?
Oh, we just need to invert it, so 0.000105555569858 items/second, there it is! 😂
To make some sense of it we now need to multiply that by 3600 (seconds in an hour) to get 0.38 per hour, which is much better, and again by 24 (hours in a day) to finally get 9.12 per day!! Now we know how fast that process was! \o/
As you see, it's not easy at all for our brains to estimate it...
The human throughput scale changes seamlessly from per second to per day!
- .0
and .00
are efficiently not generated too, much like the duration magic;
- it also automatically inserts SI prefixes when in the fastest scale (per second), so we get 2.4MB/s
or 6.42Gitems/s
👍
This is the simplest of them all, I just continually divide by the current divisor (1000 or 1024), until the value gets smaller than it. No funny business like logs or exponentials at all.
Rounding is also handled so there's no truncation or bad scale, the number of decimals also increase the larger the scale gets, and .0
and .00
are also never generated.
HumanRepr
trait was removed, now there are separate traits for each concept: HumanCount
, HumanDuration
, and HumanThroughput
.&str
, which is even faster and does not allocate any Stringsspace
from default featuresnospace
feature to space
, to avoid the negative logic (it is now default, to maintain behavior)HumanReprDuration
, include one decimal in the minutes representationops::Neg
impl1024
only (without iec
)impl AsRef<str>
), greatly improved documentationThis software is licensed under the MIT License. See the LICENSE file in the top distribution directory for the full license text.
Maintaining an open source project is hard and time-consuming, and I've put much ❤️ and effort into this.
If you've appreciated my work, you can back me up with a donation! Thank you 😊