1 | use std::{error::Error, fmt, str}; |
2 | |
3 | use super::{NumberPrefix, Prefix}; |
4 | |
5 | |
6 | impl<T: str::FromStr> str::FromStr for NumberPrefix<T> { |
7 | type Err = NumberPrefixParseError; |
8 | |
9 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
10 | let splitted = s.find(|p| { |
11 | p == 'k' || p == 'K' || p == 'M' || p == 'G' || p == 'T' || |
12 | p == 'P' || p == 'E' || p == 'Z' || p == 'Y' |
13 | }); |
14 | |
15 | let num_prefix = s.split_at(splitted.unwrap_or(s.len())); |
16 | let num = match num_prefix.0.trim().parse::<T>() { |
17 | Ok(n) => n, |
18 | Err(_) => return Err(NumberPrefixParseError(())), |
19 | }; |
20 | |
21 | let prefix_unit = num_prefix.1.trim_matches(|p| |
22 | p == 'b' || p == 'B' || p == 'm' |
23 | ); |
24 | |
25 | let prefix = match prefix_unit { |
26 | "k" | |
27 | "K" => Prefix::Kilo, |
28 | "M" => Prefix::Mega, |
29 | "G" => Prefix::Giga, |
30 | "T" => Prefix::Tera, |
31 | "P" => Prefix::Peta, |
32 | "E" => Prefix::Exa, |
33 | "Z" => Prefix::Zetta, |
34 | "Y" => Prefix::Yotta, |
35 | "Ki" => Prefix::Kibi, |
36 | "Mi" => Prefix::Mebi, |
37 | "Gi" => Prefix::Gibi, |
38 | "Ti" => Prefix::Tebi, |
39 | "Pi" => Prefix::Pebi, |
40 | "Ei" => Prefix::Exbi, |
41 | "Zi" => Prefix::Zebi, |
42 | "Yi" => Prefix::Yobi, |
43 | "" => return Ok(NumberPrefix::Standalone(num)), |
44 | _ => return Err(NumberPrefixParseError(())), |
45 | }; |
46 | |
47 | Ok(NumberPrefix::Prefixed(prefix, num)) |
48 | } |
49 | } |
50 | |
51 | |
52 | /// The error returned when a `NumberPrefix` is failed to be parsed. |
53 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
54 | pub struct NumberPrefixParseError(()); |
55 | |
56 | impl fmt::Display for NumberPrefixParseError { |
57 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
58 | fmt.write_str(data:"invalid prefix syntax" ) |
59 | } |
60 | } |
61 | |
62 | impl Error for NumberPrefixParseError { |
63 | } |
64 | |
65 | |
66 | #[cfg (test)] |
67 | mod test { |
68 | use super::*; |
69 | |
70 | #[test ] |
71 | fn parse_examples() { |
72 | let parse_example_a = "7.05E" .parse::<NumberPrefix<f64>>(); |
73 | let parse_example_b = "7.05" .parse::<NumberPrefix<f64>>(); |
74 | let parse_example_c = "7.05 GiB" .parse::<NumberPrefix<f64>>(); |
75 | |
76 | assert_eq!(parse_example_a, Ok(NumberPrefix::Prefixed(Prefix::Exa, 7.05_f64))); |
77 | assert_eq!(parse_example_b, Ok(NumberPrefix::Standalone(7.05_f64))); |
78 | assert_eq!(parse_example_c, Ok(NumberPrefix::Prefixed(Prefix::Gibi, 7.05_f64))); |
79 | } |
80 | |
81 | #[test ] |
82 | fn bad_parse() { |
83 | let parsed = "bogo meters per second" .parse::<NumberPrefix<f64>>(); |
84 | |
85 | assert_ne!(parsed, Ok(NumberPrefix::Prefixed(Prefix::Kilo, 7.05_f64))); |
86 | } |
87 | } |
88 | |