use crate::types::UnitMode;
pub fn format_bytes(b: u64, mode: UnitMode) -> String {
match mode {
UnitMode::Bytes => format!("{b}B"),
UnitMode::MiB => format!("{:.1}M", b as f64 / 1_048_576.0),
UnitMode::GiB => format!("{:.1}G", b as f64 / 1_073_741_824.0),
UnitMode::Human => {
if b >= 1_099_511_627_776 {
format!("{:.1}T", b as f64 / 1_099_511_627_776.0)
} else if b >= 1_073_741_824 {
format!("{:.1}G", b as f64 / 1_073_741_824.0)
} else if b >= 1_048_576 {
format!("{:.1}M", b as f64 / 1_048_576.0)
} else if b >= 1024 {
format!("{:.1}K", b as f64 / 1024.0)
} else {
format!("{b}B")
}
}
}
}
pub fn format_uptime(secs: u64) -> String {
let d = secs / 86400;
let h = (secs % 86400) / 3600;
let m = (secs % 3600) / 60;
if d > 0 {
format!("{d}d{h}h{m}m")
} else if h > 0 {
format!("{h}h{m}m")
} else {
format!("{m}m")
}
}
pub fn format_latency(ms: f64) -> String {
if ms < 1.0 {
"<1ms".into()
} else if ms < 1000.0 {
format!("{:.0}ms", ms)
} else {
format!("{:.1}s", ms / 1000.0)
}
}
pub fn format_rate(bytes_per_sec: f64) -> String {
if bytes_per_sec < 1.0 {
"0B/s".into()
} else if bytes_per_sec < 1024.0 {
format!("{:.0}B/s", bytes_per_sec)
} else if bytes_per_sec < 1_048_576.0 {
format!("{:.1}K/s", bytes_per_sec / 1024.0)
} else if bytes_per_sec < 1_073_741_824.0 {
format!("{:.1}M/s", bytes_per_sec / 1_048_576.0)
} else {
format!("{:.1}G/s", bytes_per_sec / 1_073_741_824.0)
}
}
pub fn truncate_mount(mount: &str, width: usize) -> String {
if mount.chars().count() <= width {
format!("{:<width$}", mount, width = width)
} else {
let s: String = mount.chars().take(width.saturating_sub(1)).collect();
format!("{}\u{2026}", s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_bytes_zero() {
assert_eq!(format_bytes(0, UnitMode::Human), "0B");
assert_eq!(format_bytes(0, UnitMode::Bytes), "0B");
assert_eq!(format_bytes(0, UnitMode::GiB), "0.0G");
assert_eq!(format_bytes(0, UnitMode::MiB), "0.0M");
}
#[test]
fn format_bytes_human_small() {
assert_eq!(format_bytes(512, UnitMode::Human), "512B");
assert_eq!(format_bytes(1023, UnitMode::Human), "1023B");
}
#[test]
fn format_bytes_human_kilo() {
assert_eq!(format_bytes(1024, UnitMode::Human), "1.0K");
assert_eq!(format_bytes(1536, UnitMode::Human), "1.5K");
}
#[test]
fn format_bytes_human_mega() {
assert_eq!(format_bytes(1_048_576, UnitMode::Human), "1.0M");
assert_eq!(format_bytes(10 * 1_048_576, UnitMode::Human), "10.0M");
}
#[test]
fn format_bytes_human_giga() {
assert_eq!(format_bytes(1_073_741_824, UnitMode::Human), "1.0G");
assert_eq!(format_bytes(5 * 1_073_741_824, UnitMode::Human), "5.0G");
}
#[test]
fn format_bytes_human_tera() {
assert_eq!(format_bytes(1_099_511_627_776, UnitMode::Human), "1.0T");
assert_eq!(format_bytes(2 * 1_099_511_627_776, UnitMode::Human), "2.0T");
}
#[test]
fn format_bytes_explicit_modes() {
let gb = 1_073_741_824u64;
assert_eq!(format_bytes(gb, UnitMode::Bytes), "1073741824B");
assert_eq!(format_bytes(gb, UnitMode::MiB), "1024.0M");
assert_eq!(format_bytes(gb, UnitMode::GiB), "1.0G");
}
#[test]
fn format_uptime_minutes_only() {
assert_eq!(format_uptime(0), "0m");
assert_eq!(format_uptime(59), "0m");
assert_eq!(format_uptime(60), "1m");
assert_eq!(format_uptime(300), "5m");
}
#[test]
fn format_uptime_hours() {
assert_eq!(format_uptime(3600), "1h0m");
assert_eq!(format_uptime(3660), "1h1m");
assert_eq!(format_uptime(7200), "2h0m");
}
#[test]
fn format_uptime_days() {
assert_eq!(format_uptime(86400), "1d0h0m");
assert_eq!(format_uptime(90000), "1d1h0m");
assert_eq!(format_uptime(2 * 86400 + 3 * 3600 + 15 * 60), "2d3h15m");
}
#[test]
fn truncate_mount_fits() {
let r = truncate_mount("/mnt", 10);
assert_eq!(r.len(), 10);
assert!(r.starts_with("/mnt"));
}
#[test]
fn truncate_mount_exact() {
let r = truncate_mount("/mnt", 4);
assert_eq!(r, "/mnt");
}
#[test]
fn truncate_mount_too_long() {
let r = truncate_mount("/very/long/mount/path", 10);
assert_eq!(r.chars().count(), 10);
assert!(r.ends_with('\u{2026}')); }
#[test]
fn truncate_mount_empty() {
let r = truncate_mount("", 5);
assert_eq!(r.len(), 5);
}
#[test]
fn format_bytes_zero_all_modes() {
assert_eq!(format_bytes(0, UnitMode::Human), "0B");
assert_eq!(format_bytes(0, UnitMode::Bytes), "0B");
assert_eq!(format_bytes(0, UnitMode::GiB), "0.0G");
assert_eq!(format_bytes(0, UnitMode::MiB), "0.0M");
}
#[test]
fn format_bytes_one() {
assert_eq!(format_bytes(1, UnitMode::Human), "1B");
assert_eq!(format_bytes(1, UnitMode::Bytes), "1B");
}
#[test]
fn format_bytes_boundary_kilo_minus_one() {
assert_eq!(format_bytes(1023, UnitMode::Human), "1023B");
}
#[test]
fn format_bytes_boundary_kilo() {
assert_eq!(format_bytes(1024, UnitMode::Human), "1.0K");
}
#[test]
fn format_bytes_boundary_mega_minus_one() {
assert_eq!(format_bytes(1_048_575, UnitMode::Human), "1024.0K");
}
#[test]
fn format_bytes_boundary_mega() {
assert_eq!(format_bytes(1_048_576, UnitMode::Human), "1.0M");
}
#[test]
fn format_bytes_boundary_giga_minus_one() {
assert_eq!(format_bytes(1_073_741_823, UnitMode::Human), "1024.0M");
}
#[test]
fn format_bytes_boundary_giga() {
assert_eq!(format_bytes(1_073_741_824, UnitMode::Human), "1.0G");
}
#[test]
fn format_bytes_boundary_tera_minus_one() {
assert_eq!(format_bytes(1_099_511_627_775, UnitMode::Human), "1024.0G");
}
#[test]
fn format_bytes_boundary_tera() {
assert_eq!(format_bytes(1_099_511_627_776, UnitMode::Human), "1.0T");
}
#[test]
fn format_bytes_max_u64() {
let _ = format_bytes(u64::MAX, UnitMode::Human);
let _ = format_bytes(u64::MAX, UnitMode::Bytes);
let _ = format_bytes(u64::MAX, UnitMode::GiB);
let _ = format_bytes(u64::MAX, UnitMode::MiB);
}
#[test]
fn format_uptime_large_value() {
let secs = 10 * 365 * 86400;
let r = format_uptime(secs);
assert!(r.starts_with("3650d"));
}
#[test]
fn format_uptime_59_seconds() {
assert_eq!(format_uptime(59), "0m");
}
#[test]
fn format_uptime_60_seconds() {
assert_eq!(format_uptime(60), "1m");
}
#[test]
fn format_uptime_23h59m() {
assert_eq!(format_uptime(86399), "23h59m");
}
#[test]
fn truncate_mount_unicode() {
let r = truncate_mount("/日本語/パス", 8);
assert!(r.chars().count() <= 8);
}
#[test]
fn truncate_mount_width_equals_string() {
let s = "/mnt/data";
let r = truncate_mount(s, s.len());
assert_eq!(r, s);
}
#[test]
fn truncate_mount_width_one_more_than_string() {
let s = "/mnt";
let r = truncate_mount(s, s.len() + 1);
assert!(r.starts_with(s));
assert_eq!(r.chars().count(), s.len() + 1);
}
#[test]
fn format_latency_sub_millisecond() {
assert_eq!(format_latency(0.3), "<1ms");
assert_eq!(format_latency(0.0), "<1ms");
}
#[test]
fn format_latency_milliseconds() {
assert_eq!(format_latency(1.0), "1ms");
assert_eq!(format_latency(12.4), "12ms");
assert_eq!(format_latency(150.0), "150ms");
assert_eq!(format_latency(999.9), "1000ms");
}
#[test]
fn format_latency_seconds() {
assert_eq!(format_latency(1000.0), "1.0s");
assert_eq!(format_latency(2500.0), "2.5s");
}
#[test]
fn format_rate_zero() {
assert_eq!(format_rate(0.0), "0B/s");
}
#[test]
fn format_rate_bytes() {
assert_eq!(format_rate(512.0), "512B/s");
}
#[test]
fn format_rate_kilobytes() {
assert_eq!(format_rate(1024.0), "1.0K/s");
assert_eq!(format_rate(10240.0), "10.0K/s");
}
#[test]
fn format_rate_megabytes() {
assert_eq!(format_rate(1_048_576.0), "1.0M/s");
assert_eq!(format_rate(52_428_800.0), "50.0M/s");
}
#[test]
fn format_rate_gigabytes() {
assert_eq!(format_rate(1_073_741_824.0), "1.0G/s");
}
}