use std::fmt;
#[allow(clippy::cast_precision_loss)] const fn bytes_as_f64_for_display(bytes: u64) -> f64 {
bytes as f64
}
#[inline]
#[must_use]
pub fn format_bytes(bytes: u64) -> String {
let mut output = String::new();
format_bytes_into(&mut output, bytes).expect("writing to String cannot fail");
output
}
#[inline]
pub fn format_bytes_into(output: &mut impl fmt::Write, bytes: u64) -> fmt::Result {
const KIB: u64 = 1024;
const MIB: u64 = KIB * 1024;
const GIB: u64 = MIB * 1024;
const TIB: u64 = GIB * 1024;
const PIB: u64 = TIB * 1024;
const KIB_F64: f64 = 1024.0;
const MIB_F64: f64 = KIB_F64 * 1024.0;
const GIB_F64: f64 = MIB_F64 * 1024.0;
const TIB_F64: f64 = GIB_F64 * 1024.0;
const PIB_F64: f64 = TIB_F64 * 1024.0;
if bytes >= PIB {
write!(
output,
"{:.2} PiB",
bytes_as_f64_for_display(bytes) / PIB_F64
)
} else if bytes >= TIB {
write!(
output,
"{:.2} TiB",
bytes_as_f64_for_display(bytes) / TIB_F64
)
} else if bytes >= GIB {
write!(
output,
"{:.2} GiB",
bytes_as_f64_for_display(bytes) / GIB_F64
)
} else if bytes >= MIB {
write!(
output,
"{:.2} MiB",
bytes_as_f64_for_display(bytes) / MIB_F64
)
} else if bytes >= KIB {
write!(
output,
"{:.2} KiB",
bytes_as_f64_for_display(bytes) / KIB_F64
)
} else {
write!(output, "{bytes} B")
}
}
#[inline]
#[must_use]
pub fn format_count(count: u64) -> String {
const UNITS: &[(u64, &str)] = &[
(1_000_000_000_000, "T"),
(1_000_000_000, "B"),
(1_000_000, "M"),
(1_000, "K"),
];
#[allow(clippy::cast_precision_loss)] fn compact(value: u64, divisor: u64, suffix: &str) -> String {
let scaled = value as f64 / divisor as f64;
format!("{scaled:.1}{suffix}")
}
for (idx, (divisor, suffix)) in UNITS.iter().enumerate() {
if count >= *divisor {
let formatted = compact(count, *divisor, suffix);
if formatted.starts_with("1000.0") && idx > 0 {
let (next_divisor, next_suffix) = UNITS[idx - 1];
return compact(count, next_divisor, next_suffix);
}
return formatted;
}
}
count.to_string()
}
#[inline]
#[must_use]
pub fn short_id(uuid: &uuid::Uuid) -> String {
uuid.to_string()[..8].to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_bytes() {
assert_eq!(format_bytes(0), "0 B");
assert_eq!(format_bytes(512), "512 B");
assert_eq!(format_bytes(1024), "1.00 KiB");
assert_eq!(format_bytes(1536), "1.50 KiB");
assert_eq!(format_bytes(1_048_576), "1.00 MiB");
assert_eq!(format_bytes(29_312_178), "27.95 MiB");
assert_eq!(format_bytes(1_073_741_824), "1.00 GiB");
}
#[test]
fn format_bytes_into_matches_owned_formatter() {
let cases = [
0,
512,
1024,
1536,
1_048_576,
29_312_178,
1_073_741_824,
1_099_511_627_776,
1_125_899_906_842_624,
];
for bytes in cases {
let mut output = String::new();
format_bytes_into(&mut output, bytes).unwrap();
assert_eq!(output, format_bytes(bytes));
}
}
#[test]
fn test_format_count() {
assert_eq!(format_count(0), "0");
assert_eq!(format_count(999), "999");
assert_eq!(format_count(1_000), "1.0K");
assert_eq!(format_count(999_999), "1.0M");
assert_eq!(format_count(12_345), "12.3K");
assert_eq!(format_count(1_234_567), "1.2M");
assert_eq!(format_count(1_234_567_890), "1.2B");
assert_eq!(format_count(9_876_543_210_987), "9.9T");
}
#[test]
fn test_short_id() {
let uuid = uuid::Uuid::parse_str("34925aee-7f65-4670-9adc-d2e95ac97b26").unwrap();
assert_eq!(short_id(&uuid), "34925aee");
}
}