use std::fmt::Write as _;
use arrayvec::ArrayString;
pub fn bytes_human(bytes: u64) -> ArrayString<16> {
const UNITS: [&str; 6] = ["B", "K", "M", "G", "T", "P"];
let mut out = ArrayString::new();
if bytes < 1024 {
let _ = write!(out, "{bytes}B");
return out;
}
let mut value = bytes as f64;
let mut unit = 0;
while value >= 1024.0 && unit + 1 < UNITS.len() {
value /= 1024.0;
unit += 1;
}
let _ = if value < 10.0 {
write!(out, "{value:.1}{}", UNITS[unit])
} else {
write!(out, "{value:.0}{}", UNITS[unit])
};
out
}
pub fn duration_verbose(secs: u64) -> ArrayString<24> {
let mut out = ArrayString::new();
let d = secs / 86_400;
let h = (secs % 86_400) / 3_600;
let m = (secs % 3_600) / 60;
let s = secs % 60;
let _ = if d > 0 {
if h > 0 {
write!(out, "{d}d {h}h")
} else {
write!(out, "{d}d")
}
} else if h > 0 {
if m > 0 {
write!(out, "{h}h {m}m")
} else {
write!(out, "{h}h")
}
} else if m > 0 {
if s > 0 {
write!(out, "{m}m {s}s")
} else {
write!(out, "{m}m")
}
} else {
write!(out, "{s}s")
};
out
}
pub fn duration_human(secs: u64) -> ArrayString<16> {
let mut out = ArrayString::new();
let d = secs / 86_400;
let h = (secs % 86_400) / 3_600;
let m = (secs % 3_600) / 60;
let s = secs % 60;
let _ = if d > 0 {
write!(out, "{d}-{h:02}:{m:02}:{s:02}")
} else if h > 0 {
write!(out, "{h}:{m:02}:{s:02}")
} else {
write!(out, "{m:02}:{s:02}")
};
out
}
pub fn cpu_time_human(ticks: u64, clk_tck: u64) -> ArrayString<16> {
let mut out = ArrayString::new();
if clk_tck == 0 {
out.push('-');
return out;
}
let centis = ticks * 100 / clk_tck;
let m = centis / 6_000;
let s = (centis % 6_000) / 100;
let c = centis % 100;
let _ = write!(out, "{m:02}:{s:02}.{c:02}");
out
}
pub fn percent_human(p: f32) -> ArrayString<8> {
let mut out = ArrayString::new();
if p.is_nan() {
out.push('-');
return out;
}
let _ = write!(out, "{p:.1}");
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bytes_scaling() {
assert_eq!(&*bytes_human(0), "0B");
assert_eq!(&*bytes_human(512), "512B");
assert_eq!(&*bytes_human(1024), "1.0K");
assert_eq!(&*bytes_human(1536), "1.5K");
assert_eq!(&*bytes_human(1024 * 1024 * 200), "200M");
}
#[test]
fn durations() {
assert_eq!(&*duration_human(0), "00:00");
assert_eq!(&*duration_human(75), "01:15");
assert_eq!(&*duration_human(3_725), "1:02:05");
assert_eq!(&*duration_human(90_061), "1-01:01:01");
}
#[test]
fn verbose_durations_have_units() {
assert_eq!(&*duration_verbose(0), "0s");
assert_eq!(&*duration_verbose(45), "45s");
assert_eq!(&*duration_verbose(60), "1m");
assert_eq!(&*duration_verbose(90), "1m 30s");
assert_eq!(&*duration_verbose(3_600), "1h");
assert_eq!(&*duration_verbose(3_725), "1h 2m");
assert_eq!(&*duration_verbose(86_400), "1d");
assert_eq!(&*duration_verbose(90_061), "1d 1h");
assert_eq!(
&*duration_verbose(4 * 86_400 + 23 * 3_600 + 13 * 60),
"4d 23h"
);
}
#[test]
fn verbose_durations_fit_the_buffer() {
let ten_years = 10 * 365 * 86_400u64;
let text = duration_verbose(ten_years);
assert!(!text.is_empty());
assert!(text.len() < 24, "got {text:?}");
}
#[test]
fn cpu_time() {
assert_eq!(&*cpu_time_human(100, 100), "00:01.00");
assert_eq!(&*cpu_time_human(0, 0), "-");
}
}