//! size in bytes //! //! implement fmt::Display to display size in a human readable unit (B, KB, MB, GB, TB). //! //! For Bytes, unit isn't printed. //! //! implement std::ops::Add use std::fmt; #[derive(PartialEq, PartialOrd, Eq, Ord, Copy, Clone)] pub struct Size(u64); //#[derive(Debug)] impl Size { pub fn new(bytes: u64) -> Self { Self(bytes) } } impl fmt::Display for Size { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let length: u32 = self.0.checked_ilog10().unwrap_or(0); //number of digits -1 // match over number of 3 digits groups, 1000 ~ 1024 // 1000^n and 1024^n have the same number of digits if n<98 // here n<=4 (TB) // // if size in KB or above we want at most 1 decimal match length / 3 { 0 => write!(f, "{}", self.0 as f32), // we assume no unit printed means Bytes. 1 => write!( f, "{}KB", (((self.0 as f32) / 1024.0) * 10.0_f32).round().trunc() / 10.0 ), 2 => write!( f, "{}MB", (((self.0 as f32) / 1048576.0) * 10.0_f32).round().trunc() / 10.0 ), 3 => write!( f, "{}GB", (((self.0 as f32) / 1073741824.0) * 10.0_f32) .round() .trunc() / 10.0 ), 4 => write!( f, "{}TB", (((self.0 as f32) / 1099511627776.0) * 10.0_f32) .round() .trunc() / 10.0 ), _ => panic!(), // unlikely to have PetaBytes of files (and above) on consumer grade hardware } } } impl std::ops::Add for Size { type Output = Self; fn add(self, other: Self) -> Self::Output { Self(self.0 + other.0) } } #[cfg(test)] mod tests { use super::*; #[test] fn create() { assert_eq!(Size::new(1).0, 1); } #[test] fn add() { let s1 = Size::new(60); let s2 = Size::new(40); assert_eq!((s1 + s2).0, 100); } #[test] fn display() { assert_eq!(10u32.checked_ilog10().unwrap_or(0) + 1, 2); assert_eq!(format!("{}", Size::new(1024)), "1KB"); // 1700/1024 = 1.66015625 assert_eq!(format!("{}", Size::new(1700)), "1.7KB"); // 2411724/(1024^2) = 2.299999237060547 assert_eq!(format!("{}", Size::new(2411724)), "2.3MB"); } }