net_bytes/
file_size.rs

1//! File size formatting utilities
2//!
3//! Provides functionality for formatting file sizes with support for both
4//! SI (base-1000) and IEC (base-1024) standards.
5//!
6//! 提供文件大小格式化功能,支持 SI (base-1000) 和 IEC (base-1024) 两种标准。
7
8use crate::{SizeStandard, format_parts};
9use rust_decimal::Decimal;
10use std::fmt;
11use std::num::NonZeroU64;
12
13/// A non-zero file size representation
14///
15/// # Type Parameters
16/// * `USE_SI`:
17///     * `true`: Use SI standard (base-1000, units: KB, MB)
18///     * `false`: Use IEC standard (base-1024, units: KiB, MiB)
19///
20/// 一个表示非零文件大小的结构体。
21///
22/// # 泛型
23/// * `USE_SI`:
24///     * `true`: 使用 SI 标准 (base-1000, 单位 KB, MB)
25///     * `false`: 使用 IEC 标准 (base-1024, 单位 KiB, MiB)
26#[derive(Debug, Copy, Clone)]
27pub struct FileSize {
28    bytes: NonZeroU64,
29    standard: SizeStandard,
30}
31
32impl PartialEq for FileSize {
33    #[inline]
34    fn eq(&self, other: &Self) -> bool {
35        self.bytes == other.bytes
36    }
37}
38
39impl PartialOrd for FileSize {
40    #[inline]
41    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
42        self.bytes.partial_cmp(&other.bytes)
43    }
44}
45
46impl FileSize {
47    /// Create a new FileSize instance
48    /// 
49    /// 创建一个新的 FileSize 实例
50    #[inline]
51    pub fn new(bytes: NonZeroU64, standard: SizeStandard) -> Self {
52        Self { bytes, standard }
53    }
54
55    /// Get the inner NonZeroU64 byte value
56    /// 
57    /// 获取内部的 NonZeroU64 字节值
58    #[inline]
59    pub fn get_nonzero(&self) -> NonZeroU64 {
60        self.bytes
61    }
62
63    /// Get the byte value as u64
64    /// 
65    /// 以 u64 的形式获取字节值
66    #[inline]
67    pub fn as_u64(&self) -> u64 {
68        self.bytes.get()
69    }
70
71    /// Returns the formatted value and unit in SI (base-1000) standard
72    ///
73    /// The formatted_value is returned as String to ensure correct decimal places (e.g., "1.00")
74    ///
75    /// 返回 SI (base-1000) 标准的 (formatted_value, unit)
76    ///
77    /// formatted_value 作为 String 返回,以保证正确的小数位数 (例如 "1.00")
78    #[inline]
79    pub fn get_si_parts(&self) -> (String, &'static str) {
80        const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
81        format_parts(Decimal::from(self.bytes.get()), Decimal::from(1000), UNITS)
82    }
83
84    /// Returns the formatted value and unit in IEC (base-1024) standard
85    ///
86    /// The formatted_value is returned as String to ensure correct decimal places (e.g., "1.00")
87    ///
88    /// 返回 IEC (base-1024) 标准的 (formatted_value, unit)
89    ///
90    /// formatted_value 作为 String 返回,以保证正确的小数位数 (例如 "1.00")
91    #[inline]
92    pub fn get_iec_parts(&self) -> (String, &'static str) {
93        const UNITS: &[&str] = &["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
94        format_parts(Decimal::from(self.bytes.get()), Decimal::from(1024), UNITS)
95    }
96}
97
98/// Implement Display trait for smart formatting
99/// 
100/// 实现 Display trait 来处理智能格式化
101impl fmt::Display for FileSize {
102    #[inline]
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        match self.standard {
105            SizeStandard::SI => {
106                let (formatted_value, unit) = self.get_si_parts();
107                write!(f, "{} {}", formatted_value, unit)
108            }
109            SizeStandard::IEC => {
110                let (formatted_value, unit) = self.get_iec_parts();
111                write!(f, "{} {}", formatted_value, unit)
112            }
113        }
114    }
115}
116
117// 单元测试
118#[cfg(test)]
119mod tests {
120    use super::FileSize;
121    use std::num::NonZeroU64;
122
123    use crate::SizeStandard;
124
125    /// Helper function - SI standard
126/// 
127/// 辅助函数 - SI 标准
128    fn format_test_si(bytes: u64) -> String {
129        let nz = NonZeroU64::new(bytes).expect("测试值不能为零");
130        FileSize::new(nz, SizeStandard::SI).to_string()
131    }
132
133    /// Helper function - IEC standard
134/// 
135/// 辅助函数 - IEC 标准
136    fn format_test_iec(bytes: u64) -> String {
137        let nz = NonZeroU64::new(bytes).expect("测试值不能为零");
138        FileSize::new(nz, SizeStandard::IEC).to_string()
139    }
140
141    // --- Tests for SI (base-1000) standard ---
142    // --- SI (base-1000) 测试 ---
143    #[test]
144    fn test_si_bytes() {
145        assert_eq!(format_test_si(512), "512.0 B");
146        assert_eq!(format_test_si(999), "999.0 B");
147    }
148
149    #[test]
150    fn test_si_kilobytes() {
151        assert_eq!(format_test_si(1000), "1.00 KB");
152        // 1.024 -> 1.02 (2 位小数)
153        assert_eq!(format_test_si(1024), "1.02 KB");
154        // 9.999 -> 10.00 (2 位小数, 舍入)
155        assert_eq!(format_test_si(9999), "10.00 KB");
156        // 10.0 (1 位小数)
157        assert_eq!(format_test_si(10000), "10.0 KB");
158        // 100.0 (1 位小数)
159        assert_eq!(format_test_si(100000), "100.0 KB");
160    }
161
162    #[test]
163    fn test_si_megabytes() {
164        assert_eq!(format_test_si(1_000_000), "1.00 MB");
165    }
166
167    // --- Tests for IEC (base-1024) standard ---
168    // --- IEC (base-1024) 测试 ---
169    #[test]
170    fn test_iec_bytes() {
171        assert_eq!(format_test_iec(512), "512.0 B");
172        assert_eq!(format_test_iec(1023), "1023.0 B");
173    }
174
175    #[test]
176    fn test_iec_kibibytes() {
177        assert_eq!(format_test_iec(1024), "1.00 KiB");
178        // 1.464... -> 1.46 (2 位小数)
179        assert_eq!(format_test_iec(1500), "1.46 KiB");
180        // 9.999... -> 10.00 (2 位小数, 舍入)
181        let bytes_near_10 = (9.999 * 1024.0) as u64;
182        assert_eq!(format_test_iec(bytes_near_10), "10.00 KiB");
183        // 10.0 (1 位小数)
184        assert_eq!(format_test_iec(10 * 1024), "10.0 KiB");
185        // 100.0 (1 位小数)
186        assert_eq!(format_test_iec(100 * 1024), "100.0 KiB");
187    }
188
189    #[test]
190    fn test_iec_mebibytes() {
191        assert_eq!(format_test_iec(1024 * 1024), "1.00 MiB");
192    }
193}