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