use std::fmt::Write;
macro_rules! format_with_units {
(
$writer:expr,
$value:expr,
$base_unit:expr,
$( ($threshold:expr, $unit:expr, $divisor:expr) ),*
) => {
let value = $value; let mut handled = false;
$(
if !handled && value >= $threshold {
// 1. Calculate the whole and fractional parts using integer math.
let whole_part = value / $divisor;
let remainder = value % $divisor;
// 2. Calculate the first decimal digit.
// We multiply by 10 before dividing to get the digit.
// E.g., for 1234 bytes -> 1234 % 1024 = 210. (210 * 10) / 1024 = 2.
let decimal_digit = (remainder * 10) / $divisor;
let _ = write!($writer, "{}", whole_part);
if decimal_digit > 0 {
let _ = write!($writer, ".{}", decimal_digit);
}
let _ = write!($writer, "{}", $unit);
handled = true;
}
)*
if !handled {
let _ = write!($writer, "{}{}", value, $base_unit);
}
};
}
pub fn format_byte_size(buf: &mut impl Write, size: usize) {
const KB: usize = 1_000;
const MB: usize = 1_000 * KB;
const GB: usize = 1_000 * MB;
format_with_units!(
buf,
size,
"B",
(GB, "GB", GB),
(MB, "MB", MB),
(KB, "KB", KB)
);
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
fn formatted_byte_size(size: usize) -> String {
let mut s = String::new();
format_byte_size(&mut s, size);
s
}
#[test]
fn test_format_byte_size() {
assert_eq!(formatted_byte_size(512), "512B");
assert_eq!(formatted_byte_size(999), "999B");
assert_eq!(formatted_byte_size(1000), "1KB");
assert_eq!(formatted_byte_size(1024), "1KB");
assert_eq!(formatted_byte_size(1124), "1.1KB");
assert_eq!(formatted_byte_size(1220 * 1000), "1.2MB");
}
}