Skip to main content

zeus_fm/zeuslib/utils/fs/
filesize.rs

1////////////////////////////////////////////////////////////////////////////////////////////////////
2//                                            Imports                                             //
3////////////////////////////////////////////////////////////////////////////////////////////////////
4
5use std::cmp::Ordering;
6use std::fmt;
7
8////////////////////////////////////////////////////////////////////////////////////////////////////
9//                                           Constants                                            //
10////////////////////////////////////////////////////////////////////////////////////////////////////
11
12const PB: u64 = 1 << 50;
13const TB: u64 = 1 << 40;
14const GB: u64 = 1 << 30;
15const MB: u64 = 1 << 20;
16const KB: u64 = 1 << 10;
17
18const PB_F: f64 = PB as f64;
19const TB_F: f64 = TB as f64;
20const GB_F: f64 = GB as f64;
21const MB_F: f64 = MB as f64;
22const KB_F: f64 = KB as f64;
23
24////////////////////////////////////////////////////////////////////////////////////////////////////
25//                                          Main Struct                                           //
26////////////////////////////////////////////////////////////////////////////////////////////////////
27
28/// The size of a file, consisting of counts for data units.
29#[derive(Debug, Clone)]
30pub struct FileSize {
31    /// Pebibytes (2<sup>50</sup> bytes) component of the file size
32    pub pb: u16,
33
34    /// Tebibytes (2<sup>40</sup> bytes) component of the file size
35    pub tb: u16,
36
37    /// Gibibytes (2<sup>30</sup> bytes) component of the file size
38    pub gb: u16,
39
40    /// Mebibytes (2<sup>20</sup> bytes) component of the file size
41    pub mb: u16,
42
43    /// Kibibytes (2<sup>10</sup> bytes) component of the file size
44    pub kb: u16,
45
46    /// Bytes component of the file size
47    pub b: u16,
48}
49
50////////////////////////////////////////////////////////////////////////////////////////////////////
51//                                      Main Implementation                                       //
52////////////////////////////////////////////////////////////////////////////////////////////////////
53
54impl FileSize {
55    /// Returns a new `FileSize` with representing given number of bytes
56    ///
57    /// This produces a `FileSize` with optimal values for each count
58    /// field. In other words, the largest possible units are filled 
59    /// first, rather than just setting the bytes field to the input.
60    ///
61    /// # Arguments
62    ///
63    /// * `total_bytes` - The number of bytes the new `FileSize` should represent
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use zeus_fm::zeuslib::utils::fs::FileSize;
69    ///
70    /// const b: u64 = 1048726;
71    ///
72    /// let fs = FileSize::from_total_bytes(b);
73    ///
74    /// assert_eq!(fs.pb, 0);
75    /// assert_eq!(fs.tb, 0);
76    /// assert_eq!(fs.gb, 0);
77    /// assert_eq!(fs.mb, 1);
78    /// assert_eq!(fs.kb, 0);
79    /// assert_eq!(fs.b, 150);
80    /// ```
81    ///
82    pub fn from_total_bytes(total_bytes: u64) -> Self {
83        let b = total_bytes;
84
85        let pb = b / PB;
86        let b = b - pb * PB;
87
88        let tb = b / TB;
89        let b = b - tb * TB;
90        let gb = b / GB;
91        let b = b - gb * GB;
92
93        let mb = b / MB;
94        let b = b - mb * MB;
95        let kb = b / KB;
96        let b = b - kb * KB;
97
98        Self {
99            pb: pb as u16,
100            tb: tb as u16,
101            gb: gb as u16,
102            mb: mb as u16,
103            kb: kb as u16,
104            b: b as u16,
105        }
106    }
107
108    /// Get the total number of bytes in this `FileSize`
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// use zeus_fm::zeuslib::utils::fs::FileSize;
114    ///
115    /// const b: u64 = 1048576;
116    /// let fsize = FileSize::from_total_bytes(b); // Create a `FileSize`
117    /// let total_bytes = fsize.get_total_bytes(); // Get the total number of bytes
118    ///
119    /// assert_eq!(total_bytes, b);
120    /// ```
121    pub fn get_total_bytes(&self) -> u64 {
122        let pb: u64 = (self.pb as u64) * PB;
123        let tb: u64 = (self.tb as u64) * TB;
124        let gb: u64 = (self.gb as u64) * GB;
125        let mb: u64 = (self.mb as u64) * MB;
126        let kb: u64 = (self.kb as u64) * KB;
127        let b: u64 = self.b as u64;
128        b + kb + mb + gb + tb + pb
129    }
130
131    /// Get the total number of Pebibytes (2<sup>50</sup> bytes) this `FileSize` represents as a
132    /// floating-point number.
133    pub fn get_fractional_pb(&self) -> f64 {
134        (self.get_total_bytes() as f64) / PB_F
135    }
136    
137    /// Get the total number of Tebibytes (2<sup>40</sup> bytes) this `FileSize` represents as a
138    /// floating-point number.
139    pub fn get_fractional_tb(&self) -> f64 {
140        (self.get_total_bytes() as f64) / TB_F
141    }
142    
143    /// Get the total number of Gibibytes (2<sup>30</sup> bytes) this `FileSize` represents as a
144    /// floating-point number.
145    pub fn get_fractional_gb(&self) -> f64 {
146        (self.get_total_bytes() as f64) / GB_F
147    }
148    
149    /// Get the total number of Mebibytes (2<sup>20</sup> bytes) this `FileSize` represents as a
150    /// floating-point number.
151    pub fn get_fractional_mb(&self) -> f64 {
152        (self.get_total_bytes() as f64) / MB_F
153    }
154    
155    /// Get the total number of Kibibytes (2<sup>10</sup> bytes) this `FileSize` represents as a
156    /// floating-point number.
157    pub fn get_fractional_kb(&self) -> f64 {
158        (self.get_total_bytes() as f64) / KB_F
159    }
160}
161
162////////////////////////////////////////////////////////////////////////////////////////////////////
163//                                             Traits                                             //
164////////////////////////////////////////////////////////////////////////////////////////////////////
165
166// Allow `FileSize` to be sorted
167impl PartialOrd for FileSize {
168    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
169        Some(self.cmp(other))
170    }
171}
172
173// Allow `FileSize` to be compared
174impl Ord for FileSize {
175    fn cmp(&self, other: &Self) -> Ordering {
176        self.get_total_bytes().cmp(&other.get_total_bytes())
177    }
178}
179
180// Allow `FileSize` to be equality-tested
181impl PartialEq for FileSize {
182    fn eq(&self, other: &Self) -> bool {
183        self.get_total_bytes() == other.get_total_bytes()
184    }
185}
186
187// Strict equality
188impl Eq for FileSize {}
189
190// Allow `FileSize` to be used in string formatting
191impl fmt::Display for FileSize {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        let data: (f64, &str) = if self.pb > 0 {
194            (self.get_fractional_pb(), "PiB")
195        } else if self.tb > 0 {
196            (self.get_fractional_tb(), "TiB")
197        } else if self.gb > 0 {
198            (self.get_fractional_gb(), "GiB")
199        } else if self.mb > 0 {
200            (self.get_fractional_mb(), "MiB")
201        } else if self.kb > 0 {
202            (self.get_fractional_kb(), "KiB")
203        } else {
204            return write!(f, "{}B", self.get_total_bytes());
205        };
206        write!(f, "{:.2}{}", data.0, data.1)
207    }
208}
209
210////////////////////////////////////////////////////////////////////////////////////////////////////
211//                                             Tests                                              //
212////////////////////////////////////////////////////////////////////////////////////////////////////
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    fn filesize_frombytes_1048576() {
220        let fs = FileSize::from_total_bytes(1048576);
221        assert_eq!(fs.pb, 0);
222        assert_eq!(fs.tb, 0);
223        assert_eq!(fs.gb, 0);
224        assert_eq!(fs.mb, 1);
225        assert_eq!(fs.kb, 0);
226        assert_eq!(fs.b, 0);
227    }
228    #[test]
229    fn filesize_frombytes_1048726() {
230        let fs = FileSize::from_total_bytes(1048726);
231        assert_eq!(fs.pb, 0);
232        assert_eq!(fs.tb, 0);
233        assert_eq!(fs.gb, 0);
234        assert_eq!(fs.mb, 1);
235        assert_eq!(fs.kb, 0);
236        assert_eq!(fs.b, 150);
237    }
238}