1use crate::util::round_float;
2
3#[derive(PartialEq, Debug, Clone)]
5#[must_use]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7pub struct PrettyBytes {
8 num: f64,
9 suffix: ByteValues,
10}
11
12impl std::fmt::Display for PrettyBytes {
13 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 write!(f, "{} {:?}", self.num, self.suffix)
15 }
16}
17
18#[derive(Clone, Copy, Debug, PartialEq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20enum ByteValues {
21 B,
22 KB,
23 MB,
24 GB,
25 TB,
26 PB,
27 EB,
28}
29
30impl ByteValues {
31 const UNITS: [Self; 7] = [
33 Self::B,
34 Self::KB,
35 Self::MB,
36 Self::GB,
37 Self::TB,
38 Self::PB,
39 Self::EB,
40 ];
41}
42
43#[allow(clippy::cast_precision_loss)]
60pub fn pretty_bytes(num: u64, round_places: Option<u8>) -> PrettyBytes {
61 if num == 0 {
63 return PrettyBytes {
64 num: 0.,
65 suffix: ByteValues::B,
66 };
67 }
68
69 let exponent = std::cmp::min((num.ilog10() / 3) as usize, ByteValues::UNITS.len() - 1);
70
71 let mut num = num as f64 / 1000_f64.powi(exponent as i32);
72
73 if let Some(round_places) = round_places {
74 num = round_float(num, round_places);
75 }
76
77 let unit = ByteValues::UNITS[exponent];
78
79 PrettyBytes { num, suffix: unit }
80}
81
82pub fn pretty_bytes_signed(num: i64, round_places: Option<u8>) -> PrettyBytes {
93 let is_negative = num.is_negative();
94 let num = num.unsigned_abs();
95
96 let mut pretty_bytes = pretty_bytes(num, round_places);
97
98 if is_negative {
99 pretty_bytes.num = -pretty_bytes.num;
100 }
101
102 pretty_bytes
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 #[allow(clippy::too_many_lines)]
111 fn test_pretty_bytes() {
112 assert_eq!(
114 pretty_bytes(0, None),
115 PrettyBytes {
116 num: 0.,
117 suffix: ByteValues::B,
118 }
119 );
120
121 assert_eq!(
122 pretty_bytes(5_430, None),
123 PrettyBytes {
124 num: 5.43,
125 suffix: ByteValues::KB,
126 }
127 );
128
129 assert_eq!(
131 pretty_bytes(1000_u64.pow(0), None),
132 PrettyBytes {
133 num: 1.,
134 suffix: ByteValues::B,
135 }
136 );
137
138 assert_eq!(
139 pretty_bytes(1000_u64.pow(1), None),
140 PrettyBytes {
141 num: 1.,
142 suffix: ByteValues::KB,
143 }
144 );
145
146 assert_eq!(
147 pretty_bytes(1000_u64.pow(2), None),
148 PrettyBytes {
149 num: 1.,
150 suffix: ByteValues::MB,
151 }
152 );
153
154 assert_eq!(
155 pretty_bytes(1000_u64.pow(3), None),
156 PrettyBytes {
157 num: 1.,
158 suffix: ByteValues::GB,
159 }
160 );
161
162 assert_eq!(
163 pretty_bytes(1000_u64.pow(4), None),
164 PrettyBytes {
165 num: 1.,
166 suffix: ByteValues::TB,
167 }
168 );
169
170 assert_eq!(
171 pretty_bytes(1000_u64.pow(5), None),
172 PrettyBytes {
173 num: 1.,
174 suffix: ByteValues::PB,
175 }
176 );
177
178 assert_eq!(
179 pretty_bytes(1000_u64.pow(6), None),
180 PrettyBytes {
181 num: 1.,
182 suffix: ByteValues::EB,
183 }
184 );
185
186 assert_eq!(
188 pretty_bytes(18_000_000_000_000_000_000, None),
189 PrettyBytes {
190 num: 18.,
191 suffix: ByteValues::EB,
192 }
193 );
194
195 assert_eq!(
197 pretty_bytes(50060, None),
198 PrettyBytes {
199 num: 50.06,
200 suffix: ByteValues::KB,
201 }
202 );
203
204 assert_eq!(
205 pretty_bytes(736_532_432, None),
206 PrettyBytes {
207 num: 736.532_432,
208 suffix: ByteValues::MB,
209 }
210 );
211
212 assert_eq!(
214 pretty_bytes(5003, Some(2)),
215 PrettyBytes {
216 num: 5.,
217 suffix: ByteValues::KB,
218 }
219 );
220
221 assert_eq!(
222 pretty_bytes(8_452_020, Some(2)),
223 PrettyBytes {
224 num: 8.45,
225 suffix: ByteValues::MB,
226 }
227 );
228
229 assert_eq!(
230 pretty_bytes(55_700, Some(0)),
231 PrettyBytes {
232 num: 56.,
233 suffix: ByteValues::KB,
234 }
235 );
236 }
237}