big_bytes/
lib.rs

1//! Helps represent bytes with the largest possible mutliple of the byte unit.
2//!
3//! # Example
4//!
5//! ```
6//! use big_bytes::BigByte;
7//!
8//! let bytes = 2.456 * 1024_f32.powi(3);
9//!
10//! assert_eq!("2.46 GiB", bytes.big_byte(2));
11//! ```
12
13/// Available multiples of the byte unit.
14///
15/// *`'K'` Represents __Kibi__, not __Kilo__*.
16pub const AVAILABLE_UNIT_SIZES: [Option<char>;9] = [
17    None,
18    Some('K'),
19    Some('M'),
20    Some('G'),
21    Some('T'),
22    Some('P'),
23    Some('E'),
24    Some('Z'),
25    Some('Y'),
26];
27
28/// Makes a type representable as a byte count.
29pub trait BigByte {
30    /// Represents `self` as a byte count.
31    fn big_byte(&self, precision: usize) -> String;
32}
33
34impl BigByte for f32 {
35    fn big_byte(&self, precision: usize) -> String {
36        let mut value: f32 = *self;
37        let mut counter: usize = 0;
38        let (counter, value): (usize, f32) = loop {
39            if counter >= AVAILABLE_UNIT_SIZES.len() {
40                break (counter, value);
41            }
42            if value < 1024.0 {
43                break (counter, value);
44            }
45            value /= 1024.0;
46            counter += 1;
47        };
48
49        match AVAILABLE_UNIT_SIZES.get(counter) {
50            Some(None) => format!("{:.*} B", precision, value),
51            Some(Some(c)) => format!("{:.*} {}iB", precision, value, c),
52            None => format!("{:.*} {}iB", precision, value, AVAILABLE_UNIT_SIZES.last().unwrap().unwrap()),
53        }
54    }
55}
56impl BigByte for f64 {
57    fn big_byte(&self, precision: usize) -> String {
58        let mut value: f64 = *self;
59        let mut counter: usize = 0;
60        let (counter, value): (usize, f64) = loop {
61            if counter + 1 >= AVAILABLE_UNIT_SIZES.len() {
62                break (counter, value);
63            }
64            if value < 1024.0 {
65                break (counter, value);
66            }
67            value /= 1024.0;
68            counter += 1;
69        };
70
71        match AVAILABLE_UNIT_SIZES.get(counter) {
72            Some(None) => format!("{:.*} B", precision, value),
73            Some(Some(c)) => format!("{:.*} {}iB", precision, value, c),
74            None => format!("{:.*} {}iB", precision, value, AVAILABLE_UNIT_SIZES.last().unwrap().unwrap()),
75        }
76    }
77}
78
79impl BigByte for u8 {
80    fn big_byte(&self, precision: usize) -> String {
81        (*self as f32).big_byte(precision)
82    }
83}
84
85impl BigByte for u16 {
86    fn big_byte(&self, precision: usize) -> String {
87        (*self as f32).big_byte(precision)
88    }
89}
90
91impl BigByte for u32 {
92    fn big_byte(&self, precision: usize) -> String {
93        (*self as f32).big_byte(precision)
94    }
95}
96
97impl BigByte for u64 {
98    fn big_byte(&self, precision: usize) -> String {
99        (*self as f32).big_byte(precision)
100    }
101}
102
103impl BigByte for u128 {
104    fn big_byte(&self, precision: usize) -> String {
105        (*self as f64).big_byte(precision)
106    }
107}
108
109impl BigByte for usize {
110    fn big_byte(&self, precision: usize) -> String {
111        (*self as f64).big_byte(precision)
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn byte_test() {
121        assert_eq!(2.001.big_byte(3), "2.001 B");
122    }
123
124    #[test]
125    #[allow(non_snake_case)]
126    fn u16_two_KiB_test() {
127        assert_eq!(2048_u16.big_byte(0), "2 KiB");
128    }
129
130    #[test]
131    fn gibibyte_test() {
132        assert_eq!(2_635_000_987.0.big_byte(1), "2.5 GiB");
133    }
134
135    #[test]
136    fn too_big_test() {
137        let bytes = 2_635.0 * 1024_f64.powi(8);
138        assert_eq!(bytes.big_byte(1), "2635.0 YiB");
139    }
140}