structform/
text_input.rs

1/// Implements `ParseAndFormat<$type> for $text_input<$type>`, and also
2/// implements `ParseAndFormat<Option<$type>>> for $text_input<Option<$type>>`.
3///
4/// This will parse by trimming the string input and then calling
5/// `str::parse`. If the input string is empty after trimming, then
6/// parse will return a `ParseError::Required` for the
7/// `ParseAndFormat<$type>` case, and return `None` for the
8/// `ParseAndFormat<Option<$type>>` case.
9///
10/// Formatting is done using `std::string::ToString`.
11#[macro_export]
12macro_rules! impl_text_input_with_stringops {
13    ($text_input: ident, $type_name: literal, $type: ty) => {
14        impl_text_input_with_stringops!(
15            $text_input,
16            |_e| structform::ParseError::InvalidFormat {
17                required_type: $type_name.to_string()
18            },
19            $type
20        );
21    };
22    ($text_input: ident, $type: ty) => {
23        impl_text_input_with_stringops!(
24            $text_input,
25            |e| structform::ParseError::FromStrError(e.to_string()),
26            $type
27        );
28    };
29    ($text_input: ident, $handle_error: expr, $type: ty) => {
30        impl structform::ParseAndFormat<$type> for $text_input<$type> {
31            fn parse(value: &str) -> Result<$type, structform::ParseError> {
32                let trimmed = value.trim();
33                if trimmed.is_empty() {
34                    Err(structform::ParseError::Required)
35                } else {
36                    trimmed.parse::<$type>().map_err($handle_error)
37                }
38            }
39
40            fn format(value: &$type) -> String {
41                value.to_string()
42            }
43        }
44
45        impl structform::ParseAndFormat<Option<$type>> for $text_input<Option<$type>> {
46            fn parse(value: &str) -> Result<Option<$type>, structform::ParseError> {
47                let trimmed = value.trim();
48                if trimmed.is_empty() {
49                    Ok(None)
50                } else {
51                    trimmed
52                        .parse::<$type>()
53                        .map(Option::Some)
54                        .map_err(|e| structform::ParseError::FromStrError(e.to_string()))
55                }
56            }
57
58            fn format(value: &Option<$type>) -> String {
59                match value {
60                    None => "".to_string(),
61                    Some(inner) => inner.to_string(),
62                }
63            }
64        }
65    };
66}
67
68/// Implements `ParseAndFormat<Vec<$type>> for $text_input<Vec<$type>>`.
69///
70/// This will parse by splitting the string on commas, and
71/// individually parsing each split using `str::parse`. Empty strings
72/// will result in an empty `Vec`.
73///
74/// Formatting is done using `std::string::ToString` on each element
75/// of the `Vec` and then joining them with a comma.
76///
77/// Note: This is not a good idea of your value might contain commas.
78#[macro_export]
79macro_rules! impl_vec_text_input_with_stringops {
80    ($text_input: ident, $type_name: literal, $type: ty) => {
81        impl_vec_text_input_with_stringops!(
82            $text_input,
83            |_e| structform::ParseError::InvalidFormat {
84                required_type: $type_name.to_string()
85            },
86            $type
87        );
88    };
89    ($text_input: ident, $type: ty) => {
90        impl_vec_text_input_with_stringops!(
91            $text_input,
92            |e| structform::ParseError::FromStrError(e.to_string()),
93            $type
94        );
95    };
96    ($text_input: ident, $handle_error: expr, $type: ty) => {
97        impl structform::ParseAndFormat<Vec<$type>> for $text_input<Vec<$type>> {
98            fn parse(value: &str) -> Result<Vec<$type>, structform::ParseError> {
99                value
100                    .trim()
101                    .split(',')
102                    .map(|s| s.trim())
103                    .filter(|s| !s.is_empty())
104                    .map(|trimmed| trimmed.parse::<$type>().map_err($handle_error))
105                    .collect()
106            }
107
108            fn format(value: &Vec<$type>) -> String {
109                value
110                    .iter()
111                    .map(|value| value.to_string())
112                    .collect::<Vec<_>>()
113                    .join(", ")
114            }
115        }
116    };
117}