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}