etop_format/number_format/
str_convert.rs1use super::types::{
2 FormatType, NumberAlign, NumberFormat, Sign, DEFAULT_PRECISION, DEFAULT_TIMEZONE,
3};
4use crate::FormatError;
5use regex::{Captures, Regex};
6use std::{fmt, str::FromStr};
7
8impl fmt::Display for FormatError {
9 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
10 let s = format!("{:?}", self);
20 write!(f, "{}", s)
21 }
22}
23
24impl FromStr for NumberAlign {
25 type Err = FormatError;
26
27 fn from_str(s: &str) -> Result<Self, Self::Err> {
28 match s {
29 ">" => Ok(NumberAlign::Right),
30 "<" => Ok(NumberAlign::Left),
31 "^" => Ok(NumberAlign::Center),
32 "=" => Ok(NumberAlign::SignedRight),
33 _ => Err(FormatError::CouldNotParseFormatType),
34 }
35 }
36}
37
38impl FromStr for FormatType {
39 type Err = FormatError;
40
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 match s {
43 "e" => Ok(FormatType::Exponent),
44 "E" => Ok(FormatType::ExponentUppercase),
45 "f" => Ok(FormatType::FixedPoint),
46 "s" => Ok(FormatType::SI),
47 "%" => Ok(FormatType::Percentage),
48 "b" => Ok(FormatType::Binary),
49 "o" => Ok(FormatType::Octal),
50 "O" => Ok(FormatType::OctalUppercase),
51 "d" => Ok(FormatType::Decimal),
52 "x" => Ok(FormatType::Hex),
53 "X" => Ok(FormatType::HexUppercase),
54 _ => Err(FormatError::CouldNotParseFormatType),
55 }
56 }
57}
58
59impl TryFrom<&str> for NumberFormat {
60 type Error = FormatError;
61
62 fn try_from(pattern: &str) -> Result<NumberFormat, FormatError> {
63 let re =
64 Regex::new(r"^(?:(.)?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([A-Za-z%])?$")
65 .map_err(|_| FormatError::CouldNotCreateRegex)?;
66 let captures = re.captures(pattern).ok_or(FormatError::CouldNotMatchRegex)?;
67 Ok(NumberFormat::from(captures))
68 }
69}
70
71impl From<Captures<'_>> for NumberFormat {
72 fn from(c: Captures<'_>) -> Self {
74 let fill = c.get(1).and_then(|m| m.as_str().chars().next()).unwrap_or(' ');
75 let align = c
76 .get(2)
77 .map(|s| s.as_str().parse().unwrap_or(NumberAlign::Right))
78 .unwrap_or(NumberAlign::Right);
79 let sign = match c.get(3).map(|m| m.as_str()) {
80 Some("-") => Sign::OnlyNegative,
81 Some("+") => Sign::Always,
82 Some(" ") => Sign::SpaceOrDash,
83 _ => Sign::OnlyNegative,
84 };
85 let type_prefix = matches!(c.get(4).map(|m| m.as_str()), Some("#"));
86 let zero_padding = c.get(5).is_some();
87 let min_width = c.get(6).map(|m| m.as_str().parse().unwrap_or(0)).unwrap_or(0);
88 let commas = matches!(c.get(7).map(|m| m.as_str()), Some(","));
89 let precision = c
90 .get(8)
91 .map(|m| m.as_str().get(1..).unwrap_or_default().parse().unwrap_or(DEFAULT_PRECISION))
92 .unwrap_or(DEFAULT_PRECISION);
93 let format_type: FormatType =
94 c.get(9).and_then(|s| s.as_str().parse().ok()).unwrap_or(FormatType::None);
95
96 let timezone = DEFAULT_TIMEZONE;
97
98 let max_width = usize::MAX;
99 let mut spec = Self {
100 fill,
101 align,
102 sign,
103 type_prefix,
104 zero_padding,
105 min_width,
106 max_width,
107 commas,
108 precision,
109 format_type,
110 timezone,
111 };
112
113 if spec.zero_padding || (spec.fill == '0' && spec.align == NumberAlign::SignedRight) {
115 spec.zero_padding = true;
116 spec.fill = '0';
117 spec.align = NumberAlign::SignedRight;
118 }
119
120 if spec.format_type == FormatType::Decimal {
122 spec.precision = 0;
123 };
124
125 spec
126 }
127}