1use crate::parse_decimal_scaled;
4
5pub fn format_bytes(bytes: u64) -> String {
16 const UNITS: [&str; 6] = ["B", "KB", "MB", "GB", "TB", "PB"];
17 const BASE: u64 = 1024;
18
19 let mut unit = 0;
20 let mut divisor = 1_u64;
21 while unit + 1 < UNITS.len() && bytes / divisor >= BASE {
22 unit += 1;
23 divisor = divisor.saturating_mul(BASE);
24 }
25
26 if unit == 0 {
27 format!("{bytes} B")
28 } else {
29 let whole = bytes / divisor;
30 let remainder = bytes % divisor;
31 let mut hundredths =
32 ((u128::from(remainder) * 100) + (u128::from(divisor) / 2)) / u128::from(divisor);
33 let mut whole = whole;
34 if hundredths == 100 {
35 whole = whole.saturating_add(1);
36 hundredths = 0;
37 }
38 format!("{whole}.{hundredths:02} {}", UNITS[unit])
39 }
40}
41
42pub fn parse_bytes(s: &str) -> Option<u64> {
58 let s = s.trim().to_lowercase();
59 let (num_part, unit_part) = s.split_at(s.find(|c: char| c.is_alphabetic()).unwrap_or(s.len()));
60 let unit = unit_part.trim();
61
62 let multiplier = match unit {
63 "b" | "" => 1_u128,
64 "kb" | "k" | "ki" => 1024_u128,
65 "mb" | "m" | "mi" => 1024_u128.pow(2),
66 "gb" | "g" | "gi" => 1024_u128.pow(3),
67 "tb" | "t" | "ti" => 1024_u128.pow(4),
68 "pb" | "p" | "pi" => 1024_u128.pow(5),
69 _ => return None,
70 };
71
72 parse_decimal_scaled(num_part.trim(), multiplier).and_then(|bytes| u64::try_from(bytes).ok())
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_format_bytes() {
81 assert_eq!(format_bytes(0), "0 B");
82 assert_eq!(format_bytes(512), "512 B");
83 assert_eq!(format_bytes(1024), "1.00 KB");
84 assert_eq!(format_bytes(1024 * 1024 * 5), "5.00 MB");
85 assert_eq!(format_bytes(1024 * 1024 * 1024 * 2), "2.00 GB");
86 }
87
88 #[test]
89 fn test_parse_bytes() {
90 assert_eq!(parse_bytes("512"), Some(512));
91 assert_eq!(parse_bytes("512b"), Some(512));
92 assert_eq!(parse_bytes("1 KB"), Some(1024));
93 assert_eq!(parse_bytes("5mb"), Some(5_242_880));
94 assert_eq!(parse_bytes("2gb"), Some(2_147_483_648));
95 assert_eq!(parse_bytes("1.5 KB"), Some(1536));
96 assert_eq!(parse_bytes("2Mi"), Some(2 * 1024 * 1024));
97 assert_eq!(parse_bytes(" 1Ti "), Some(1024_u64.pow(4)));
98 assert_eq!(parse_bytes("-1 KB"), None);
99 assert_eq!(parse_bytes("18446744073709551616"), None);
100 assert_eq!(parse_bytes("invalid"), None);
101 }
102}