1use std::fmt::{self, Debug, Display, Write};
18use std::ops::{Div, Mul};
19use std::str::FromStr;
20
21use serde::de::{Unexpected, Visitor};
22use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
23
24const UNIT: u64 = 1;
25
26const BINARY_DATA_MAGNITUDE: u64 = 1024;
27pub const B: u64 = UNIT;
28pub const KIB: u64 = B * BINARY_DATA_MAGNITUDE;
29pub const MIB: u64 = KIB * BINARY_DATA_MAGNITUDE;
30pub const GIB: u64 = MIB * BINARY_DATA_MAGNITUDE;
31pub const TIB: u64 = GIB * BINARY_DATA_MAGNITUDE;
32pub const PIB: u64 = TIB * BINARY_DATA_MAGNITUDE;
33
34#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default)]
35pub struct ReadableSize(pub u64);
36
37impl ReadableSize {
38 pub const fn kb(count: u64) -> ReadableSize {
39 ReadableSize(count * KIB)
40 }
41
42 pub const fn mb(count: u64) -> ReadableSize {
43 ReadableSize(count * MIB)
44 }
45
46 pub const fn gb(count: u64) -> ReadableSize {
47 ReadableSize(count * GIB)
48 }
49
50 pub const fn as_mb(self) -> u64 {
51 self.0 / MIB
52 }
53
54 pub const fn as_bytes(self) -> u64 {
55 self.0
56 }
57}
58
59impl Div<u64> for ReadableSize {
60 type Output = ReadableSize;
61
62 fn div(self, rhs: u64) -> ReadableSize {
63 ReadableSize(self.0 / rhs)
64 }
65}
66
67impl Div<ReadableSize> for ReadableSize {
68 type Output = u64;
69
70 fn div(self, rhs: ReadableSize) -> u64 {
71 self.0 / rhs.0
72 }
73}
74
75impl Mul<u64> for ReadableSize {
76 type Output = ReadableSize;
77
78 fn mul(self, rhs: u64) -> ReadableSize {
79 ReadableSize(self.0 * rhs)
80 }
81}
82
83impl Serialize for ReadableSize {
84 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
85 where
86 S: Serializer,
87 {
88 let size = self.0;
89 let mut buffer = String::new();
90 if size == 0 {
91 write!(buffer, "{}KiB", size).unwrap();
92 } else if size % PIB == 0 {
93 write!(buffer, "{}PiB", size / PIB).unwrap();
94 } else if size % TIB == 0 {
95 write!(buffer, "{}TiB", size / TIB).unwrap();
96 } else if size % GIB as u64 == 0 {
97 write!(buffer, "{}GiB", size / GIB).unwrap();
98 } else if size % MIB as u64 == 0 {
99 write!(buffer, "{}MiB", size / MIB).unwrap();
100 } else if size % KIB as u64 == 0 {
101 write!(buffer, "{}KiB", size / KIB).unwrap();
102 } else {
103 return serializer.serialize_u64(size);
104 }
105 serializer.serialize_str(&buffer)
106 }
107}
108
109impl FromStr for ReadableSize {
110 type Err = String;
111
112 fn from_str(s: &str) -> Result<ReadableSize, String> {
114 let size_str = s.trim();
115 if size_str.is_empty() {
116 return Err(format!("{:?} is not a valid size.", s));
117 }
118
119 if !size_str.is_ascii() {
120 return Err(format!("ASCII string is expected, but got {:?}", s));
121 }
122
123 let size_len = size_str
125 .to_string()
126 .chars()
127 .take_while(|c| char::is_ascii_digit(c) || ['.', 'e', 'E', '-', '+'].contains(c))
128 .count();
129
130 let (size, unit) = size_str.split_at(size_len);
132
133 let unit = match unit.trim() {
134 "K" | "KB" | "KiB" => KIB,
135 "M" | "MB" | "MiB" => MIB,
136 "G" | "GB" | "GiB" => GIB,
137 "T" | "TB" | "TiB" => TIB,
138 "P" | "PB" | "PiB" => PIB,
139 "B" | "" => B,
140 _ => {
141 return Err(format!(
142 "only B, KB, KiB, MB, MiB, GB, GiB, TB, TiB, PB, and PiB are supported: {:?}",
143 s
144 ));
145 }
146 };
147
148 match size.parse::<f64>() {
149 Ok(n) => Ok(ReadableSize((n * unit as f64) as u64)),
150 Err(_) => Err(format!("invalid size string: {:?}", s)),
151 }
152 }
153}
154
155impl Debug for ReadableSize {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 write!(f, "{}", self)
158 }
159}
160
161impl Display for ReadableSize {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 if self.0 >= PIB {
164 write!(f, "{:.1}PiB", self.0 as f64 / PIB as f64)
165 } else if self.0 >= TIB {
166 write!(f, "{:.1}TiB", self.0 as f64 / TIB as f64)
167 } else if self.0 >= GIB {
168 write!(f, "{:.1}GiB", self.0 as f64 / GIB as f64)
169 } else if self.0 >= MIB {
170 write!(f, "{:.1}MiB", self.0 as f64 / MIB as f64)
171 } else if self.0 >= KIB {
172 write!(f, "{:.1}KiB", self.0 as f64 / KIB as f64)
173 } else {
174 write!(f, "{}B", self.0)
175 }
176 }
177}
178
179impl<'de> Deserialize<'de> for ReadableSize {
180 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181 where
182 D: Deserializer<'de>,
183 {
184 struct SizeVisitor;
185
186 impl<'de> Visitor<'de> for SizeVisitor {
187 type Value = ReadableSize;
188
189 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
190 formatter.write_str("valid size")
191 }
192
193 fn visit_i64<E>(self, size: i64) -> Result<ReadableSize, E>
194 where
195 E: de::Error,
196 {
197 if size >= 0 {
198 self.visit_u64(size as u64)
199 } else {
200 Err(E::invalid_value(Unexpected::Signed(size), &self))
201 }
202 }
203
204 fn visit_u64<E>(self, size: u64) -> Result<ReadableSize, E>
205 where
206 E: de::Error,
207 {
208 Ok(ReadableSize(size))
209 }
210
211 fn visit_str<E>(self, size_str: &str) -> Result<ReadableSize, E>
212 where
213 E: de::Error,
214 {
215 size_str.parse().map_err(E::custom)
216 }
217 }
218
219 deserializer.deserialize_any(SizeVisitor)
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_readable_size() {
229 let s = ReadableSize::kb(2);
230 assert_eq!(s.0, 2048);
231 assert_eq!(s.as_mb(), 0);
232 let s = ReadableSize::mb(2);
233 assert_eq!(s.0, 2 * 1024 * 1024);
234 assert_eq!(s.as_mb(), 2);
235 let s = ReadableSize::gb(2);
236 assert_eq!(s.0, 2 * 1024 * 1024 * 1024);
237 assert_eq!(s.as_mb(), 2048);
238
239 assert_eq!((ReadableSize::mb(2) / 2).0, MIB);
240 assert_eq!((ReadableSize::mb(1) / 2).0, 512 * KIB);
241 assert_eq!(ReadableSize::mb(2) / ReadableSize::kb(1), 2048);
242 }
243
244 #[test]
245 fn test_parse_readable_size() {
246 #[derive(Serialize, Deserialize)]
247 struct SizeHolder {
248 s: ReadableSize,
249 }
250
251 let legal_cases = vec![
252 (0, "0KiB"),
253 (2 * KIB, "2KiB"),
254 (4 * MIB, "4MiB"),
255 (5 * GIB, "5GiB"),
256 (7 * TIB, "7TiB"),
257 (11 * PIB, "11PiB"),
258 ];
259 for (size, exp) in legal_cases {
260 let c = SizeHolder {
261 s: ReadableSize(size),
262 };
263 let res_str = toml::to_string(&c).unwrap();
264 let exp_str = format!("s = {:?}\n", exp);
265 assert_eq!(res_str, exp_str);
266 let res_size: SizeHolder = toml::from_str(&exp_str).unwrap();
267 assert_eq!(res_size.s.0, size);
268 }
269
270 let c = SizeHolder {
271 s: ReadableSize(512),
272 };
273 let res_str = toml::to_string(&c).unwrap();
274 assert_eq!(res_str, "s = 512\n");
275 let res_size: SizeHolder = toml::from_str(&res_str).unwrap();
276 assert_eq!(res_size.s.0, c.s.0);
277
278 let decode_cases = vec![
279 (" 0.5 PB", PIB / 2),
280 ("0.5 TB", TIB / 2),
281 ("0.5GB ", GIB / 2),
282 ("0.5MB", MIB / 2),
283 ("0.5KB", KIB / 2),
284 ("0.5P", PIB / 2),
285 ("0.5T", TIB / 2),
286 ("0.5G", GIB / 2),
287 ("0.5M", MIB / 2),
288 ("0.5K", KIB / 2),
289 ("23", 23),
290 ("1", 1),
291 ("1024B", KIB),
292 (" 0.5 PiB", PIB / 2),
294 ("1PiB", PIB),
295 ("0.5 TiB", TIB / 2),
296 ("2 TiB", TIB * 2),
297 ("0.5GiB ", GIB / 2),
298 ("787GiB ", GIB * 787),
299 ("0.5MiB", MIB / 2),
300 ("3MiB", MIB * 3),
301 ("0.5KiB", KIB / 2),
302 ("1 KiB", KIB),
303 ("0.5e6 B", B * 500000),
305 ("0.5E6 B", B * 500000),
306 ("1e6B", B * 1000000),
307 ("8E6B", B * 8000000),
308 ("8e7", B * 80000000),
309 ("1e-1MB", MIB / 10),
310 ("1e+1MB", MIB * 10),
311 ("0e+10MB", 0),
312 ];
313 for (src, exp) in decode_cases {
314 let src = format!("s = {:?}", src);
315 let res: SizeHolder = toml::from_str(&src).unwrap();
316 assert_eq!(res.s.0, exp);
317 }
318
319 let illegal_cases = vec![
320 "0.5kb", "0.5kB", "0.5Kb", "0.5k", "0.5g", "b", "gb", "1b", "B", "1K24B", " 5_KB",
321 "4B7", "5M_",
322 ];
323 for src in illegal_cases {
324 let src_str = format!("s = {:?}", src);
325 assert!(toml::from_str::<SizeHolder>(&src_str).is_err(), "{}", src);
326 }
327 }
328}