1use crate::{SizeStandard, format_parts};
9use rust_decimal::Decimal;
10use std::fmt;
11use std::num::NonZeroU64;
12
13#[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 #[inline]
51 pub fn new(bytes: NonZeroU64, standard: SizeStandard) -> Self {
52 Self { bytes, standard }
53 }
54
55 #[inline]
59 pub fn get_nonzero(&self) -> NonZeroU64 {
60 self.bytes
61 }
62
63 #[inline]
67 pub fn as_u64(&self) -> u64 {
68 self.bytes.get()
69 }
70
71 #[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 #[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
98impl 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#[cfg(test)]
119mod tests {
120 use super::FileSize;
121 use std::num::NonZeroU64;
122
123 use crate::SizeStandard;
124
125 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 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 #[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 assert_eq!(format_test_si(1024), "1.02 KB");
154 assert_eq!(format_test_si(9999), "10.00 KB");
156 assert_eq!(format_test_si(10000), "10.0 KB");
158 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 #[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 assert_eq!(format_test_iec(1500), "1.46 KiB");
180 let bytes_near_10 = (9.999 * 1024.0) as u64;
182 assert_eq!(format_test_iec(bytes_near_10), "10.00 KiB");
183 assert_eq!(format_test_iec(10 * 1024), "10.0 KiB");
185 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}