1 pub use serde::de::{Deserialize, IntoDeserializer};
2 
3 use crate::value::{Array, Table, Value};
4 
5 /// Construct a [`Table`] from TOML syntax.
6 ///
7 /// ```rust
8 /// let cargo_toml = toml::toml! {
9 ///     [package]
10 ///     name = "toml"
11 ///     version = "0.4.5"
12 ///     authors = ["Alex Crichton <[email protected]>"]
13 ///
14 ///     [badges]
15 ///     travis-ci = { repository = "alexcrichton/toml-rs" }
16 ///
17 ///     [dependencies]
18 ///     serde = "1.0"
19 ///
20 ///     [dev-dependencies]
21 ///     serde_derive = "1.0"
22 ///     serde_json = "1.0"
23 /// };
24 ///
25 /// println!("{:#?}", cargo_toml);
26 /// ```
27 #[macro_export]
28 macro_rules! toml {
29     ($($toml:tt)+) => {{
30         let table = $crate::value::Table::new();
31         let mut root = $crate::Value::Table(table);
32         $crate::toml_internal!(@toplevel root [] $($toml)+);
33         match root {
34             $crate::Value::Table(table) => table,
35             _ => unreachable!(),
36         }
37     }};
38 }
39 
40 // TT-muncher to parse TOML syntax into a toml::Value.
41 //
42 //    @toplevel -- Parse tokens outside of an inline table or inline array. In
43 //                 this state, `[table headers]` and `[[array headers]]` are
44 //                 allowed and `key = value` pairs are not separated by commas.
45 //
46 //    @topleveldatetime -- Helper to parse a Datetime from string and insert it
47 //                 into a table, continuing in the @toplevel state.
48 //
49 //    @path -- Turn a path segment into a string. Segments that look like idents
50 //                 are stringified, while quoted segments like `"cfg(windows)"`
51 //                 are not.
52 //
53 //    @value -- Parse the value part of a `key = value` pair, which may be a
54 //                 primitive or inline table or inline array.
55 //
56 //    @table -- Parse the contents of an inline table, returning them as a
57 //                 toml::Value::Table.
58 //
59 //    @tabledatetime -- Helper to parse a Datetime from string and insert it
60 //                 into a table, continuing in the @table state.
61 //
62 //    @array -- Parse the contents of an inline array, returning them as a
63 //                 toml::Value::Array.
64 //
65 //    @arraydatetime -- Helper to parse a Datetime from string and push it into
66 //                 an array, continuing in the @array state.
67 //
68 //    @trailingcomma -- Helper to append a comma to a sequence of tokens if the
69 //                 sequence is non-empty and does not already end in a trailing
70 //                 comma.
71 //
72 #[macro_export]
73 #[doc(hidden)]
74 macro_rules! toml_internal {
75     // Base case, no elements remaining.
76     (@toplevel $root:ident [$($path:tt)*]) => {};
77 
78     // Parse negative number `key = -value`.
79     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = - $v:tt $($rest:tt)*) => {
80         $crate::toml_internal!(@toplevel $root [$($path)*] $($($k)-+).+ = (-$v) $($rest)*);
81     };
82 
83     // Parse positive number `key = +value`.
84     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = + $v:tt $($rest:tt)*) => {
85         $crate::toml_internal!(@toplevel $root [$($path)*] $($($k)-+).+ = ($v) $($rest)*);
86     };
87 
88     // Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
89     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
90         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
91     };
92     // Space instead of T.
93     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
94         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
95     };
96 
97     // Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
98     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
99         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
100     };
101     // Space instead of T.
102     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
103         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
104     };
105 
106     // Parse local datetime `key = 1979-05-27T00:32:00.999999`.
107     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
108         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
109     };
110     // Space instead of T.
111     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
112         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
113     };
114 
115     // Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
116     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
117         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
118     };
119     // Space instead of T.
120     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
121         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
122     };
123 
124     // Parse local date `key = 1979-05-27`.
125     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $($rest:tt)*) => {
126         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day) $($rest)*);
127     };
128 
129     // Parse local time `key = 00:32:00.999999`.
130     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
131         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($hr : $min : $sec . $frac) $($rest)*);
132     };
133 
134     // Parse local time `key = 07:32:00`.
135     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
136         $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($hr : $min : $sec) $($rest)*);
137     };
138 
139     // Parse any other `key = value` including string, inline array, inline
140     // table, number, and boolean.
141     (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $v:tt $($rest:tt)*) => {{
142         $crate::macros::insert_toml(
143             &mut $root,
144             &[$($path)* $(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
145             $crate::toml_internal!(@value $v));
146         $crate::toml_internal!(@toplevel $root [$($path)*] $($rest)*);
147     }};
148 
149     // Parse array header `[[bin]]`.
150     (@toplevel $root:ident $oldpath:tt [[$($($path:tt)-+).+]] $($rest:tt)*) => {
151         $crate::macros::push_toml(
152             &mut $root,
153             &[$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+]);
154         $crate::toml_internal!(@toplevel $root [$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
155     };
156 
157     // Parse table header `[patch.crates-io]`.
158     (@toplevel $root:ident $oldpath:tt [$($($path:tt)-+).+] $($rest:tt)*) => {
159         $crate::macros::insert_toml(
160             &mut $root,
161             &[$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+],
162             $crate::Value::Table($crate::value::Table::new()));
163         $crate::toml_internal!(@toplevel $root [$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
164     };
165 
166     // Parse datetime from string and insert into table.
167     (@topleveldatetime $root:ident [$($path:tt)*] $($($k:tt)-+).+ = ($($datetime:tt)+) $($rest:tt)*) => {
168         $crate::macros::insert_toml(
169             &mut $root,
170             &[$($path)* $(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
171             $crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
172         $crate::toml_internal!(@toplevel $root [$($path)*] $($rest)*);
173     };
174 
175     // Turn a path segment into a string.
176     (@path $ident:ident) => {
177         stringify!($ident)
178     };
179 
180     // For a path segment that is not an ident, expect that it is already a
181     // quoted string, like in `[target."cfg(windows)".dependencies]`.
182     (@path $quoted:tt) => {
183         $quoted
184     };
185 
186     // Construct a Value from an inline table.
187     (@value { $($inline:tt)* }) => {{
188         let mut table = $crate::Value::Table($crate::value::Table::new());
189         $crate::toml_internal!(@trailingcomma (@table table) $($inline)*);
190         table
191     }};
192 
193     // Construct a Value from an inline array.
194     (@value [ $($inline:tt)* ]) => {{
195         let mut array = $crate::value::Array::new();
196         $crate::toml_internal!(@trailingcomma (@array array) $($inline)*);
197         $crate::Value::Array(array)
198     }};
199 
200     (@value (-nan)) => {
201         $crate::Value::Float(::std::f64::NAN.copysign(-1.0))
202     };
203 
204     (@value (nan)) => {
205         $crate::Value::Float(::std::f64::NAN.copysign(1.0))
206     };
207 
208     (@value nan) => {
209         $crate::Value::Float(::std::f64::NAN.copysign(1.0))
210     };
211 
212     (@value (-inf)) => {
213         $crate::Value::Float(::std::f64::NEG_INFINITY)
214     };
215 
216     (@value (inf)) => {
217         $crate::Value::Float(::std::f64::INFINITY)
218     };
219 
220     (@value inf) => {
221         $crate::Value::Float(::std::f64::INFINITY)
222     };
223 
224     // Construct a Value from any other type, probably string or boolean or number.
225     (@value $v:tt) => {{
226         // TODO: Implement this with something like serde_json::to_value instead.
227         let de = $crate::macros::IntoDeserializer::<$crate::de::Error>::into_deserializer($v);
228         <$crate::Value as $crate::macros::Deserialize>::deserialize(de).unwrap()
229     }};
230 
231     // Base case of inline table.
232     (@table $root:ident) => {};
233 
234     // Parse negative number `key = -value`.
235     (@table $root:ident $($($k:tt)-+).+ = - $v:tt , $($rest:tt)*) => {
236         $crate::toml_internal!(@table $root $($($k)-+).+ = (-$v) , $($rest)*);
237     };
238 
239     // Parse positive number `key = +value`.
240     (@table $root:ident $($($k:tt)-+).+ = + $v:tt , $($rest:tt)*) => {
241         $crate::toml_internal!(@table $root $($($k)-+).+ = ($v) , $($rest)*);
242     };
243 
244     // Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
245     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
246         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
247     };
248     // Space instead of T.
249     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
250         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
251     };
252 
253     // Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
254     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
255         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
256     };
257     // Space instead of T.
258     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
259         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
260     };
261 
262     // Parse local datetime `key = 1979-05-27T00:32:00.999999`.
263     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
264         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
265     };
266     // Space instead of T.
267     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
268         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
269     };
270 
271     // Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
272     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
273         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
274     };
275     // Space instead of T.
276     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
277         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
278     };
279 
280     // Parse local date `key = 1979-05-27`.
281     (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
282         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day) $($rest)*);
283     };
284 
285     // Parse local time `key = 00:32:00.999999`.
286     (@table $root:ident $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
287         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($hr : $min : $sec . $frac) $($rest)*);
288     };
289 
290     // Parse local time `key = 07:32:00`.
291     (@table $root:ident $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
292         $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($hr : $min : $sec) $($rest)*);
293     };
294 
295     // Parse any other type, probably string or boolean or number.
296     (@table $root:ident $($($k:tt)-+).+ = $v:tt , $($rest:tt)*) => {
297         $crate::macros::insert_toml(
298             &mut $root,
299             &[$(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
300             $crate::toml_internal!(@value $v));
301         $crate::toml_internal!(@table $root $($rest)*);
302     };
303 
304     // Parse a Datetime from string and continue in @table state.
305     (@tabledatetime $root:ident $($($k:tt)-+).+ = ($($datetime:tt)*) $($rest:tt)*) => {
306         $crate::macros::insert_toml(
307             &mut $root,
308             &[$(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
309             $crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
310         $crate::toml_internal!(@table $root $($rest)*);
311     };
312 
313     // Base case of inline array.
314     (@array $root:ident) => {};
315 
316     // Parse negative number `-value`.
317     (@array $root:ident - $v:tt , $($rest:tt)*) => {
318         $crate::toml_internal!(@array $root (-$v) , $($rest)*);
319     };
320 
321     // Parse positive number `+value`.
322     (@array $root:ident + $v:tt , $($rest:tt)*) => {
323         $crate::toml_internal!(@array $root ($v) , $($rest)*);
324     };
325 
326     // Parse offset datetime `1979-05-27T00:32:00.999999-07:00`.
327     (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
328         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
329     };
330     // Space instead of T.
331     (@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
332         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
333     };
334 
335     // Parse offset datetime `1979-05-27T00:32:00-07:00`.
336     (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
337         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
338     };
339     // Space instead of T.
340     (@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
341         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
342     };
343 
344     // Parse local datetime `1979-05-27T00:32:00.999999`.
345     (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
346         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
347     };
348     // Space instead of T.
349     (@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
350         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
351     };
352 
353     // Parse offset datetime `1979-05-27T07:32:00Z` and local datetime `1979-05-27T07:32:00`.
354     (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
355         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec) $($rest)*);
356     };
357     // Space instead of T.
358     (@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
359         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
360     };
361 
362     // Parse local date `1979-05-27`.
363     (@array $root:ident $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
364         $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day) $($rest)*);
365     };
366 
367     // Parse local time `00:32:00.999999`.
368     (@array $root:ident $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
369         $crate::toml_internal!(@arraydatetime $root ($hr : $min : $sec . $frac) $($rest)*);
370     };
371 
372     // Parse local time `07:32:00`.
373     (@array $root:ident $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
374         $crate::toml_internal!(@arraydatetime $root ($hr : $min : $sec) $($rest)*);
375     };
376 
377     // Parse any other type, probably string or boolean or number.
378     (@array $root:ident $v:tt , $($rest:tt)*) => {
379         $root.push($crate::toml_internal!(@value $v));
380         $crate::toml_internal!(@array $root $($rest)*);
381     };
382 
383     // Parse a Datetime from string and continue in @array state.
384     (@arraydatetime $root:ident ($($datetime:tt)*) $($rest:tt)*) => {
385         $root.push($crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
386         $crate::toml_internal!(@array $root $($rest)*);
387     };
388 
389     // No trailing comma required if the tokens are empty.
390     (@trailingcomma ($($args:tt)*)) => {
391         $crate::toml_internal!($($args)*);
392     };
393 
394     // Tokens end with a trailing comma, do not append another one.
395     (@trailingcomma ($($args:tt)*) ,) => {
396         $crate::toml_internal!($($args)* ,);
397     };
398 
399     // Tokens end with something other than comma, append a trailing comma.
400     (@trailingcomma ($($args:tt)*) $last:tt) => {
401         $crate::toml_internal!($($args)* $last ,);
402     };
403 
404     // Not yet at the last token.
405     (@trailingcomma ($($args:tt)*) $first:tt $($rest:tt)+) => {
406         $crate::toml_internal!(@trailingcomma ($($args)* $first) $($rest)+);
407     };
408 }
409 
410 // Called when parsing a `key = value` pair.
411 // Inserts an entry into the table at the given path.
insert_toml(root: &mut Value, path: &[&str], value: Value)412 pub fn insert_toml(root: &mut Value, path: &[&str], value: Value) {
413     *traverse(root, path) = value;
414 }
415 
416 // Called when parsing an `[[array header]]`.
417 // Pushes an empty table onto the array at the given path.
push_toml(root: &mut Value, path: &[&str])418 pub fn push_toml(root: &mut Value, path: &[&str]) {
419     let target = traverse(root, path);
420     if !target.is_array() {
421         *target = Value::Array(Array::new());
422     }
423     target
424         .as_array_mut()
425         .unwrap()
426         .push(Value::Table(Table::new()));
427 }
428 
traverse<'a>(root: &'a mut Value, path: &[&str]) -> &'a mut Value429 fn traverse<'a>(root: &'a mut Value, path: &[&str]) -> &'a mut Value {
430     let mut cur = root;
431     for &key in path {
432         // Lexical lifetimes :D
433         let cur1 = cur;
434 
435         // From the TOML spec:
436         //
437         // > Each double-bracketed sub-table will belong to the most recently
438         // > defined table element above it.
439         let cur2 = if cur1.is_array() {
440             cur1.as_array_mut().unwrap().last_mut().unwrap()
441         } else {
442             cur1
443         };
444 
445         // We are about to index into this value, so it better be a table.
446         if !cur2.is_table() {
447             *cur2 = Value::Table(Table::new());
448         }
449 
450         if !cur2.as_table().unwrap().contains_key(key) {
451             // Insert an empty table for the next loop iteration to point to.
452             let empty = Value::Table(Table::new());
453             cur2.as_table_mut().unwrap().insert(key.to_owned(), empty);
454         }
455 
456         // Step into the current table.
457         cur = cur2.as_table_mut().unwrap().get_mut(key).unwrap();
458     }
459     cur
460 }
461