use core::fmt;
use crate::BSize;
#[derive(Debug, Clone)]
pub struct Display {
size: u64,
mode: DisplayMode,
}
#[derive(Debug, Clone)]
enum DisplayMode {
Binary,
Decimal,
}
impl Display {
pub fn binary(mut self) -> Self {
self.mode = DisplayMode::Binary;
self
}
pub fn decimal(mut self) -> Self {
self.mode = DisplayMode::Decimal;
self
}
fn new(size: u64) -> Self {
Self {
size,
mode: DisplayMode::Binary,
}
}
}
impl fmt::Display for Display {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let bytes = self.size;
let unit = match self.mode {
DisplayMode::Binary => 1024,
DisplayMode::Decimal => 1000,
};
let unit_prefixes = match self.mode {
DisplayMode::Binary => b"KMGTPE",
DisplayMode::Decimal => b"kMGTPE",
};
let unit_suffix = match self.mode {
DisplayMode::Binary => "iB",
DisplayMode::Decimal => "B",
};
let unit_separator = " ";
let precision = f.precision().unwrap_or(1);
if bytes < unit {
write!(f, "{bytes}{unit_separator}B")?;
} else {
let size = bytes as f64;
let mut ideal_prefix = 0usize;
let mut ideal_size = size;
loop {
ideal_prefix += 1;
ideal_size /= unit as f64;
if ideal_size < unit as f64 {
break;
}
}
let exp = ideal_prefix;
let unit_prefix = unit_prefixes[exp - 1] as char;
write!(
f,
"{:.precision$}{unit_separator}{unit_prefix}{unit_suffix}",
ideal_size,
)?;
}
Ok(())
}
}
macro_rules! impl_display {
($($ty:ty),* $(,)?) => {
$(
impl BSize<$ty> {
pub fn display(self) -> Display {
Display::new(self.0 as u64)
}
}
)*
};
}
impl_display!(u8, u16, u32, u64, usize);
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use super::*;
#[test]
fn test_formatting_snapshots() {
use DisplayMode::*;
fn display(size: u64, mode: DisplayMode) -> Display {
Display { size, mode }
}
assert_snapshot!(display(0, Binary), @"0 B");
assert_snapshot!(display(0, Decimal), @"0 B");
assert_snapshot!(display(1, Binary), @"1 B");
assert_snapshot!(display(1, Decimal), @"1 B");
assert_snapshot!(display(500, Binary), @"500 B");
assert_snapshot!(display(500, Decimal), @"500 B");
assert_snapshot!(display(999, Binary), @"999 B");
assert_snapshot!(display(999, Decimal), @"999 B");
assert_snapshot!(display(1000, Binary), @"1000 B");
assert_snapshot!(display(1000, Decimal), @"1.0 kB");
assert_snapshot!(display(1023, Binary), @"1023 B");
assert_snapshot!(display(1023, Decimal), @"1.0 kB");
assert_snapshot!(display(1024, Binary), @"1.0 KiB");
assert_snapshot!(display(1024, Decimal), @"1.0 kB");
assert_snapshot!(display(1025, Binary), @"1.0 KiB");
assert_snapshot!(display(1025, Decimal), @"1.0 kB");
assert_snapshot!(display(1500, Binary), @"1.5 KiB");
assert_snapshot!(display(1500, Decimal), @"1.5 kB");
assert_snapshot!(display(2048, Binary), @"2.0 KiB");
assert_snapshot!(display(2048, Decimal), @"2.0 kB");
assert_snapshot!(display(1_000_000, Binary), @"976.6 KiB");
assert_snapshot!(display(1_000_000, Decimal), @"1.0 MB");
assert_snapshot!(display(1_048_576, Binary), @"1.0 MiB");
assert_snapshot!(display(1_048_576, Decimal), @"1.0 MB");
assert_snapshot!(display(987_654_321, Binary), @"941.9 MiB");
assert_snapshot!(display(987_654_321, Decimal), @"987.7 MB");
assert_snapshot!(display(1_099_511_627_776, Binary), @"1.0 TiB");
assert_snapshot!(display(1_099_511_627_776, Decimal), @"1.1 TB");
assert_snapshot!(display(1_125_899_906_842_624, Binary), @"1.0 PiB");
assert_snapshot!(display(1_125_899_906_842_624, Decimal), @"1.1 PB");
assert_snapshot!(display(1_152_921_504_606_846_976, Binary), @"1.0 EiB");
assert_snapshot!(display(1_152_921_504_606_846_976, Decimal), @"1.2 EB");
assert_snapshot!(display(u64::MAX - 1, Binary), @"16.0 EiB");
assert_snapshot!(display(u64::MAX - 1, Decimal), @"18.4 EB");
assert_snapshot!(display(u64::MAX, Binary), @"16.0 EiB");
assert_snapshot!(display(u64::MAX, Decimal), @"18.4 EB");
}
}