toml/
macros.rs

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