toml/
macros.rs

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