1use crate::errors::*;
2use nom::IResult;
3use nom::Parser as _;
4use nom::branch::alt;
5use nom::bytes::complete::tag;
6use nom::combinator::{all_consuming, map, opt, value};
7use nom::number::complete::double;
8use std::fmt;
9use std::str::FromStr;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
13pub enum Prefix {
14 Nano,
16 Micro,
18 Milli,
20 #[default]
22 One,
23 OneButBinary,
26 Kilo,
28 Kibi,
30 Mega,
32 Mebi,
34 Giga,
36 Gibi,
38 Tera,
40 Tebi,
42}
43
44const MUL: [f64; 13] = [
45 1e-9,
46 1e-6,
47 1e-3,
48 1.0,
49 1.0,
50 1e3,
51 1024.0,
52 1e6,
53 1024.0 * 1024.0,
54 1e9,
55 1024.0 * 1024.0 * 1024.0,
56 1e12,
57 1024.0 * 1024.0 * 1024.0 * 1024.0,
58];
59
60impl Prefix {
61 #[inline]
62 pub const fn min_available() -> Self {
63 Self::Nano
64 }
65
66 #[inline]
67 pub const fn max_available() -> Self {
68 Self::Tebi
69 }
70
71 #[inline]
72 pub fn max(self, other: Self) -> Self {
73 if other > self { other } else { self }
74 }
75
76 #[inline]
77 pub const fn apply(self, value: f64) -> f64 {
78 value / MUL[self as usize]
79 }
80
81 #[inline]
82 pub const fn unapply(self, value: f64) -> f64 {
83 value * MUL[self as usize]
84 }
85
86 pub fn eng(mut number: f64) -> Self {
87 if number == 0.0 {
88 Self::One
89 } else {
90 number = number.abs();
91 if number > 1.0 {
92 number = number.round();
93 } else {
94 let round_up_to = -(number.log10().ceil() as i32);
95 let m = 10f64.powi(round_up_to);
96 number = (number * m).round() / m;
97 }
98 match number.log10().div_euclid(3.) as i32 {
99 i32::MIN..=-3 => Prefix::Nano,
100 -2 => Prefix::Micro,
101 -1 => Prefix::Milli,
102 0 => Prefix::One,
103 1 => Prefix::Kilo,
104 2 => Prefix::Mega,
105 3 => Prefix::Giga,
106 4..=i32::MAX => Prefix::Tera,
107 }
108 }
109 }
110
111 pub fn eng_binary(number: f64) -> Self {
112 if number == 0.0 {
113 Self::One
114 } else {
115 match number.abs().round().log2().div_euclid(10.) as i32 {
116 i32::MIN..=0 => Prefix::OneButBinary,
117 1 => Prefix::Kibi,
118 2 => Prefix::Mebi,
119 3 => Prefix::Gibi,
120 4..=i32::MAX => Prefix::Tebi,
121 }
122 }
123 }
124
125 #[inline]
126 pub const fn is_binary(&self) -> bool {
127 matches!(
128 self,
129 Self::OneButBinary | Self::Kibi | Self::Mebi | Self::Gibi | Self::Tebi
130 )
131 }
132}
133
134impl fmt::Display for Prefix {
135 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136 f.write_str(match self {
137 Self::Nano => "n",
138 Self::Micro => "u",
139 Self::Milli => "m",
140 Self::One | Self::OneButBinary => "",
141 Self::Kilo => "K",
142 Self::Kibi => "Ki",
143 Self::Mega => "M",
144 Self::Mebi => "Mi",
145 Self::Giga => "G",
146 Self::Gibi => "Gi",
147 Self::Tera => "T",
148 Self::Tebi => "Ti",
149 })
150 }
151}
152
153impl FromStr for Prefix {
154 type Err = Error;
155
156 fn from_str(s: &str) -> Result<Self> {
157 match s {
158 "n" => Ok(Prefix::Nano),
159 "u" => Ok(Prefix::Micro),
160 "m" => Ok(Prefix::Milli),
161 "1" => Ok(Prefix::One),
162 "1i" => Ok(Prefix::OneButBinary),
163 "K" => Ok(Prefix::Kilo),
164 "Ki" => Ok(Prefix::Kibi),
165 "M" => Ok(Prefix::Mega),
166 "Mi" => Ok(Prefix::Mebi),
167 "G" => Ok(Prefix::Giga),
168 "Gi" => Ok(Prefix::Gibi),
169 "T" => Ok(Prefix::Tera),
170 "Ti" => Ok(Prefix::Tebi),
171 x => Err(Error::new(format!("Unknown prefix: '{x}'"))),
172 }
173 }
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
177pub struct ValuePrefix(pub f64, pub Prefix);
178
179impl ValuePrefix {
180 #[inline]
181 pub const fn value(&self) -> f64 {
182 self.0
183 }
184
185 #[inline]
186 pub const fn prefix(&self) -> Prefix {
187 self.1
188 }
189
190 #[inline]
191 pub const fn result(&self) -> f64 {
192 self.prefix().unapply(self.value())
193 }
194}
195
196impl fmt::Display for ValuePrefix {
197 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198 write!(f, "{}{}", self.value(), self.prefix())
199 }
200}
201
202impl FromStr for ValuePrefix {
203 type Err = Error;
204
205 fn from_str(s: &str) -> Result<Self, Self::Err> {
206 parse_value_prefix(s)
207 .map(|(_, v)| v)
208 .map_err(|e| Error::new(format!("Failed to parse value prefix: {e}")))
209 }
210}
211
212fn parse_prefix(input: &str) -> IResult<&str, Prefix> {
213 map(
214 opt(alt((
215 value(Prefix::Kibi, tag("Ki")),
216 value(Prefix::Mebi, tag("Mi")),
217 value(Prefix::Gibi, tag("Gi")),
218 value(Prefix::Tebi, tag("Ti")),
219 value(Prefix::Nano, tag("n")),
220 value(Prefix::Micro, tag("u")),
221 value(Prefix::Milli, tag("m")),
222 value(Prefix::OneButBinary, tag("i")),
223 value(Prefix::Kilo, tag("K")),
224 value(Prefix::Mega, tag("M")),
225 value(Prefix::Giga, tag("G")),
226 value(Prefix::Tera, tag("T")),
227 ))),
228 |p| p.unwrap_or_default(),
229 )
230 .parse(input)
231}
232
233fn parse_value_prefix(input: &str) -> IResult<&str, ValuePrefix> {
234 all_consuming(map((double, parse_prefix), |(v, p)| ValuePrefix(v, p))).parse(input)
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn eng() {
243 assert_eq!(Prefix::eng(0.000_000_000_1), Prefix::Nano);
244 assert_eq!(Prefix::eng(0.000_000_001), Prefix::Nano);
245 assert_eq!(Prefix::eng(0.000_000_01), Prefix::Nano);
246 assert_eq!(Prefix::eng(0.000_000_1), Prefix::Nano);
247 assert_eq!(Prefix::eng(0.000_001), Prefix::Micro);
248 assert_eq!(Prefix::eng(0.000_01), Prefix::Micro);
249 assert_eq!(Prefix::eng(0.000_1), Prefix::Micro);
250 assert_eq!(Prefix::eng(0.001), Prefix::Milli);
251 assert_eq!(Prefix::eng(0.01), Prefix::Milli);
252 assert_eq!(Prefix::eng(0.1), Prefix::Milli);
253 assert_eq!(Prefix::eng(1.0), Prefix::One);
254 assert_eq!(Prefix::eng(10.0), Prefix::One);
255 assert_eq!(Prefix::eng(100.0), Prefix::One);
256 assert_eq!(Prefix::eng(1_000.0), Prefix::Kilo);
257 assert_eq!(Prefix::eng(10_000.0), Prefix::Kilo);
258 assert_eq!(Prefix::eng(100_000.0), Prefix::Kilo);
259 assert_eq!(Prefix::eng(1_000_000.0), Prefix::Mega);
260 assert_eq!(Prefix::eng(10_000_000.0), Prefix::Mega);
261 assert_eq!(Prefix::eng(100_000_000.0), Prefix::Mega);
262 assert_eq!(Prefix::eng(1_000_000_000.0), Prefix::Giga);
263 assert_eq!(Prefix::eng(10_000_000_000.0), Prefix::Giga);
264 assert_eq!(Prefix::eng(100_000_000_000.0), Prefix::Giga);
265 assert_eq!(Prefix::eng(1_000_000_000_000.0), Prefix::Tera);
266 assert_eq!(Prefix::eng(10_000_000_000_000.0), Prefix::Tera);
267 assert_eq!(Prefix::eng(100_000_000_000_000.0), Prefix::Tera);
268 assert_eq!(Prefix::eng(1_000_000_000_000_000.0), Prefix::Tera);
269 }
270
271 #[test]
272 fn eng_round() {
273 assert_eq!(Prefix::eng(0.000_000_000_09), Prefix::Nano);
274 assert_eq!(Prefix::eng(0.000_000_000_9), Prefix::Nano);
275 assert_eq!(Prefix::eng(0.000_000_009), Prefix::Nano);
276 assert_eq!(Prefix::eng(0.000_000_09), Prefix::Nano);
277 assert_eq!(Prefix::eng(0.000_000_9), Prefix::Micro);
278 assert_eq!(Prefix::eng(0.000_009), Prefix::Micro);
279 assert_eq!(Prefix::eng(0.000_09), Prefix::Micro);
280 assert_eq!(Prefix::eng(0.000_9), Prefix::Milli);
281 assert_eq!(Prefix::eng(0.009), Prefix::Milli);
282 assert_eq!(Prefix::eng(0.09), Prefix::Milli);
283 assert_eq!(Prefix::eng(0.9), Prefix::One);
284 assert_eq!(Prefix::eng(9.9), Prefix::One);
285 assert_eq!(Prefix::eng(99.9), Prefix::One);
286 assert_eq!(Prefix::eng(999.9), Prefix::Kilo);
287 assert_eq!(Prefix::eng(9_999.9), Prefix::Kilo);
288 assert_eq!(Prefix::eng(99_999.9), Prefix::Kilo);
289 assert_eq!(Prefix::eng(999_999.9), Prefix::Mega);
290 assert_eq!(Prefix::eng(9_999_999.9), Prefix::Mega);
291 assert_eq!(Prefix::eng(99_999_999.9), Prefix::Mega);
292 assert_eq!(Prefix::eng(999_999_999.9), Prefix::Giga);
293 assert_eq!(Prefix::eng(9_999_999_999.9), Prefix::Giga);
294 assert_eq!(Prefix::eng(99_999_999_999.9), Prefix::Giga);
295 assert_eq!(Prefix::eng(999_999_999_999.9), Prefix::Tera);
296 assert_eq!(Prefix::eng(9_999_999_999_999.9), Prefix::Tera);
297 assert_eq!(Prefix::eng(99_999_999_999_999.9), Prefix::Tera);
298 assert_eq!(Prefix::eng(999_999_999_999_999.9), Prefix::Tera);
299 }
300
301 #[test]
302 fn eng_binary() {
303 assert_eq!(Prefix::eng_binary(0.1), Prefix::OneButBinary);
304 assert_eq!(Prefix::eng_binary(1.0), Prefix::OneButBinary);
305 assert_eq!(Prefix::eng_binary((1 << 9) as f64), Prefix::OneButBinary);
306 assert_eq!(Prefix::eng_binary((1 << 10) as f64), Prefix::Kibi);
307 assert_eq!(Prefix::eng_binary((1 << 19) as f64), Prefix::Kibi);
308 assert_eq!(Prefix::eng_binary((1 << 29) as f64), Prefix::Mebi);
309 assert_eq!(Prefix::eng_binary((1 << 20) as f64), Prefix::Mebi);
310 assert_eq!(Prefix::eng_binary((1 << 30) as f64), Prefix::Gibi);
311 assert_eq!(Prefix::eng_binary((1_u64 << 39) as f64), Prefix::Gibi);
312 assert_eq!(Prefix::eng_binary((1_u64 << 40) as f64), Prefix::Tebi);
313 assert_eq!(Prefix::eng_binary((1_u64 << 49) as f64), Prefix::Tebi);
314 assert_eq!(Prefix::eng_binary((1_u64 << 50) as f64), Prefix::Tebi);
315 }
316
317 #[test]
318 fn eng_binary_round() {
319 assert_eq!(Prefix::eng_binary(0.9), Prefix::OneButBinary);
320 assert_eq!(
321 Prefix::eng_binary((1 << 9) as f64 - 0.1),
322 Prefix::OneButBinary
323 );
324 assert_eq!(Prefix::eng_binary((1 << 10) as f64 - 0.1), Prefix::Kibi);
325 assert_eq!(Prefix::eng_binary((1 << 19) as f64 - 0.1), Prefix::Kibi);
326 assert_eq!(Prefix::eng_binary((1 << 29) as f64 - 0.1), Prefix::Mebi);
327 assert_eq!(Prefix::eng_binary((1 << 20) as f64 - 0.1), Prefix::Mebi);
328 assert_eq!(Prefix::eng_binary((1 << 30) as f64 - 0.1), Prefix::Gibi);
329 assert_eq!(Prefix::eng_binary((1_u64 << 39) as f64 - 0.1), Prefix::Gibi);
330 assert_eq!(Prefix::eng_binary((1_u64 << 40) as f64 - 0.1), Prefix::Tebi);
331 assert_eq!(Prefix::eng_binary((1_u64 << 49) as f64 - 0.1), Prefix::Tebi);
332 assert_eq!(Prefix::eng_binary((1_u64 << 50) as f64 - 0.1), Prefix::Tebi);
333 }
334
335 #[test]
336 fn value_prefix() -> Result<()> {
337 assert_eq!(ValuePrefix::from_str("1")?.result(), 1.0);
338 assert_eq!(ValuePrefix::from_str("1G")?.result(), 1e9);
339 assert_eq!(ValuePrefix::from_str("1e9")?.result(), 1e9);
340 assert_eq!(ValuePrefix::from_str("10e9")?.result(), 10e9);
341 assert_eq!(ValuePrefix::from_str("10Gi")?.result(), 10737418240.0);
342 assert_eq!(ValuePrefix::from_str("10M")?.result(), 1e7);
343
344 Ok(())
345 }
346}