1 #![allow(renamed_and_removed_lints)]
2 #![allow(clippy::blacklisted_name)]
3
4 use std::collections::HashMap;
5 use std::fmt::Debug;
6
7 use serde::Deserialize;
8 use snapbox::assert_data_eq;
9 use snapbox::prelude::*;
10 use snapbox::str;
11 use toml::value::Datetime;
12 use toml::Spanned;
13
14 /// A set of good datetimes.
good_datetimes() -> Vec<&'static str>15 pub(crate) fn good_datetimes() -> Vec<&'static str> {
16 vec![
17 "1997-09-09T09:09:09Z",
18 "1997-09-09T09:09:09+09:09",
19 "1997-09-09T09:09:09-09:09",
20 "1997-09-09T09:09:09",
21 "1997-09-09",
22 "09:09:09",
23 "1997-09-09T09:09:09.09Z",
24 "1997-09-09T09:09:09.09+09:09",
25 "1997-09-09T09:09:09.09-09:09",
26 "1997-09-09T09:09:09.09",
27 "09:09:09.09",
28 ]
29 }
30
31 #[test]
test_spanned_field()32 fn test_spanned_field() {
33 #[derive(Deserialize)]
34 struct Foo<T> {
35 foo: Spanned<T>,
36 }
37
38 #[derive(Deserialize)]
39 struct BareFoo<T> {
40 foo: T,
41 }
42
43 fn good<T>(s: &str, expected: &str, end: Option<usize>)
44 where
45 T: serde::de::DeserializeOwned + Debug + PartialEq,
46 {
47 let foo: Foo<T> = toml::from_str(s).unwrap();
48
49 assert_eq!(6, foo.foo.span().start);
50 if let Some(end) = end {
51 assert_eq!(end, foo.foo.span().end);
52 } else {
53 assert_eq!(s.len(), foo.foo.span().end);
54 }
55 assert_eq!(expected, &s[foo.foo.span()]);
56
57 // Test for Spanned<> at the top level
58 let foo_outer: Spanned<BareFoo<T>> = toml::from_str(s).unwrap();
59
60 assert_eq!(0, foo_outer.span().start);
61 assert_eq!(s.len(), foo_outer.span().end);
62 assert_eq!(foo.foo.into_inner(), foo_outer.into_inner().foo);
63 }
64
65 good::<String>("foo = \"foo\"", "\"foo\"", None);
66 good::<u32>("foo = 42", "42", None);
67 // leading plus
68 good::<u32>("foo = +42", "+42", None);
69 // table
70 good::<HashMap<String, u32>>(
71 "foo = {\"foo\" = 42, \"bar\" = 42}",
72 "{\"foo\" = 42, \"bar\" = 42}",
73 None,
74 );
75 // array
76 good::<Vec<u32>>("foo = [0, 1, 2, 3, 4]", "[0, 1, 2, 3, 4]", None);
77 // datetime
78 good::<String>(
79 "foo = \"1997-09-09T09:09:09Z\"",
80 "\"1997-09-09T09:09:09Z\"",
81 None,
82 );
83
84 for expected in good_datetimes() {
85 let s = format!("foo = {}", expected);
86 good::<Datetime>(&s, expected, None);
87 }
88 // ending at something other than the absolute end
89 good::<u32>("foo = 42\nnoise = true", "42", Some(8));
90 }
91
92 #[test]
test_inner_spanned_table()93 fn test_inner_spanned_table() {
94 #[derive(Deserialize)]
95 struct Foo {
96 foo: Spanned<HashMap<Spanned<String>, Spanned<String>>>,
97 }
98
99 fn good(s: &str, zero: bool) {
100 let foo: Foo = toml::from_str(s).unwrap();
101
102 if zero {
103 assert_eq!(foo.foo.span().start, 0);
104 assert_eq!(foo.foo.span().end, 73);
105 } else {
106 assert_eq!(foo.foo.span().start, s.find('{').unwrap());
107 assert_eq!(foo.foo.span().end, s.find('}').unwrap() + 1);
108 }
109 for (k, v) in foo.foo.as_ref().iter() {
110 assert_eq!(&s[k.span().start..k.span().end], k.as_ref());
111 assert_eq!(&s[(v.span().start + 1)..(v.span().end - 1)], v.as_ref());
112 }
113 }
114
115 good(
116 "\
117 [foo]
118 a = 'b'
119 bar = 'baz'
120 c = 'd'
121 e = \"f\"
122 ",
123 true,
124 );
125
126 good(
127 "
128 foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" }",
129 false,
130 );
131 }
132
133 #[test]
test_outer_spanned_table()134 fn test_outer_spanned_table() {
135 #[derive(Deserialize)]
136 struct Foo {
137 foo: HashMap<Spanned<String>, Spanned<String>>,
138 }
139
140 fn good(s: &str) {
141 let foo: Foo = toml::from_str(s).unwrap();
142
143 for (k, v) in foo.foo.iter() {
144 assert_eq!(&s[k.span().start..k.span().end], k.as_ref());
145 assert_eq!(&s[(v.span().start + 1)..(v.span().end - 1)], v.as_ref());
146 }
147 }
148
149 good(
150 "
151 [foo]
152 a = 'b'
153 bar = 'baz'
154 c = 'd'
155 e = \"f\"
156 ",
157 );
158
159 good(
160 "
161 foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" }
162 ",
163 );
164 }
165
166 #[test]
test_spanned_nested()167 fn test_spanned_nested() {
168 #[derive(Deserialize)]
169 struct Foo {
170 foo: HashMap<Spanned<String>, HashMap<Spanned<String>, Spanned<String>>>,
171 }
172
173 fn good(s: &str) {
174 let foo: Foo = toml::from_str(s).unwrap();
175
176 for (k, v) in foo.foo.iter() {
177 assert_eq!(&s[k.span().start..k.span().end], k.as_ref());
178 for (n_k, n_v) in v.iter() {
179 assert_eq!(&s[n_k.span().start..n_k.span().end], n_k.as_ref());
180 assert_eq!(
181 &s[(n_v.span().start + 1)..(n_v.span().end - 1)],
182 n_v.as_ref()
183 );
184 }
185 }
186 }
187
188 good(
189 "
190 [foo.a]
191 a = 'b'
192 c = 'd'
193 e = \"f\"
194 [foo.bar]
195 baz = 'true'
196 ",
197 );
198
199 good(
200 "
201 [foo]
202 foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" }
203 bazz = {}
204 g = { h = 'i' }
205 ",
206 );
207 }
208
209 #[test]
test_spanned_array()210 fn test_spanned_array() {
211 #[derive(Deserialize)]
212 struct Foo {
213 foo: Vec<Spanned<HashMap<Spanned<String>, Spanned<String>>>>,
214 }
215
216 let toml = "\
217 [[foo]]
218 a = 'b'
219 bar = 'baz'
220 c = 'd'
221 e = \"f\"
222 [[foo]]
223 a = 'c'
224 bar = 'baz'
225 c = 'g'
226 e = \"h\"
227 ";
228 let foo_list: Foo = toml::from_str(toml).unwrap();
229
230 for (foo, expected) in foo_list.foo.iter().zip([0..75, 84..159]) {
231 assert_eq!(foo.span(), expected);
232 for (k, v) in foo.as_ref().iter() {
233 assert_eq!(&toml[k.span().start..k.span().end], k.as_ref());
234 assert_eq!(&toml[(v.span().start + 1)..(v.span().end - 1)], v.as_ref());
235 }
236 }
237 }
238
239 #[test]
deny_unknown_fields()240 fn deny_unknown_fields() {
241 #[derive(Debug, serde::Deserialize)]
242 #[serde(deny_unknown_fields)]
243 struct Example {
244 #[allow(dead_code)]
245 real: u32,
246 }
247
248 let error = toml::from_str::<Example>(
249 r#"# my comment
250 # bla bla bla
251 fake = 1"#,
252 )
253 .unwrap_err();
254 assert_data_eq!(
255 error.to_string(),
256 str![[r#"
257 TOML parse error at line 3, column 1
258 |
259 3 | fake = 1
260 | ^^^^
261 unknown field `fake`, expected `real`
262
263 "#]]
264 .raw()
265 );
266 }
267