| 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 | |