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: &mut Value = traverse(root, path);
415 if !target.is_array() {
416 *target = Value::Array(Array::new());
417 }
418 target&mut Vec
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}
456