bytefmt/
lib.rs

1//! # bytefmt
2//!
3//! Bytefmt is Rust utility to parse byte string into bytes count and vice versa.
4//!
5//! ## Examples
6//!
7//! ```
8//! extern crate bytefmt;
9//!
10//! fn main() {
11//!     let input = "1.23 MB";
12//!
13//!     // Parse string into bytes
14//!     let bytes: u64 = bytefmt::parse(input).unwrap();
15//!     assert_eq!(bytes, 1_230_000);
16//!
17//!     // Format bytes into string
18//!     let bytes_str = bytefmt::format(bytes);
19//!     assert_eq!(&bytes_str, input);
20//!
21//!     // Parse to specific unit
22//!     let kb: f64 = bytefmt::parse_to(input, bytefmt::Unit::KB).unwrap();
23//!     assert_eq!(kb, 1_230 as f64);
24//!
25//!     // Format to specific unit
26//!     let kb_str = bytefmt::format_to(bytes, bytefmt::Unit::KB);
27//!     assert_eq!(&kb_str, "1230 KB");
28//! }
29//! ```
30////////////////////////////////////////////////////////////////////////////////
31extern 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
95/// Parse given string to bytes count
96///
97/// # Examples  
98///
99/// ```
100/// assert_eq!(bytefmt::parse("123").unwrap(), 123);
101/// assert_eq!(bytefmt::parse("1.23 B").unwrap(), 1);
102/// assert_eq!(bytefmt::parse("1.23 KB").unwrap(), 1_230);
103/// assert_eq!(bytefmt::parse("1.23 MB").unwrap(), 1_230_000);
104/// assert_eq!(bytefmt::parse("1.23 GB").unwrap(), 1_230_000_000);
105/// assert_eq!(bytefmt::parse("1.23 TB").unwrap(), 1_230_000_000_000);
106/// assert_eq!(bytefmt::parse("1.23 PB").unwrap(), 1_230_000_000_000_000);
107/// assert_eq!(bytefmt::parse("1.23 KiB").unwrap(), 1_259);
108/// assert_eq!(bytefmt::parse("1.23 MiB").unwrap(), 1_289_748);
109/// assert_eq!(bytefmt::parse("1.23 GiB").unwrap(), 1_320_702_443);
110/// assert_eq!(bytefmt::parse("1.23 TiB").unwrap(), 1_352_399_302_164);
111/// assert_eq!(bytefmt::parse("1.23 PiB").unwrap(), 1_384_856_885_416_427);
112/// ```
113pub 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
141/// Parse given string to specific byte unit
142///
143/// # Examples  
144///
145/// ```
146/// let kb = bytefmt::parse_to("123B", bytefmt::Unit::KB).unwrap();
147/// let mb = bytefmt::parse_to("123B", bytefmt::Unit::MB).unwrap();
148/// 
149/// assert_eq!(kb, 0.123);
150/// assert_eq!(mb, 0.000123);
151/// ```
152pub 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
175/// Format bytes to byte string
176///
177/// # Examples
178///
179/// ```
180/// assert_eq!(bytefmt::format(123), "123 B");
181/// assert_eq!(bytefmt::format(1_230), "1.23 KB");
182/// assert_eq!(bytefmt::format(1_230_000), "1.23 MB");
183/// assert_eq!(bytefmt::format(1_230_000_000), "1.23 GB");
184/// assert_eq!(bytefmt::format(1_230_000_000_000), "1.23 TB");
185/// assert_eq!(bytefmt::format(1_230_000_000_000_000), "1.23 PB");
186/// ```
187pub 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
211/// Format bytes to specific unit byte string
212///
213/// # Examples
214///
215/// ```
216/// assert_eq!(bytefmt::format_to(1245, bytefmt::Unit::KB), "1.25 KB");
217/// assert_eq!(bytefmt::format_to(1275, bytefmt::Unit::KIB), "1.25 KiB");
218/// assert_eq!(bytefmt::format_to(500, bytefmt::Unit::KB), "0.5 KB");
219/// assert_eq!(bytefmt::format_to(512, bytefmt::Unit::KIB), "0.5 KiB");
220/// ```
221pub 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        // Parse string into bytes
393        let bytes: u64 = parse(input).unwrap();
394        assert_eq!(bytes, 1_230_000);
395
396        // Format bytes into string
397        let bytes_str = format(bytes);
398        assert_eq!(&bytes_str, input);
399
400        // Parse to specific unit
401        let kb: f64 = parse_to(input, Unit::KB).unwrap();
402        assert_eq!(kb, 1_230 as f64);
403
404        // Format to specific unit
405        let kb_str = format_to(bytes, Unit::KB);
406        assert_eq!(&kb_str, "1230 KB");
407    }
408}