1use std::{error::Error, fmt, str};
2
3use super::{NumberPrefix, Prefix};
4
5
6impl<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)]
54pub struct NumberPrefixParseError(());
55
56impl 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
62impl Error for NumberPrefixParseError {
63}
64
65
66#[cfg(test)]
67mod 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