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///     version = "0.4.5"
12///     authors = ["Alex Crichton <alex@alexcrichton.com>"]
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]
28macro_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)]
74macro_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)
202    };
203
204    (@value (nan)) => {
205        $crate::Value::Float(::std::f64::NAN)
206    };
207
208    (@value nan) => {
209        $crate::Value::Float(::std::f64::NAN)
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.
412pub 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.
418pub 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
429fn 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}