1 | #![allow (clippy::too_many_lines)] |
2 | |
3 | use serde::{de, Deserialize}; |
4 | use std::fmt; |
5 | |
6 | macro_rules! bad { |
7 | ($toml:expr, $ty:ty, $msg:expr) => { |
8 | match basic_toml::from_str::<$ty>($toml) { |
9 | Ok(s) => panic!("parsed to: {:#?}" , s), |
10 | Err(e) => assert_eq!(e.to_string(), $msg), |
11 | } |
12 | }; |
13 | } |
14 | |
15 | #[derive(Debug, Deserialize, PartialEq)] |
16 | struct Parent<T> { |
17 | p_a: T, |
18 | p_b: Vec<Child<T>>, |
19 | } |
20 | |
21 | #[derive(Debug, Deserialize, PartialEq)] |
22 | #[serde(deny_unknown_fields)] |
23 | struct Child<T> { |
24 | c_a: T, |
25 | c_b: T, |
26 | } |
27 | |
28 | #[derive(Debug, PartialEq)] |
29 | enum CasedString { |
30 | Lowercase(String), |
31 | Uppercase(String), |
32 | } |
33 | |
34 | impl<'de> de::Deserialize<'de> for CasedString { |
35 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
36 | where |
37 | D: de::Deserializer<'de>, |
38 | { |
39 | struct CasedStringVisitor; |
40 | |
41 | impl<'de> de::Visitor<'de> for CasedStringVisitor { |
42 | type Value = CasedString; |
43 | |
44 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
45 | formatter.write_str("a string" ) |
46 | } |
47 | |
48 | fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> |
49 | where |
50 | E: de::Error, |
51 | { |
52 | if s.is_empty() { |
53 | Err(de::Error::invalid_length(0, &"a non-empty string" )) |
54 | } else if s.chars().all(|x| x.is_ascii_lowercase()) { |
55 | Ok(CasedString::Lowercase(s.to_string())) |
56 | } else if s.chars().all(|x| x.is_ascii_uppercase()) { |
57 | Ok(CasedString::Uppercase(s.to_string())) |
58 | } else { |
59 | Err(de::Error::invalid_value( |
60 | de::Unexpected::Str(s), |
61 | &"all lowercase or all uppercase" , |
62 | )) |
63 | } |
64 | } |
65 | } |
66 | |
67 | deserializer.deserialize_any(CasedStringVisitor) |
68 | } |
69 | } |
70 | |
71 | #[test] |
72 | fn custom_errors() { |
73 | basic_toml::from_str::<Parent<CasedString>>( |
74 | " |
75 | p_a = 'a' |
76 | p_b = [{c_a = 'a', c_b = 'c'}] |
77 | " , |
78 | ) |
79 | .unwrap(); |
80 | |
81 | // Custom error at p_b value. |
82 | bad!( |
83 | " |
84 | p_a = '' |
85 | # ^ |
86 | " , |
87 | Parent<CasedString>, |
88 | "invalid length 0, expected a non-empty string for key `p_a` at line 2 column 19" |
89 | ); |
90 | |
91 | // Missing field in table. |
92 | bad!( |
93 | " |
94 | p_a = 'a' |
95 | # ^ |
96 | " , |
97 | Parent<CasedString>, |
98 | "missing field `p_b` at line 1 column 1" |
99 | ); |
100 | |
101 | // Invalid type in p_b. |
102 | bad!( |
103 | " |
104 | p_a = 'a' |
105 | p_b = 1 |
106 | # ^ |
107 | " , |
108 | Parent<CasedString>, |
109 | "invalid type: integer `1`, expected a sequence for key `p_b` at line 3 column 19" |
110 | ); |
111 | |
112 | // Sub-table in Vec is missing a field. |
113 | bad!( |
114 | " |
115 | p_a = 'a' |
116 | p_b = [ |
117 | {c_a = 'a'} |
118 | # ^ |
119 | ] |
120 | " , |
121 | Parent<CasedString>, |
122 | "missing field `c_b` for key `p_b` at line 4 column 17" |
123 | ); |
124 | |
125 | // Sub-table in Vec has a field with a bad value. |
126 | bad!( |
127 | " |
128 | p_a = 'a' |
129 | p_b = [ |
130 | {c_a = 'a', c_b = '*'} |
131 | # ^ |
132 | ] |
133 | " , |
134 | Parent<CasedString>, |
135 | "invalid value: string \"* \", expected all lowercase or all uppercase for key `p_b` at line 4 column 35" |
136 | ); |
137 | |
138 | // Sub-table in Vec is missing a field. |
139 | bad!( |
140 | " |
141 | p_a = 'a' |
142 | p_b = [ |
143 | {c_a = 'a', c_b = 'b'}, |
144 | {c_a = 'aa'} |
145 | # ^ |
146 | ] |
147 | " , |
148 | Parent<CasedString>, |
149 | "missing field `c_b` for key `p_b` at line 5 column 17" |
150 | ); |
151 | |
152 | // Sub-table in the middle of a Vec is missing a field. |
153 | bad!( |
154 | " |
155 | p_a = 'a' |
156 | p_b = [ |
157 | {c_a = 'a', c_b = 'b'}, |
158 | {c_a = 'aa'}, |
159 | # ^ |
160 | {c_a = 'aaa', c_b = 'bbb'}, |
161 | ] |
162 | " , |
163 | Parent<CasedString>, |
164 | "missing field `c_b` for key `p_b` at line 5 column 17" |
165 | ); |
166 | |
167 | // Sub-table in the middle of a Vec has a field with a bad value. |
168 | bad!( |
169 | " |
170 | p_a = 'a' |
171 | p_b = [ |
172 | {c_a = 'a', c_b = 'b'}, |
173 | {c_a = 'aa', c_b = 1}, |
174 | # ^ |
175 | {c_a = 'aaa', c_b = 'bbb'}, |
176 | ] |
177 | " , |
178 | Parent<CasedString>, |
179 | "invalid type: integer `1`, expected a string for key `p_b` at line 5 column 36" |
180 | ); |
181 | |
182 | // Sub-table in the middle of a Vec has an extra field. |
183 | // FIXME: This location could be better. |
184 | bad!( |
185 | " |
186 | p_a = 'a' |
187 | p_b = [ |
188 | {c_a = 'a', c_b = 'b'}, |
189 | {c_a = 'aa', c_b = 'bb', c_d = 'd'}, |
190 | # ^ |
191 | {c_a = 'aaa', c_b = 'bbb'}, |
192 | {c_a = 'aaaa', c_b = 'bbbb'}, |
193 | ] |
194 | " , |
195 | Parent<CasedString>, |
196 | "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 5 column 17" |
197 | ); |
198 | |
199 | // Sub-table in the middle of a Vec is missing a field. |
200 | // FIXME: This location is pretty off. |
201 | bad!( |
202 | " |
203 | p_a = 'a' |
204 | [[p_b]] |
205 | c_a = 'a' |
206 | c_b = 'b' |
207 | [[p_b]] |
208 | c_a = 'aa' |
209 | # c_b = 'bb' # <- missing field |
210 | [[p_b]] |
211 | c_a = 'aaa' |
212 | c_b = 'bbb' |
213 | [[p_b]] |
214 | # ^ |
215 | c_a = 'aaaa' |
216 | c_b = 'bbbb' |
217 | " , |
218 | Parent<CasedString>, |
219 | "missing field `c_b` for key `p_b` at line 12 column 13" |
220 | ); |
221 | |
222 | // Sub-table in the middle of a Vec has a field with a bad value. |
223 | bad!( |
224 | " |
225 | p_a = 'a' |
226 | [[p_b]] |
227 | c_a = 'a' |
228 | c_b = 'b' |
229 | [[p_b]] |
230 | c_a = 'aa' |
231 | c_b = '*' |
232 | # ^ |
233 | [[p_b]] |
234 | c_a = 'aaa' |
235 | c_b = 'bbb' |
236 | " , |
237 | Parent<CasedString>, |
238 | "invalid value: string \"* \", expected all lowercase or all uppercase for key `p_b.c_b` at line 8 column 19" |
239 | ); |
240 | |
241 | // Sub-table in the middle of a Vec has an extra field. |
242 | // FIXME: This location is pretty off. |
243 | bad!( |
244 | " |
245 | p_a = 'a' |
246 | [[p_b]] |
247 | c_a = 'a' |
248 | c_b = 'b' |
249 | [[p_b]] |
250 | c_a = 'aa' |
251 | c_d = 'dd' # unknown field |
252 | [[p_b]] |
253 | c_a = 'aaa' |
254 | c_b = 'bbb' |
255 | [[p_b]] |
256 | # ^ |
257 | c_a = 'aaaa' |
258 | c_b = 'bbbb' |
259 | " , |
260 | Parent<CasedString>, |
261 | "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 12 column 13" |
262 | ); |
263 | } |
264 | |
265 | #[test] |
266 | fn serde_derive_deserialize_errors() { |
267 | bad!( |
268 | " |
269 | p_a = '' |
270 | # ^ |
271 | " , |
272 | Parent<String>, |
273 | "missing field `p_b` at line 1 column 1" |
274 | ); |
275 | |
276 | bad!( |
277 | " |
278 | p_a = '' |
279 | p_b = [ |
280 | {c_a = ''} |
281 | # ^ |
282 | ] |
283 | " , |
284 | Parent<String>, |
285 | "missing field `c_b` for key `p_b` at line 4 column 17" |
286 | ); |
287 | |
288 | bad!( |
289 | " |
290 | p_a = '' |
291 | p_b = [ |
292 | {c_a = '', c_b = 1} |
293 | # ^ |
294 | ] |
295 | " , |
296 | Parent<String>, |
297 | "invalid type: integer `1`, expected a string for key `p_b` at line 4 column 34" |
298 | ); |
299 | |
300 | // FIXME: This location could be better. |
301 | bad!( |
302 | " |
303 | p_a = '' |
304 | p_b = [ |
305 | {c_a = '', c_b = '', c_d = ''}, |
306 | # ^ |
307 | ] |
308 | " , |
309 | Parent<String>, |
310 | "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 4 column 17" |
311 | ); |
312 | |
313 | bad!( |
314 | " |
315 | p_a = 'a' |
316 | p_b = [ |
317 | {c_a = '', c_b = 1, c_d = ''}, |
318 | # ^ |
319 | ] |
320 | " , |
321 | Parent<String>, |
322 | "invalid type: integer `1`, expected a string for key `p_b` at line 4 column 34" |
323 | ); |
324 | } |
325 | |
326 | #[test] |
327 | fn error_handles_crlf() { |
328 | bad!( |
329 | " \r\n\ |
330 | [t1] \r\n\ |
331 | [t2] \r\n\ |
332 | a = 1 \r\n\ |
333 | . = 2 \r\n\ |
334 | " , |
335 | serde_json::Value, |
336 | "expected a table key, found a period at line 5 column 1" |
337 | ); |
338 | |
339 | // Should be the same as above. |
340 | bad!( |
341 | " \n\ |
342 | [t1] \n\ |
343 | [t2] \n\ |
344 | a = 1 \n\ |
345 | . = 2 \n\ |
346 | " , |
347 | serde_json::Value, |
348 | "expected a table key, found a period at line 5 column 1" |
349 | ); |
350 | } |
351 | |