pub fn readable_size(bytes_per_sec: f64, use_bytes: bool) -> String {
let (value, units) = if use_bytes {
(bytes_per_sec, ["B", "KB", "MB", "GB", "TB"])
} else {
(bytes_per_sec * 8.0, ["b", "kb", "Mb", "Gb", "Tb"])
};
if value < 1_000.0 {
format!("{:.0}{}", value, units[0])
} else if value < 1_000_000.0 {
format!("{:.2}{}", value / 1_000.0, units[1])
} else if value < 1_000_000_000.0 {
format!("{:.2}{}", value / 1_000_000.0, units[2])
} else if value < 1_000_000_000_000.0 {
format!("{:.2}{}", value / 1_000_000_000.0, units[3])
} else {
format!("{:.2}{}", value / 1_000_000_000_000.0, units[4])
}
}
pub fn readable_total(bytes: u64, use_bytes: bool) -> String {
let value = bytes as f64;
let units = ["B", "KB", "MB", "GB", "TB"];
let _ = use_bytes;
if value < 1_000.0 {
format!("{:.0}{}", value, units[0])
} else if value < 1_000_000.0 {
format!("{:.1}{}", value / 1_000.0, units[1])
} else if value < 1_000_000_000.0 {
format!("{:.0}{}", value / 1_000_000.0, units[2])
} else if value < 1_000_000_000_000.0 {
format!("{:.2}{}", value / 1_000_000_000.0, units[3])
} else {
format!("{:.2}{}", value / 1_000_000_000_000.0, units[4])
}
}
pub fn sparkline(data: &[u64], max_width: usize) -> String {
const BLOCKS: [char; 8] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
if data.is_empty() {
return String::new();
}
let start = data.len().saturating_sub(max_width);
let slice = &data[start..];
let max = slice.iter().copied().max().unwrap_or(1).max(1);
slice
.iter()
.map(|&v| {
if v == 0 {
' '
} else {
let idx = ((v as f64 / max as f64) * 7.0).round() as usize;
BLOCKS[idx.min(7)]
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn readable_size_bits_zero() {
assert_eq!(readable_size(0.0, false), "0b");
}
#[test]
fn readable_size_bits_small() {
assert_eq!(readable_size(100.0, false), "800b");
}
#[test]
fn readable_size_bits_kilobits() {
assert_eq!(readable_size(1000.0, false), "8.00kb");
}
#[test]
fn readable_size_bits_megabits() {
assert_eq!(readable_size(1_000_000.0, false), "8.00Mb");
}
#[test]
fn readable_size_bits_gigabits() {
assert_eq!(readable_size(125_000_000.0, false), "1.00Gb");
}
#[test]
fn readable_size_bytes_mode() {
assert_eq!(readable_size(0.0, true), "0B");
assert_eq!(readable_size(500.0, true), "500B");
assert_eq!(readable_size(1500.0, true), "1.50KB");
assert_eq!(readable_size(1_500_000.0, true), "1.50MB");
}
#[test]
fn readable_total_small() {
assert_eq!(readable_total(0, false), "0B");
assert_eq!(readable_total(999, false), "999B");
}
#[test]
fn readable_total_kilobytes() {
assert_eq!(readable_total(1500, false), "1.5KB");
}
#[test]
fn readable_total_megabytes() {
assert_eq!(readable_total(5_000_000, false), "5MB");
}
#[test]
fn readable_total_gigabytes() {
assert_eq!(readable_total(1_500_000_000, false), "1.50GB");
}
#[test]
fn readable_total_terabytes() {
assert_eq!(readable_total(2_000_000_000_000, false), "2.00TB");
}
#[test]
fn readable_size_bits_terabits() {
assert_eq!(readable_size(125_000_000_000.0, false), "1.00Tb");
}
#[test]
fn readable_size_bytes_gigabytes() {
assert_eq!(readable_size(1_500_000_000.0, true), "1.50GB");
}
#[test]
fn readable_size_bytes_terabytes() {
assert_eq!(readable_size(2_000_000_000_000.0, true), "2.00TB");
}
#[test]
fn readable_size_bytes_zero() {
assert_eq!(readable_size(0.0, true), "0B");
}
#[test]
fn readable_size_boundary_999() {
let r = readable_size(999.0, true);
assert!(r.contains("B") && !r.contains("K"));
}
#[test]
fn readable_size_boundary_1000() {
let r = readable_size(1000.0, true);
assert!(r.contains("KB"));
}
#[test]
fn readable_size_boundary_999999() {
let r = readable_size(999_999.0, true);
assert!(r.contains("KB"));
}
#[test]
fn readable_size_boundary_1000000() {
let r = readable_size(1_000_000.0, true);
assert!(r.contains("MB"));
}
#[test]
fn readable_size_fractional_bytes() {
let r = readable_size(0.5, true);
assert_eq!(r, "0B"); }
#[test]
fn readable_size_bits_exact_boundary() {
assert_eq!(readable_size(125.0, false), "1.00kb");
}
#[test]
fn readable_total_exactly_1000() {
let r = readable_total(1000, false);
assert!(r.contains("KB"));
}
#[test]
fn readable_total_use_bytes_flag_ignored() {
assert_eq!(readable_total(500, true), readable_total(500, false));
}
#[test]
fn readable_total_large_value() {
let r = readable_total(10_000_000_000_000, false);
assert!(r.contains("TB"));
}
#[test]
fn readable_total_u64_max() {
let r = readable_total(u64::MAX, false);
assert!(!r.is_empty());
}
#[test]
fn sparkline_empty() {
assert_eq!(sparkline(&[], 10), "");
}
#[test]
fn sparkline_single_value() {
let s = sparkline(&[100], 10);
assert_eq!(s.chars().count(), 1);
assert_eq!(s, "█");
}
#[test]
fn sparkline_all_zeros() {
let s = sparkline(&[0, 0, 0, 0], 10);
assert_eq!(s, " ");
}
#[test]
fn sparkline_ascending() {
let s = sparkline(&[0, 25, 50, 75, 100], 10);
assert_eq!(s.chars().count(), 5);
assert_eq!(s.chars().last().unwrap(), '█');
}
#[test]
fn sparkline_truncates_to_max_width() {
let data: Vec<u64> = (0..100).collect();
let s = sparkline(&data, 20);
assert_eq!(s.chars().count(), 20);
}
#[test]
fn sparkline_uses_recent_data() {
let data: Vec<u64> = (0..100).collect();
let s = sparkline(&data, 5);
assert_eq!(s.chars().count(), 5);
}
#[test]
fn sparkline_uniform_values() {
let s = sparkline(&[50, 50, 50, 50], 10);
let chars: Vec<char> = s.chars().collect();
assert!(chars.iter().all(|&c| c == chars[0]));
}
#[test]
fn sparkline_one_spike() {
let s = sparkline(&[0, 0, 100, 0, 0], 10);
let chars: Vec<char> = s.chars().collect();
assert_eq!(chars[2], '█');
assert_eq!(chars[0], ' ');
assert_eq!(chars[4], ' ');
}
}