1extern crate regex;
32
33use regex::Regex;
34
35pub const B: u64 = 1;
36pub const KB: u64 = 1_000;
37pub const MB: u64 = 1_000_000;
38pub const GB: u64 = 1_000_000_000;
39pub const TB: u64 = 1_000_000_000_000;
40pub const PB: u64 = 1_000_000_000_000_000;
41
42pub const KIB: u64 = 1_024;
43pub const MIB: u64 = 1_048_576;
44pub const GIB: u64 = 1_073_741_824;
45pub const TIB: u64 = 1_099_511_627_776;
46pub const PIB: u64 = 1_125_899_906_842_624;
47
48#[derive(Debug,PartialEq)]
49pub enum Unit {
50 B,
51 KB,
52 MB,
53 GB,
54 TB,
55 PB,
56 KIB,
57 MIB,
58 GIB,
59 TIB,
60 PIB,
61}
62
63fn parse_size_unit<S: Into<String>>(s: S) -> Result<(f64, Unit), &'static str> {
64 let str = s.into();
65 let re = Regex::new(r"^(?i)(\d+(\.\d+)?) *((k|m|g|t|p|ki|mi|gi|ti|pi)?b)?$").unwrap();
66 let captures = re.captures(&str);
67
68 match captures {
69 Some(res) => {
70 let size = res[1].to_owned();
71 let unit: String = match res.get(3) {
72 Some(val) => val.as_str().to_owned().to_uppercase(),
73 None => "B".to_owned(),
74 };
75
76 Ok((size.parse::<f64>().unwrap(), match &*unit {
77 "B" => Unit::B,
78 "KB" => Unit::KB,
79 "MB" => Unit::MB,
80 "GB" => Unit::GB,
81 "TB" => Unit::TB,
82 "PB" => Unit::PB,
83 "KIB" => Unit::KIB,
84 "MIB" => Unit::MIB,
85 "GIB" => Unit::GIB,
86 "TIB" => Unit::TIB,
87 "PIB" => Unit::PIB,
88 _ => Unit::B,
89 }))
90 }
91 None => Err("Parse Error. Invalid byte format."),
92 }
93}
94
95pub fn parse<S: Into<String>>(str: S) -> Result<u64, &'static str> {
114 let parsed = parse_size_unit(str);
115
116 match parsed {
117 Ok(r) => {
118 let value = r.0;
119 let unit = r.1;
120
121 let bytes = match unit {
122 Unit::B => value * B as f64,
123 Unit::KB => value * KB as f64,
124 Unit::MB => value * MB as f64,
125 Unit::GB => value * GB as f64,
126 Unit::TB => value * TB as f64,
127 Unit::PB => value * PB as f64,
128 Unit::KIB => value * KIB as f64,
129 Unit::MIB => value * MIB as f64,
130 Unit::GIB => value * GIB as f64,
131 Unit::TIB => value * TIB as f64,
132 Unit::PIB => value * PIB as f64,
133 };
134
135 Ok(bytes as u64)
136 },
137 Err(msg) => Err(msg),
138 }
139}
140
141pub fn parse_to<S: Into<String>>(str: S, result_unit: Unit) -> Result<f64, &'static str> {
153 match parse(str) {
154 Ok(bytes) => {
155 let result = match result_unit {
156 Unit::B => bytes as f64,
157 Unit::KB => bytes as f64 / KB as f64,
158 Unit::MB => bytes as f64 / MB as f64,
159 Unit::GB => bytes as f64 / GB as f64,
160 Unit::TB => bytes as f64 / TB as f64,
161 Unit::PB => bytes as f64 / PB as f64,
162 Unit::KIB => bytes as f64 / KIB as f64,
163 Unit::MIB => bytes as f64 / MIB as f64,
164 Unit::GIB => bytes as f64 / GIB as f64,
165 Unit::TIB => bytes as f64 / TIB as f64,
166 Unit::PIB => bytes as f64 / PIB as f64,
167 };
168
169 Ok(result)
170 },
171 Err(msg) => Err(msg),
172 }
173}
174
175pub fn format(bytes: u64) -> String {
188 if bytes < KB {
189 return format_to(bytes, Unit::B);
190 }
191
192 if bytes < MB {
193 return format_to(bytes, Unit::KB);
194 }
195
196 if bytes < GB {
197 return format_to(bytes, Unit::MB);
198 }
199
200 if bytes < TB {
201 return format_to(bytes, Unit::GB);
202 }
203
204 if bytes < PB {
205 return format_to(bytes, Unit::TB);
206 }
207
208 format_to(bytes, Unit::PB)
209}
210
211pub fn format_to(bytes: u64, unit: Unit) -> String {
222 let result = match unit {
223 Unit::B => bytes as f64,
224 Unit::KB => bytes as f64 / KB as f64,
225 Unit::MB => bytes as f64 / MB as f64,
226 Unit::GB => bytes as f64 / GB as f64,
227 Unit::TB => bytes as f64 / TB as f64,
228 Unit::PB => bytes as f64 / PB as f64,
229 Unit::KIB => bytes as f64 / KIB as f64,
230 Unit::MIB => bytes as f64 / MIB as f64,
231 Unit::GIB => bytes as f64 / GIB as f64,
232 Unit::TIB => bytes as f64 / TIB as f64,
233 Unit::PIB => bytes as f64 / PIB as f64,
234 };
235
236 let mut str = format!("{:.2}", result)
237 .trim_end_matches('0')
238 .trim_end_matches('.')
239 .to_string();
240
241 match unit {
242 Unit::B => str.push_str(" B"),
243 Unit::KB => str.push_str(" KB"),
244 Unit::MB => str.push_str(" MB"),
245 Unit::GB => str.push_str(" GB"),
246 Unit::TB => str.push_str(" TB"),
247 Unit::PB => str.push_str(" PB"),
248 Unit::KIB => str.push_str(" KiB"),
249 Unit::MIB => str.push_str(" MiB"),
250 Unit::GIB => str.push_str(" GiB"),
251 Unit::TIB => str.push_str(" TiB"),
252 Unit::PIB => str.push_str(" PiB"),
253 }
254
255 str
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_parse_size_unit() {
264 assert_eq!(parse_size_unit("123").unwrap(), (123_f64, Unit::B));
265 assert_eq!(parse_size_unit("12.34").unwrap(), (12.34_f64, Unit::B));
266 assert_eq!(parse_size_unit("123B").unwrap(), (123_f64, Unit::B));
267 assert_eq!(parse_size_unit("12.34B").unwrap(), (12.34_f64, Unit::B));
268
269 assert_eq!(parse_size_unit("12.34kb").unwrap(), (12.34_f64, Unit::KB));
270 assert_eq!(parse_size_unit("12.34kib").unwrap(), (12.34_f64, Unit::KIB));
271 assert_eq!(parse_size_unit("12.34KB").unwrap(), (12.34_f64, Unit::KB));
272 assert_eq!(parse_size_unit("12.34KiB").unwrap(), (12.34_f64, Unit::KIB));
273
274 assert_eq!(parse_size_unit("12.34mb").unwrap(), (12.34_f64, Unit::MB));
275 assert_eq!(parse_size_unit("12.34mib").unwrap(), (12.34_f64, Unit::MIB));
276 assert_eq!(parse_size_unit("12.34MB").unwrap(), (12.34_f64, Unit::MB));
277 assert_eq!(parse_size_unit("12.34MiB").unwrap(), (12.34_f64, Unit::MIB));
278
279 assert_eq!(parse_size_unit("12.34gb").unwrap(), (12.34_f64, Unit::GB));
280 assert_eq!(parse_size_unit("12.34gib").unwrap(), (12.34_f64, Unit::GIB));
281 assert_eq!(parse_size_unit("12.34GB").unwrap(), (12.34_f64, Unit::GB));
282 assert_eq!(parse_size_unit("12.34GiB").unwrap(), (12.34_f64, Unit::GIB));
283
284 assert_eq!(parse_size_unit("12.34tb").unwrap(), (12.34_f64, Unit::TB));
285 assert_eq!(parse_size_unit("12.34tib").unwrap(), (12.34_f64, Unit::TIB));
286 assert_eq!(parse_size_unit("12.34TB").unwrap(), (12.34_f64, Unit::TB));
287 assert_eq!(parse_size_unit("12.34TiB").unwrap(), (12.34_f64, Unit::TIB));
288
289 assert_eq!(parse_size_unit("12.34pb").unwrap(), (12.34_f64, Unit::PB));
290 assert_eq!(parse_size_unit("12.34pib").unwrap(), (12.34_f64, Unit::PIB));
291 assert_eq!(parse_size_unit("12.34PB").unwrap(), (12.34_f64, Unit::PB));
292 assert_eq!(parse_size_unit("12.34PiB").unwrap(), (12.34_f64, Unit::PIB));
293
294 assert_eq!(parse_size_unit("12.34 kb").unwrap(), (12.34_f64, Unit::KB));
295 assert_eq!(parse_size_unit("12.34 kib").unwrap(), (12.34_f64, Unit::KIB));
296 assert_eq!(parse_size_unit("12.34 KB").unwrap(), (12.34_f64, Unit::KB));
297 assert_eq!(parse_size_unit("12.34 KiB").unwrap(), (12.34_f64, Unit::KIB));
298
299 assert_eq!(parse_size_unit("12.34 mb").unwrap(), (12.34_f64, Unit::MB));
300 assert_eq!(parse_size_unit("12.34 mib").unwrap(), (12.34_f64, Unit::MIB));
301 assert_eq!(parse_size_unit("12.34 MB").unwrap(), (12.34_f64, Unit::MB));
302 assert_eq!(parse_size_unit("12.34 MiB").unwrap(), (12.34_f64, Unit::MIB));
303
304 assert_eq!(parse_size_unit("12.34 gb").unwrap(), (12.34_f64, Unit::GB));
305 assert_eq!(parse_size_unit("12.34 gib").unwrap(), (12.34_f64, Unit::GIB));
306 assert_eq!(parse_size_unit("12.34 GB").unwrap(), (12.34_f64, Unit::GB));
307 assert_eq!(parse_size_unit("12.34 GiB").unwrap(), (12.34_f64, Unit::GIB));
308
309 assert_eq!(parse_size_unit("12.34 tb").unwrap(), (12.34_f64, Unit::TB));
310 assert_eq!(parse_size_unit("12.34 tib").unwrap(), (12.34_f64, Unit::TIB));
311 assert_eq!(parse_size_unit("12.34 TB").unwrap(), (12.34_f64, Unit::TB));
312 assert_eq!(parse_size_unit("12.34 TiB").unwrap(), (12.34_f64, Unit::TIB));
313
314 assert_eq!(parse_size_unit("12.34 pb").unwrap(), (12.34_f64, Unit::PB));
315 assert_eq!(parse_size_unit("12.34 pib").unwrap(), (12.34_f64, Unit::PIB));
316 assert_eq!(parse_size_unit("12.34 PB").unwrap(), (12.34_f64, Unit::PB));
317 assert_eq!(parse_size_unit("12.34 PiB").unwrap(), (12.34_f64, Unit::PIB));
318 }
319
320 #[test]
321 fn test_parse() {
322 assert_eq!(parse("123").unwrap(), 123);
323 assert_eq!(parse("1.23B").unwrap(), 1);
324 assert_eq!(parse("1.23KB").unwrap(), 1_230);
325 assert_eq!(parse("1.23MB").unwrap(), 1_230_000);
326 assert_eq!(parse("1.23GB").unwrap(), 1_230_000_000);
327 assert_eq!(parse("1.23TB").unwrap(), 1_230_000_000_000);
328 assert_eq!(parse("1.23PB").unwrap(), 1_230_000_000_000_000);
329 assert_eq!(parse("1.23KIB").unwrap(), 1_259);
330 assert_eq!(parse("1.23MIB").unwrap(), 1_289_748);
331 assert_eq!(parse("1.23GIB").unwrap(), 1_320_702_443);
332 assert_eq!(parse("1.23TIB").unwrap(), 1_352_399_302_164);
333 assert_eq!(parse("1.23PIB").unwrap(), 1_384_856_885_416_427);
334 }
335
336 #[test]
337 fn test_parse_to() {
338 assert_eq!(parse_to("123", Unit::KB).unwrap(), 0.123);
339 assert_eq!(format!("{:.2}", parse_to("1.23KB", Unit::KB).unwrap()), "1.23");
340 assert_eq!(format!("{:.2}", parse_to("1.23MB", Unit::MB).unwrap()), "1.23");
341 assert_eq!(format!("{:.2}", parse_to("1.23GB", Unit::GB).unwrap()), "1.23");
342 assert_eq!(format!("{:.2}", parse_to("1.23TB", Unit::TB).unwrap()), "1.23");
343 assert_eq!(format!("{:.2}", parse_to("1.23PB", Unit::PB).unwrap()), "1.23");
344 assert_eq!(format!("{:.2}", parse_to("1.23KIB", Unit::KIB).unwrap()), "1.23");
345 assert_eq!(format!("{:.2}", parse_to("1.23MIB", Unit::MIB).unwrap()), "1.23");
346 assert_eq!(format!("{:.2}", parse_to("1.23GIB", Unit::GIB).unwrap()), "1.23");
347 assert_eq!(format!("{:.2}", parse_to("1.23TIB", Unit::TIB).unwrap()), "1.23");
348 assert_eq!(format!("{:.2}", parse_to("1.23PIB", Unit::PIB).unwrap()), "1.23");
349 }
350
351 #[test]
352 fn test_format() {
353 assert_eq!(format(123), "123 B");
354 assert_eq!(format(1_230), "1.23 KB");
355 assert_eq!(format(1_230_000), "1.23 MB");
356 assert_eq!(format(1_230_000_000), "1.23 GB");
357 assert_eq!(format(1_230_000_000_000), "1.23 TB");
358 assert_eq!(format(1_230_000_000_000_000), "1.23 PB");
359 }
360
361
362 #[test]
363 fn test_format_to() {
364 assert_eq!(format_to(123, Unit::B), "123 B");
365 assert_eq!(format_to(1_245, Unit::KB), "1.25 KB");
366 assert_eq!(format_to(1_245_000, Unit::MB), "1.25 MB");
367 assert_eq!(format_to(1_245_000_000, Unit::GB), "1.25 GB");
368 assert_eq!(format_to(1_245_000_000_000, Unit::TB), "1.25 TB");
369 assert_eq!(format_to(1_245_000_000_000_000, Unit::PB), "1.25 PB");
370 assert_eq!(format_to(1_275, Unit::KIB), "1.25 KiB");
371 assert_eq!(format_to(1_306_525, Unit::MIB), "1.25 MiB");
372 assert_eq!(format_to(1_337_882_312, Unit::GIB), "1.25 GiB");
373 assert_eq!(format_to(1_369_991_488_208, Unit::TIB), "1.25 TiB");
374 assert_eq!(format_to(1_402_871_283_925_909, Unit::PIB), "1.25 PiB");
375
376 assert_eq!(format_to(500, Unit::KB), "0.5 KB");
377 assert_eq!(format_to(500_000, Unit::MB), "0.5 MB");
378 assert_eq!(format_to(500_000_000, Unit::GB), "0.5 GB");
379 assert_eq!(format_to(500_000_000_000, Unit::TB), "0.5 TB");
380 assert_eq!(format_to(500_000_000_000_000, Unit::PB), "0.5 PB");
381 assert_eq!(format_to(512, Unit::KIB), "0.5 KiB");
382 assert_eq!(format_to(524_288, Unit::MIB), "0.5 MiB");
383 assert_eq!(format_to(536_870_912, Unit::GIB), "0.5 GiB");
384 assert_eq!(format_to(549_755_813_888, Unit::TIB), "0.5 TiB");
385 assert_eq!(format_to(562_949_953_421_312, Unit::PIB), "0.5 PiB");
386 }
387
388 #[test]
389 fn test_readme() {
390 let input = "1.23 MB";
391
392 let bytes: u64 = parse(input).unwrap();
394 assert_eq!(bytes, 1_230_000);
395
396 let bytes_str = format(bytes);
398 assert_eq!(&bytes_str, input);
399
400 let kb: f64 = parse_to(input, Unit::KB).unwrap();
402 assert_eq!(kb, 1_230 as f64);
403
404 let kb_str = format_to(bytes, Unit::KB);
406 assert_eq!(&kb_str, "1230 KB");
407 }
408}