]>
Commit | Line | Data |
---|---|---|
8bb4bdeb XL |
1 | extern crate serde; |
2 | extern crate toml; | |
3 | #[macro_use] | |
4 | extern crate serde_derive; | |
5 | ||
6 | use std::collections::{BTreeMap, HashSet}; | |
7 | use serde::{Deserialize, Deserializer}; | |
8 | ||
9 | use toml::Value; | |
10 | use toml::Value::{Table, Integer, Array, Float}; | |
11 | ||
12 | macro_rules! t { | |
13 | ($e:expr) => (match $e { | |
14 | Ok(t) => t, | |
15 | Err(e) => panic!("{} failed with {}", stringify!($e), e), | |
16 | }) | |
17 | } | |
18 | ||
19 | macro_rules! equivalent { | |
20 | ($literal:expr, $toml:expr,) => ({ | |
21 | let toml = $toml; | |
22 | let literal = $literal; | |
23 | ||
24 | // In/out of Value is equivalent | |
25 | println!("try_from"); | |
26 | assert_eq!(t!(Value::try_from(literal.clone())), toml); | |
27 | println!("try_into"); | |
28 | assert_eq!(literal, t!(toml.clone().try_into())); | |
29 | ||
30 | // Through a string equivalent | |
31 | println!("to_string(literal)"); | |
32 | assert_eq!(t!(toml::to_string(&literal)), toml.to_string()); | |
33 | println!("to_string(toml)"); | |
34 | assert_eq!(t!(toml::to_string(&toml)), toml.to_string()); | |
35 | println!("literal, from_str(toml)"); | |
36 | assert_eq!(literal, t!(toml::from_str(&toml.to_string()))); | |
37 | println!("toml, from_str(toml)"); | |
38 | assert_eq!(toml, t!(toml::from_str(&toml.to_string()))); | |
39 | }) | |
40 | } | |
41 | ||
42 | macro_rules! error { | |
43 | ($ty:ty, $toml:expr, $error:expr) => ({ | |
44 | println!("attempting parsing"); | |
45 | match toml::from_str::<$ty>(&$toml.to_string()) { | |
46 | Ok(_) => panic!("successful"), | |
47 | Err(e) => { | |
48 | assert!(e.to_string().contains($error), | |
49 | "bad error: {}", e); | |
50 | } | |
51 | } | |
52 | ||
53 | println!("attempting toml decoding"); | |
54 | match $toml.try_into::<$ty>() { | |
55 | Ok(_) => panic!("successful"), | |
56 | Err(e) => { | |
57 | assert!(e.to_string().contains($error), | |
58 | "bad error: {}", e); | |
59 | } | |
60 | } | |
61 | }) | |
62 | } | |
63 | ||
64 | macro_rules! decode( ($t:expr) => ({ | |
65 | t!($t.try_into()) | |
66 | }) ); | |
67 | ||
68 | macro_rules! map( ($($k:ident: $v:expr),*) => ({ | |
69 | let mut _m = BTreeMap::new(); | |
70 | $(_m.insert(stringify!($k).to_string(), $v);)* | |
71 | _m | |
72 | }) ); | |
73 | ||
74 | #[test] | |
75 | fn smoke() { | |
76 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
77 | struct Foo { a: isize } | |
78 | ||
79 | equivalent!( | |
80 | Foo { a: 2 }, | |
81 | Table(map! { a: Integer(2) }), | |
82 | ); | |
83 | } | |
84 | ||
85 | #[test] | |
86 | fn smoke_hyphen() { | |
87 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
88 | struct Foo { | |
89 | a_b: isize, | |
90 | } | |
91 | ||
92 | equivalent! { | |
93 | Foo { a_b: 2 }, | |
94 | Table(map! { a_b: Integer(2) }), | |
95 | } | |
96 | ||
97 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
98 | struct Foo2 { | |
99 | #[serde(rename = "a-b")] | |
100 | a_b: isize, | |
101 | } | |
102 | ||
103 | let mut m = BTreeMap::new(); | |
104 | m.insert("a-b".to_string(), Integer(2)); | |
105 | equivalent! { | |
106 | Foo2 { a_b: 2 }, | |
107 | Table(m), | |
108 | } | |
109 | } | |
110 | ||
111 | #[test] | |
112 | fn nested() { | |
113 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
114 | struct Foo { a: isize, b: Bar } | |
115 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
116 | struct Bar { a: String } | |
117 | ||
118 | equivalent! { | |
119 | Foo { a: 2, b: Bar { a: "test".to_string() } }, | |
120 | Table(map! { | |
121 | a: Integer(2), | |
122 | b: Table(map! { | |
123 | a: Value::String("test".to_string()) | |
124 | }) | |
125 | }), | |
126 | } | |
127 | } | |
128 | ||
129 | #[test] | |
130 | fn application_decode_error() { | |
131 | #[derive(PartialEq, Debug)] | |
132 | struct Range10(usize); | |
7cac9316 XL |
133 | impl<'de> Deserialize<'de> for Range10 { |
134 | fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Range10, D::Error> { | |
8bb4bdeb XL |
135 | let x: usize = try!(Deserialize::deserialize(d)); |
136 | if x > 10 { | |
137 | Err(serde::de::Error::custom("more than 10")) | |
138 | } else { | |
139 | Ok(Range10(x)) | |
140 | } | |
141 | } | |
142 | } | |
143 | let d_good = Integer(5); | |
144 | let d_bad1 = Value::String("not an isize".to_string()); | |
145 | let d_bad2 = Integer(11); | |
146 | ||
147 | assert_eq!(Range10(5), d_good.try_into().unwrap()); | |
148 | ||
149 | let err1: Result<Range10, _> = d_bad1.try_into(); | |
150 | assert!(err1.is_err()); | |
151 | let err2: Result<Range10, _> = d_bad2.try_into(); | |
152 | assert!(err2.is_err()); | |
153 | } | |
154 | ||
155 | #[test] | |
156 | fn array() { | |
157 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
158 | struct Foo { a: Vec<isize> } | |
159 | ||
160 | equivalent! { | |
161 | Foo { a: vec![1, 2, 3, 4] }, | |
162 | Table(map! { | |
163 | a: Array(vec![ | |
164 | Integer(1), | |
165 | Integer(2), | |
166 | Integer(3), | |
167 | Integer(4) | |
168 | ]) | |
169 | }), | |
170 | }; | |
171 | } | |
172 | ||
173 | #[test] | |
174 | fn inner_structs_with_options() { | |
175 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
176 | struct Foo { | |
177 | a: Option<Box<Foo>>, | |
178 | b: Bar, | |
179 | } | |
180 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
181 | struct Bar { | |
182 | a: String, | |
183 | b: f64, | |
184 | } | |
185 | ||
186 | equivalent! { | |
187 | Foo { | |
188 | a: Some(Box::new(Foo { | |
189 | a: None, | |
190 | b: Bar { a: "foo".to_string(), b: 4.5 }, | |
191 | })), | |
192 | b: Bar { a: "bar".to_string(), b: 1.0 }, | |
193 | }, | |
194 | Table(map! { | |
195 | a: Table(map! { | |
196 | b: Table(map! { | |
197 | a: Value::String("foo".to_string()), | |
198 | b: Float(4.5) | |
199 | }) | |
200 | }), | |
201 | b: Table(map! { | |
202 | a: Value::String("bar".to_string()), | |
203 | b: Float(1.0) | |
204 | }) | |
205 | }), | |
206 | } | |
207 | } | |
208 | ||
209 | #[test] | |
210 | fn hashmap() { | |
211 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
212 | struct Foo { | |
213 | set: HashSet<char>, | |
214 | map: BTreeMap<String, isize>, | |
215 | } | |
216 | ||
217 | equivalent! { | |
218 | Foo { | |
219 | map: { | |
220 | let mut m = BTreeMap::new(); | |
221 | m.insert("foo".to_string(), 10); | |
222 | m.insert("bar".to_string(), 4); | |
223 | m | |
224 | }, | |
225 | set: { | |
226 | let mut s = HashSet::new(); | |
227 | s.insert('a'); | |
228 | s | |
229 | }, | |
230 | }, | |
231 | Table(map! { | |
232 | map: Table(map! { | |
233 | foo: Integer(10), | |
234 | bar: Integer(4) | |
235 | }), | |
236 | set: Array(vec![Value::String("a".to_string())]) | |
237 | }), | |
238 | } | |
239 | } | |
240 | ||
241 | #[test] | |
242 | fn table_array() { | |
243 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
244 | struct Foo { a: Vec<Bar>, } | |
245 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
246 | struct Bar { a: isize } | |
247 | ||
248 | equivalent! { | |
249 | Foo { a: vec![Bar { a: 1 }, Bar { a: 2 }] }, | |
250 | Table(map! { | |
251 | a: Array(vec![ | |
252 | Table(map!{ a: Integer(1) }), | |
253 | Table(map!{ a: Integer(2) }), | |
254 | ]) | |
255 | }), | |
256 | } | |
257 | } | |
258 | ||
259 | #[test] | |
260 | fn type_errors() { | |
261 | #[derive(Deserialize)] | |
262 | #[allow(dead_code)] | |
263 | struct Foo { bar: isize } | |
264 | ||
265 | error! { | |
266 | Foo, | |
267 | Table(map! { | |
268 | bar: Value::String("a".to_string()) | |
269 | }), | |
270 | "invalid type: string \"a\", expected isize for key `bar`" | |
271 | } | |
272 | ||
273 | #[derive(Deserialize)] | |
274 | #[allow(dead_code)] | |
275 | struct Bar { foo: Foo } | |
276 | ||
277 | error! { | |
278 | Bar, | |
279 | Table(map! { | |
280 | foo: Table(map! { | |
281 | bar: Value::String("a".to_string()) | |
282 | }) | |
283 | }), | |
284 | "invalid type: string \"a\", expected isize for key `foo.bar`" | |
285 | } | |
286 | } | |
287 | ||
288 | #[test] | |
289 | fn missing_errors() { | |
290 | #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
291 | struct Foo { bar: isize } | |
292 | ||
293 | error! { | |
294 | Foo, | |
295 | Table(map! { }), | |
296 | "missing field `bar`" | |
297 | } | |
298 | } | |
299 | ||
300 | #[test] | |
301 | fn parse_enum() { | |
302 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
303 | struct Foo { a: E } | |
304 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
305 | #[serde(untagged)] | |
306 | enum E { | |
307 | Bar(isize), | |
308 | Baz(String), | |
309 | Last(Foo2), | |
310 | } | |
311 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
312 | struct Foo2 { | |
313 | test: String, | |
314 | } | |
315 | ||
316 | equivalent! { | |
317 | Foo { a: E::Bar(10) }, | |
318 | Table(map! { a: Integer(10) }), | |
319 | } | |
320 | ||
321 | equivalent! { | |
322 | Foo { a: E::Baz("foo".to_string()) }, | |
323 | Table(map! { a: Value::String("foo".to_string()) }), | |
324 | } | |
325 | ||
326 | equivalent! { | |
327 | Foo { a: E::Last(Foo2 { test: "test".to_string() }) }, | |
328 | Table(map! { a: Table(map! { test: Value::String("test".to_string()) }) }), | |
329 | } | |
330 | } | |
331 | ||
7cac9316 XL |
332 | #[test] |
333 | fn parse_enum_string() { | |
334 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
335 | struct Foo { a: Sort } | |
336 | ||
337 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
338 | #[serde(rename_all = "lowercase")] | |
339 | enum Sort { | |
340 | Asc, | |
341 | Desc, | |
342 | } | |
343 | ||
344 | equivalent! { | |
345 | Foo { a: Sort::Desc }, | |
346 | Table(map! { a: Value::String("desc".to_string()) }), | |
347 | } | |
348 | ||
349 | } | |
350 | ||
8bb4bdeb XL |
351 | // #[test] |
352 | // fn unused_fields() { | |
353 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
354 | // struct Foo { a: isize } | |
355 | // | |
356 | // let v = Foo { a: 2 }; | |
357 | // let mut d = Decoder::new(Table(map! { | |
358 | // a, Integer(2), | |
359 | // b, Integer(5) | |
360 | // })); | |
361 | // assert_eq!(v, t!(Deserialize::deserialize(&mut d))); | |
362 | // | |
363 | // assert_eq!(d.toml, Some(Table(map! { | |
364 | // b, Integer(5) | |
365 | // }))); | |
366 | // } | |
367 | // | |
368 | // #[test] | |
369 | // fn unused_fields2() { | |
370 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
371 | // struct Foo { a: Bar } | |
372 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
373 | // struct Bar { a: isize } | |
374 | // | |
375 | // let v = Foo { a: Bar { a: 2 } }; | |
376 | // let mut d = Decoder::new(Table(map! { | |
377 | // a, Table(map! { | |
378 | // a, Integer(2), | |
379 | // b, Integer(5) | |
380 | // }) | |
381 | // })); | |
382 | // assert_eq!(v, t!(Deserialize::deserialize(&mut d))); | |
383 | // | |
384 | // assert_eq!(d.toml, Some(Table(map! { | |
385 | // a, Table(map! { | |
386 | // b, Integer(5) | |
387 | // }) | |
388 | // }))); | |
389 | // } | |
390 | // | |
391 | // #[test] | |
392 | // fn unused_fields3() { | |
393 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
394 | // struct Foo { a: Bar } | |
395 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
396 | // struct Bar { a: isize } | |
397 | // | |
398 | // let v = Foo { a: Bar { a: 2 } }; | |
399 | // let mut d = Decoder::new(Table(map! { | |
400 | // a, Table(map! { | |
401 | // a, Integer(2) | |
402 | // }) | |
403 | // })); | |
404 | // assert_eq!(v, t!(Deserialize::deserialize(&mut d))); | |
405 | // | |
406 | // assert_eq!(d.toml, None); | |
407 | // } | |
408 | // | |
409 | // #[test] | |
410 | // fn unused_fields4() { | |
411 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
412 | // struct Foo { a: BTreeMap<String, String> } | |
413 | // | |
414 | // let v = Foo { a: map! { a, "foo".to_string() } }; | |
415 | // let mut d = Decoder::new(Table(map! { | |
416 | // a, Table(map! { | |
417 | // a, Value::String("foo".to_string()) | |
418 | // }) | |
419 | // })); | |
420 | // assert_eq!(v, t!(Deserialize::deserialize(&mut d))); | |
421 | // | |
422 | // assert_eq!(d.toml, None); | |
423 | // } | |
424 | // | |
425 | // #[test] | |
426 | // fn unused_fields5() { | |
427 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
428 | // struct Foo { a: Vec<String> } | |
429 | // | |
430 | // let v = Foo { a: vec!["a".to_string()] }; | |
431 | // let mut d = Decoder::new(Table(map! { | |
432 | // a, Array(vec![Value::String("a".to_string())]) | |
433 | // })); | |
434 | // assert_eq!(v, t!(Deserialize::deserialize(&mut d))); | |
435 | // | |
436 | // assert_eq!(d.toml, None); | |
437 | // } | |
438 | // | |
439 | // #[test] | |
440 | // fn unused_fields6() { | |
441 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
442 | // struct Foo { a: Option<Vec<String>> } | |
443 | // | |
444 | // let v = Foo { a: Some(vec![]) }; | |
445 | // let mut d = Decoder::new(Table(map! { | |
446 | // a, Array(vec![]) | |
447 | // })); | |
448 | // assert_eq!(v, t!(Deserialize::deserialize(&mut d))); | |
449 | // | |
450 | // assert_eq!(d.toml, None); | |
451 | // } | |
452 | // | |
453 | // #[test] | |
454 | // fn unused_fields7() { | |
455 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
456 | // struct Foo { a: Vec<Bar> } | |
457 | // #[derive(Serialize, Deserialize, PartialEq, Debug)] | |
458 | // struct Bar { a: isize } | |
459 | // | |
460 | // let v = Foo { a: vec![Bar { a: 1 }] }; | |
461 | // let mut d = Decoder::new(Table(map! { | |
462 | // a, Array(vec![Table(map! { | |
463 | // a, Integer(1), | |
464 | // b, Integer(2) | |
465 | // })]) | |
466 | // })); | |
467 | // assert_eq!(v, t!(Deserialize::deserialize(&mut d))); | |
468 | // | |
469 | // assert_eq!(d.toml, Some(Table(map! { | |
470 | // a, Array(vec![Table(map! { | |
471 | // b, Integer(2) | |
472 | // })]) | |
473 | // }))); | |
474 | // } | |
475 | ||
476 | #[test] | |
477 | fn empty_arrays() { | |
478 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
479 | struct Foo { a: Vec<Bar> } | |
480 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
481 | struct Bar; | |
482 | ||
483 | equivalent! { | |
484 | Foo { a: vec![] }, | |
485 | Table(map! {a: Array(Vec::new())}), | |
486 | } | |
487 | } | |
488 | ||
489 | #[test] | |
490 | fn empty_arrays2() { | |
491 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
492 | struct Foo { a: Option<Vec<Bar>> } | |
493 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] | |
494 | struct Bar; | |
495 | ||
496 | equivalent! { | |
497 | Foo { a: None }, | |
498 | Table(map! {}), | |
499 | } | |
500 | ||
501 | equivalent!{ | |
502 | Foo { a: Some(vec![]) }, | |
503 | Table(map! { a: Array(vec![]) }), | |
504 | } | |
505 | } | |
506 | ||
507 | #[test] | |
508 | fn extra_keys() { | |
509 | #[derive(Serialize, Deserialize)] | |
510 | struct Foo { a: isize } | |
511 | ||
512 | let toml = Table(map! { a: Integer(2), b: Integer(2) }); | |
513 | assert!(toml.clone().try_into::<Foo>().is_ok()); | |
514 | assert!(toml::from_str::<Foo>(&toml.to_string()).is_ok()); | |
515 | } |