disk-usage/src/size.rs
2025-04-05 19:31:07 +02:00

93 lines
2.8 KiB
Rust

//! 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");
}
}