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> {
54 let s = s.trim().to_lowercase();
55 let (num_part, unit_part) = s.split_at(s.find(|c: char| c.is_alphabetic()).unwrap_or(s.len()));
56 let unit = unit_part.trim();
57
58 let multiplier = match unit {
59 "b" | "" => 1_u128,
60 "kb" | "k" => 1024_u128,
61 "mb" | "m" => 1024_u128.pow(2),
62 "gb" | "g" => 1024_u128.pow(3),
63 "tb" | "t" => 1024_u128.pow(4),
64 "pb" | "p" => 1024_u128.pow(5),
65 _ => return None,
66 };
67
68 parse_decimal_scaled(num_part.trim(), multiplier).and_then(|bytes| u64::try_from(bytes).ok())
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn test_format_bytes() {
77 assert_eq!(format_bytes(0), "0 B");
78 assert_eq!(format_bytes(512), "512 B");
79 assert_eq!(format_bytes(1024), "1.00 KB");
80 assert_eq!(format_bytes(1024 * 1024 * 5), "5.00 MB");
81 assert_eq!(format_bytes(1024 * 1024 * 1024 * 2), "2.00 GB");
82 }
83
84 #[test]
85 fn test_parse_bytes() {
86 assert_eq!(parse_bytes("512"), Some(512));
87 assert_eq!(parse_bytes("512b"), Some(512));
88 assert_eq!(parse_bytes("1 KB"), Some(1024));
89 assert_eq!(parse_bytes("5mb"), Some(5_242_880));
90 assert_eq!(parse_bytes("2gb"), Some(2_147_483_648));
91 assert_eq!(parse_bytes("1.5 KB"), Some(1536));
92 assert_eq!(parse_bytes("-1 KB"), None);
93 assert_eq!(parse_bytes("18446744073709551616"), None);
94 assert_eq!(parse_bytes("invalid"), None);
95 }
96}