1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Helps represent bytes with the largest possible mutliple of the byte unit.
//!
//! # Example
//!
//! ```
//! use big_bytes::BigByte;
//!
//! let bytes = 2.456 * 1024_f32.powi(3);
//!
//! assert_eq!("2.46 GiB", bytes.big_byte(2));
//! ```

/// Available multiples of the byte unit.
///
/// *`'K'` Represents __Kibi__, not __Kilo__*.
pub const AVAILABLE_UNIT_SIZES: [Option<char>;9] = [
    None,
    Some('K'),
    Some('M'),
    Some('G'),
    Some('T'),
    Some('P'),
    Some('E'),
    Some('Z'),
    Some('Y'),
];

/// Makes a type representable as a byte count.
pub trait BigByte {
    /// Represents `self` as a byte count.
    fn big_byte(&self, precision: usize) -> String;
}

impl BigByte for f32 {
    fn big_byte(&self, precision: usize) -> String {
        let mut value: f32 = *self;
        let mut counter: usize = 0;
        let (counter, value): (usize, f32) = loop {
            if counter >= AVAILABLE_UNIT_SIZES.len() {
                break (counter, value);
            }
            if value < 1024.0 {
                break (counter, value);
            }
            value /= 1024.0;
            counter += 1;
        };

        match AVAILABLE_UNIT_SIZES.get(counter) {
            Some(None) => format!("{:.*} B", precision, value),
            Some(Some(c)) => format!("{:.*} {}iB", precision, value, c),
            None => format!("{:.*} {}iB", precision, value, AVAILABLE_UNIT_SIZES.last().unwrap().unwrap()),
        }
    }
}
impl BigByte for f64 {
    fn big_byte(&self, precision: usize) -> String {
        let mut value: f64 = *self;
        let mut counter: usize = 0;
        let (counter, value): (usize, f64) = loop {
            if counter + 1 >= AVAILABLE_UNIT_SIZES.len() {
                break (counter, value);
            }
            if value < 1024.0 {
                break (counter, value);
            }
            value /= 1024.0;
            counter += 1;
        };

        match AVAILABLE_UNIT_SIZES.get(counter) {
            Some(None) => format!("{:.*} B", precision, value),
            Some(Some(c)) => format!("{:.*} {}iB", precision, value, c),
            None => format!("{:.*} {}iB", precision, value, AVAILABLE_UNIT_SIZES.last().unwrap().unwrap()),
        }
    }
}

impl BigByte for u8 {
    fn big_byte(&self, precision: usize) -> String {
        (*self as f32).big_byte(precision)
    }
}

impl BigByte for u16 {
    fn big_byte(&self, precision: usize) -> String {
        (*self as f32).big_byte(precision)
    }
}

impl BigByte for u32 {
    fn big_byte(&self, precision: usize) -> String {
        (*self as f32).big_byte(precision)
    }
}

impl BigByte for u64 {
    fn big_byte(&self, precision: usize) -> String {
        (*self as f32).big_byte(precision)
    }
}

impl BigByte for u128 {
    fn big_byte(&self, precision: usize) -> String {
        (*self as f64).big_byte(precision)
    }
}

impl BigByte for usize {
    fn big_byte(&self, precision: usize) -> String {
        (*self as f64).big_byte(precision)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn byte_test() {
        assert_eq!(2.001.big_byte(3), "2.001 B");
    }

    #[test]
    #[allow(non_snake_case)]
    fn u16_two_KiB_test() {
        assert_eq!(2048_u16.big_byte(0), "2 KiB");
    }

    #[test]
    fn gibibyte_test() {
        assert_eq!(2_635_000_987.0.big_byte(1), "2.5 GiB");
    }

    #[test]
    fn too_big_test() {
        let bytes = 2_635.0 * 1024_f64.powi(8);
        assert_eq!(bytes.big_byte(1), "2635.0 YiB");
    }
}