1use alloc::string::String;
5use alloc::vec::Vec;
6
7use super::{FileValue, FromField, ValueError};
8
9fn set<T>(slot: &mut Option<T>, value: T) -> Result<(), ValueError> {
11 if slot.is_some() {
12 return Err(ValueError::Duplicate);
13 }
14 *slot = Some(value);
15 Ok(())
16}
17
18impl FromField for String {
19 type Slot = Option<String>;
20
21 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
22 set(slot, String::from(value))
23 }
24
25 fn finish(slot: Self::Slot) -> Result<Self, ValueError> {
26 slot.ok_or(ValueError::Missing)
27 }
28}
29
30fn digits(s: &str) -> bool {
32 !s.is_empty() && s.bytes().all(|b| b.is_ascii_digit())
33}
34
35macro_rules! unsigned {
36 ($($t:ty),*) => {$(
37 impl FromField for $t {
38 type Slot = Option<$t>;
39
40 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
41 if !digits(value) {
42 return Err(ValueError::Invalid("not a whole number"));
43 }
44 let n = value.parse::<$t>().map_err(|_| ValueError::Invalid("out of range"))?;
45 set(slot, n)
46 }
47
48 fn finish(slot: Self::Slot) -> Result<Self, ValueError> {
49 slot.ok_or(ValueError::Missing)
50 }
51 }
52 )*};
53}
54
55macro_rules! signed {
56 ($($t:ty),*) => {$(
57 impl FromField for $t {
58 type Slot = Option<$t>;
59
60 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
61 if !digits(value.strip_prefix('-').unwrap_or(value)) {
62 return Err(ValueError::Invalid("not a whole number"));
63 }
64 let n = value.parse::<$t>().map_err(|_| ValueError::Invalid("out of range"))?;
65 set(slot, n)
66 }
67
68 fn finish(slot: Self::Slot) -> Result<Self, ValueError> {
69 slot.ok_or(ValueError::Missing)
70 }
71 }
72 )*};
73}
74
75unsigned!(u8, u16, u32, u64, u128, usize);
76signed!(i8, i16, i32, i64, i128, isize);
77
78macro_rules! float {
79 ($($t:ty),*) => {$(
80 impl FromField for $t {
81 type Slot = Option<$t>;
82
83 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
84 let n = value.parse::<$t>().map_err(|_| ValueError::Invalid("not a number"))?;
85 if !n.is_finite() {
86 return Err(ValueError::Invalid("not a finite number"));
87 }
88 set(slot, n)
89 }
90
91 fn finish(slot: Self::Slot) -> Result<Self, ValueError> {
92 slot.ok_or(ValueError::Missing)
93 }
94 }
95 )*};
96}
97
98float!(f32, f64);
99
100impl FromField for bool {
101 type Slot = Option<bool>;
102
103 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
104 let b = match value {
105 "on" | "true" | "1" => true,
106 "off" | "false" | "0" => false,
107 _ => return Err(ValueError::Invalid("not on, true, 1, off, false, or 0")),
108 };
109 set(slot, b)
110 }
111
112 fn finish(slot: Self::Slot) -> Result<Self, ValueError> {
114 Ok(slot.unwrap_or(false))
115 }
116}
117
118impl FromField for char {
119 type Slot = Option<char>;
120
121 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
122 let mut chars = value.chars();
123 match (chars.next(), chars.next()) {
124 (Some(c), None) => set(slot, c),
125 _ => Err(ValueError::Invalid("not exactly one character")),
126 }
127 }
128
129 fn finish(slot: Self::Slot) -> Result<Self, ValueError> {
130 slot.ok_or(ValueError::Missing)
131 }
132}
133
134impl<T: FromField> FromField for Option<T> {
135 type Slot = (bool, T::Slot);
136
137 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
138 slot.0 = true;
139 T::accept_text(&mut slot.1, value)
140 }
141
142 fn accept_file(slot: &mut Self::Slot, file: FileValue<'_>) -> Result<(), ValueError> {
143 slot.0 = true;
144 T::accept_file(&mut slot.1, file)
145 }
146
147 fn finish(slot: Self::Slot) -> Result<Self, ValueError> {
148 if !slot.0 {
149 return Ok(None);
150 }
151 match T::finish(slot.1) {
152 Ok(v) => Ok(Some(v)),
153 Err(ValueError::Missing) => Ok(None),
155 Err(e) => Err(e),
156 }
157 }
158}
159
160impl<T: FromField> FromField for Vec<T> {
161 type Slot = Vec<T>;
162
163 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
164 let mut one = T::Slot::default();
165 T::accept_text(&mut one, value)?;
166 slot.push(T::finish(one)?);
167 Ok(())
168 }
169
170 fn accept_file(slot: &mut Self::Slot, file: FileValue<'_>) -> Result<(), ValueError> {
171 let mut one = T::Slot::default();
172 T::accept_file(&mut one, file)?;
173 match T::finish(one) {
174 Ok(v) => slot.push(v),
175 Err(ValueError::Missing) => {}
176 Err(e) => return Err(e),
177 }
178 Ok(())
179 }
180
181 fn finish(slot: Self::Slot) -> Result<Self, ValueError> {
182 Ok(slot)
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 fn one<T: FromField>(values: &[&str]) -> Result<T, ValueError> {
191 let mut slot = T::Slot::default();
192 for v in values {
193 T::accept_text(&mut slot, v)?;
194 }
195 T::finish(slot)
196 }
197
198 #[test]
199 fn test_integers_strict() {
200 assert_eq!(one::<u8>(&["255"]), Ok(255));
201 assert_eq!(one::<u8>(&["007"]), Ok(7));
202 assert_eq!(
203 one::<u8>(&["256"]),
204 Err(ValueError::Invalid("out of range"))
205 );
206 for bad in ["+1", " 1", "1 ", "", "-0", "1.0", "0x1"] {
207 assert!(one::<u8>(&[bad]).is_err(), "{bad:?}");
208 }
209 assert_eq!(one::<i8>(&["-128"]), Ok(-128));
210 assert_eq!(
211 one::<i8>(&["-129"]),
212 Err(ValueError::Invalid("out of range"))
213 );
214 assert!(
215 one::<i8>(&["+1"]).is_err()
216 && one::<i8>(&["-"]).is_err()
217 && one::<i8>(&["--1"]).is_err()
218 );
219 assert_eq!(one::<u64>(&[]), Err(ValueError::Missing));
220 assert_eq!(one::<u64>(&["1", "2"]), Err(ValueError::Duplicate));
221 assert_eq!(
222 one::<u128>(&["340282366920938463463374607431768211455"]),
223 Ok(u128::MAX)
224 );
225 }
226
227 #[test]
228 fn test_floats_refuse_non_finite() {
229 assert_eq!(one::<f64>(&["1e308"]), Ok(1e308));
230 assert_eq!(one::<f64>(&["-0"]), Ok(-0.0));
231 for bad in ["1e309", "NaN", "inf", "-inf", "infinity", "", "abc"] {
232 assert!(one::<f64>(&[bad]).is_err(), "{bad:?}");
233 }
234 }
235
236 #[test]
237 fn test_bool_spellings_and_missing() {
238 for (s, v) in [
239 ("on", true),
240 ("true", true),
241 ("1", true),
242 ("off", false),
243 ("false", false),
244 ("0", false),
245 ] {
246 assert_eq!(one::<bool>(&[s]), Ok(v));
247 }
248 assert!(one::<bool>(&["ON"]).is_err() && one::<bool>(&["yes"]).is_err());
249 assert_eq!(one::<bool>(&[]), Ok(false));
250 assert_eq!(one::<bool>(&["on", "on"]), Err(ValueError::Duplicate));
251 }
252
253 #[test]
254 fn test_char_option_vec() {
255 assert_eq!(one::<char>(&["é"]), Ok('é'));
256 assert!(one::<char>(&[""]).is_err() && one::<char>(&["ab"]).is_err());
257 assert_eq!(one::<Option<u8>>(&[]), Ok(None));
258 assert_eq!(one::<Option<u8>>(&["3"]), Ok(Some(3)));
259 assert_eq!(one::<Option<u8>>(&["3", "4"]), Err(ValueError::Duplicate));
260 assert_eq!(one::<Vec<u8>>(&[]), Ok(Vec::new()));
261 assert_eq!(one::<Vec<u8>>(&["1", "2", "3"]), Ok(alloc::vec![1, 2, 3]));
262 assert!(one::<Vec<u8>>(&["1", "x"]).is_err());
263 assert_eq!(one::<String>(&["a"]), Ok(String::from("a")));
264 }
265
266 #[test]
267 fn test_text_into_file_field_is_wrong_kind() {
268 let mut slot = <super::super::FileData as FromField>::Slot::default();
269 assert_eq!(
270 <super::super::FileData as FromField>::accept_text(&mut slot, "x"),
271 Err(ValueError::ExpectedFile)
272 );
273 }
274}