toml/
macros.rs

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