1 use snapbox::assert_eq;
2 use toml_edit::{Document, Key, Value};
3 
4 macro_rules! parse {
5     ($s:expr, $ty:ty) => {{
6         let v = $s.parse::<$ty>();
7         assert!(v.is_ok(), "Failed with {}", v.unwrap_err());
8         v.unwrap()
9     }};
10 }
11 
12 macro_rules! parse_value {
13     ($s:expr) => {
14         parse!($s, Value)
15     };
16 }
17 
18 macro_rules! test_key {
19     ($s:expr, $expected:expr) => {{
20         let key = parse!($s, Key);
21         assert_eq!($expected, key.get(), "");
22     }};
23 }
24 
25 #[test]
test_key_from_str()26 fn test_key_from_str() {
27     test_key!("a", "a");
28     test_key!(r#"'hello key'"#, "hello key");
29     test_key!(
30         r#""Jos\u00E9\U000A0000\n\t\r\f\b\"""#,
31         "Jos\u{00E9}\u{A0000}\n\t\r\u{c}\u{8}\""
32     );
33     test_key!("\"\"", "");
34     test_key!("\"'hello key'bla\"", "'hello key'bla");
35     test_key!(
36         "'C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\cargo-edit-test.YizxPxxElXn9'",
37         "C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\cargo-edit-test.YizxPxxElXn9"
38     );
39 }
40 
41 #[test]
test_value_from_str()42 fn test_value_from_str() {
43     assert!(parse_value!("1979-05-27T00:32:00.999999-07:00").is_datetime());
44     assert!(parse_value!("1979-05-27T00:32:00.999999Z").is_datetime());
45     assert!(parse_value!("1979-05-27T00:32:00.999999").is_datetime());
46     assert!(parse_value!("1979-05-27T00:32:00").is_datetime());
47     assert!(parse_value!("1979-05-27").is_datetime());
48     assert!(parse_value!("00:32:00").is_datetime());
49     assert!(parse_value!("-239").is_integer());
50     assert!(parse_value!("1e200").is_float());
51     assert!(parse_value!("9_224_617.445_991_228_313").is_float());
52     assert!(parse_value!(r#""basic string\nJos\u00E9\n""#).is_str());
53     assert!(parse_value!(
54         r#""""
55 multiline basic string
56 """"#
57     )
58     .is_str());
59     assert!(parse_value!(r"'literal string\ \'").is_str());
60     assert!(parse_value!(
61         r"'''multiline
62 literal \ \
63 string'''"
64     )
65     .is_str());
66     assert!(parse_value!(r#"{ hello = "world", a = 1}"#).is_inline_table());
67     assert!(
68         parse_value!(r#"[ { x = 1, a = "2" }, {a = "a",b = "b",     c =    "c"} ]"#).is_array()
69     );
70     let wp = "C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\cargo-edit-test.YizxPxxElXn9";
71     let lwp = "'C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\cargo-edit-test.YizxPxxElXn9'";
72     assert_eq!(Value::from(wp).as_str(), parse_value!(lwp).as_str());
73     assert!(parse_value!(r#""\\\"\b\f\n\r\t\u00E9\U000A0000""#).is_str());
74 }
75 
76 #[test]
test_key_unification()77 fn test_key_unification() {
78     let toml = r#"
79 [a]
80 [a.'b'.c]
81 [a."b".c.e]
82 [a.b.c.d]
83 "#;
84     let expected = r#"
85 [a]
86 [a.'b'.c]
87 [a.'b'.c.e]
88 [a.'b'.c.d]
89 "#;
90     let doc = toml.parse::<Document>();
91     assert!(doc.is_ok());
92     let doc = doc.unwrap();
93 
94     assert_eq(expected, doc.to_string());
95 }
96 
97 macro_rules! bad {
98     ($toml:expr, $msg:expr) => {
99         match $toml.parse::<Document>() {
100             Ok(s) => panic!("parsed to: {:#?}", s),
101             Err(e) => snapbox::assert_eq($msg, e.to_string()),
102         }
103     };
104 }
105 
106 #[test]
crlf()107 fn crlf() {
108     "\
109      [project]\r\n\
110      \r\n\
111      name = \"splay\"\r\n\
112      version = \"0.1.0\"\r\n\
113      authors = [\"[email protected]\"]\r\n\
114      \r\n\
115      [[lib]]\r\n\
116      \r\n\
117      path = \"lib.rs\"\r\n\
118      name = \"splay\"\r\n\
119      description = \"\"\"\
120      A Rust implementation of a TAR file reader and writer. This library does not\r\n\
121      currently handle compression, but it is abstract over all I/O readers and\r\n\
122      writers. Additionally, great lengths are taken to ensure that the entire\r\n\
123      contents are never required to be entirely resident in memory all at once.\r\n\
124      \"\"\"\
125      "
126     .parse::<Document>()
127     .unwrap();
128 }
129 
130 #[test]
fun_with_strings()131 fn fun_with_strings() {
132     let table = r#"
133 bar = "\U00000000"
134 key1 = "One\nTwo"
135 key2 = """One\nTwo"""
136 key3 = """
137 One
138 Two"""
139 
140 key4 = "The quick brown fox jumps over the lazy dog."
141 key5 = """
142 The quick brown \
143 
144 
145 fox jumps over \
146 the lazy dog."""
147 key6 = """\
148    The quick brown \
149    fox jumps over \
150    the lazy dog.\
151    """
152 # What you see is what you get.
153 winpath  = 'C:\Users\nodejs\templates'
154 winpath2 = '\\ServerX\admin$\system32\'
155 quoted   = 'Tom "Dubs" Preston-Werner'
156 regex    = '<\i\c*\s*>'
157 
158 regex2 = '''I [dw]on't need \d{2} apples'''
159 lines  = '''
160 The first newline is
161 trimmed in raw strings.
162 All other whitespace
163 is preserved.
164 '''
165 "#
166     .parse::<Document>()
167     .unwrap();
168     assert_eq!(table["bar"].as_str(), Some("\0"));
169     assert_eq!(table["key1"].as_str(), Some("One\nTwo"));
170     assert_eq!(table["key2"].as_str(), Some("One\nTwo"));
171     assert_eq!(table["key3"].as_str(), Some("One\nTwo"));
172 
173     let msg = "The quick brown fox jumps over the lazy dog.";
174     assert_eq!(table["key4"].as_str(), Some(msg));
175     assert_eq!(table["key5"].as_str(), Some(msg));
176     assert_eq!(table["key6"].as_str(), Some(msg));
177 
178     assert_eq!(
179         table["winpath"].as_str(),
180         Some(r"C:\Users\nodejs\templates")
181     );
182     assert_eq!(
183         table["winpath2"].as_str(),
184         Some(r"\\ServerX\admin$\system32\")
185     );
186     assert_eq!(
187         table["quoted"].as_str(),
188         Some(r#"Tom "Dubs" Preston-Werner"#)
189     );
190     assert_eq!(table["regex"].as_str(), Some(r"<\i\c*\s*>"));
191     assert_eq!(
192         table["regex2"].as_str(),
193         Some(r"I [dw]on't need \d{2} apples")
194     );
195     assert_eq!(
196         table["lines"].as_str(),
197         Some(
198             "The first newline is\n\
199              trimmed in raw strings.\n\
200              All other whitespace\n\
201              is preserved.\n"
202         )
203     );
204 }
205 
206 #[test]
tables_in_arrays()207 fn tables_in_arrays() {
208     let table = r#"
209 [[foo]]
210 #…
211 [foo.bar]
212 #…
213 
214 [[foo]] # ...
215 #…
216 [foo.bar]
217 #...
218 "#
219     .parse::<Document>()
220     .unwrap();
221     table["foo"][0]["bar"].as_table().unwrap();
222     table["foo"][1]["bar"].as_table().unwrap();
223 }
224 
225 #[test]
empty_table()226 fn empty_table() {
227     let table = r#"
228 [foo]"#
229         .parse::<Document>()
230         .unwrap();
231     table["foo"].as_table().unwrap();
232 }
233 
234 #[test]
mixed_table_issue_527()235 fn mixed_table_issue_527() {
236     let input = r#"
237 [package]
238 metadata.msrv = "1.65.0"
239 
240 [package.metadata.release.pre-release-replacements]
241 "#;
242     let document = input.parse::<Document>().unwrap();
243     let actual = document.to_string();
244     assert_eq(input, actual);
245 }
246 
247 #[test]
fruit()248 fn fruit() {
249     let table = r#"
250 [[fruit]]
251 name = "apple"
252 
253 [fruit.physical]
254 color = "red"
255 shape = "round"
256 
257 [[fruit.variety]]
258 name = "red delicious"
259 
260 [[fruit.variety]]
261 name = "granny smith"
262 
263 [[fruit]]
264 name = "banana"
265 
266 [[fruit.variety]]
267 name = "plantain"
268 "#
269     .parse::<Document>()
270     .unwrap();
271     assert_eq!(table["fruit"][0]["name"].as_str(), Some("apple"));
272     assert_eq!(table["fruit"][0]["physical"]["color"].as_str(), Some("red"));
273     assert_eq!(
274         table["fruit"][0]["physical"]["shape"].as_str(),
275         Some("round")
276     );
277     assert_eq!(
278         table["fruit"][0]["variety"][0]["name"].as_str(),
279         Some("red delicious")
280     );
281     assert_eq!(
282         table["fruit"][0]["variety"][1]["name"].as_str(),
283         Some("granny smith")
284     );
285     assert_eq!(table["fruit"][1]["name"].as_str(), Some("banana"));
286     assert_eq!(
287         table["fruit"][1]["variety"][0]["name"].as_str(),
288         Some("plantain")
289     );
290 }
291 
292 #[test]
stray_cr()293 fn stray_cr() {
294     bad!(
295         "\r",
296         "\
297 TOML parse error at line 1, column 1
298   |
299 1 | \r
300   | ^
301 
302 "
303     );
304     bad!(
305         "a = [ \r ]",
306         "\
307 TOML parse error at line 1, column 7
308   |
309 1 | a = [ \r
310  ]
311   |       ^
312 invalid array
313 expected `]`
314 "
315     );
316     bad!(
317         "a = \"\"\"\r\"\"\"",
318         "\
319 TOML parse error at line 1, column 8
320   |
321 1 | a = \"\"\"\r
322 \"\"\"
323   |        ^
324 invalid multiline basic string
325 "
326     );
327     bad!(
328         "a = \"\"\"\\  \r  \"\"\"",
329         "\
330 TOML parse error at line 1, column 10
331   |
332 1 | a = \"\"\"\\  \r
333   \"\"\"
334   |          ^
335 invalid escape sequence
336 expected `b`, `f`, `n`, `r`, `t`, `u`, `U`, `\\`, `\"`
337 "
338     );
339     bad!(
340         "a = '''\r'''",
341         "\
342 TOML parse error at line 1, column 8
343   |
344 1 | a = '''\r
345 '''
346   |        ^
347 invalid multiline literal string
348 "
349     );
350     bad!(
351         "a = '\r'",
352         "\
353 TOML parse error at line 1, column 6
354   |
355 1 | a = '\r
356 '
357   |      ^
358 invalid literal string
359 "
360     );
361     bad!(
362         "a = \"\r\"",
363         "\
364 TOML parse error at line 1, column 6
365   |
366 1 | a = \"\r
367 \"
368   |      ^
369 invalid basic string
370 "
371     );
372 }
373 
374 #[test]
blank_literal_string()375 fn blank_literal_string() {
376     let table = "foo = ''".parse::<Document>().unwrap();
377     assert_eq!(table["foo"].as_str(), Some(""));
378 }
379 
380 #[test]
many_blank()381 fn many_blank() {
382     let table = "foo = \"\"\"\n\n\n\"\"\"".parse::<Document>().unwrap();
383     assert_eq!(table["foo"].as_str(), Some("\n\n"));
384 }
385 
386 #[test]
literal_eats_crlf()387 fn literal_eats_crlf() {
388     let table = "
389         foo = \"\"\"\\\r\n\"\"\"
390         bar = \"\"\"\\\r\n   \r\n   \r\n   a\"\"\"
391     "
392     .parse::<Document>()
393     .unwrap();
394     assert_eq!(table["foo"].as_str(), Some(""));
395     assert_eq!(table["bar"].as_str(), Some("a"));
396 }
397 
398 #[test]
string_no_newline()399 fn string_no_newline() {
400     bad!(
401         "a = \"\n\"",
402         "\
403 TOML parse error at line 1, column 6
404   |
405 1 | a = \"
406   |      ^
407 invalid basic string
408 "
409     );
410     bad!(
411         "a = '\n'",
412         "\
413 TOML parse error at line 1, column 6
414   |
415 1 | a = '
416   |      ^
417 invalid literal string
418 "
419     );
420 }
421 
422 #[test]
bad_leading_zeros()423 fn bad_leading_zeros() {
424     bad!(
425         "a = 00",
426         "\
427 TOML parse error at line 1, column 6
428   |
429 1 | a = 00
430   |      ^
431 expected newline, `#`
432 "
433     );
434     bad!(
435         "a = -00",
436         "\
437 TOML parse error at line 1, column 7
438   |
439 1 | a = -00
440   |       ^
441 expected newline, `#`
442 "
443     );
444     bad!(
445         "a = +00",
446         "\
447 TOML parse error at line 1, column 7
448   |
449 1 | a = +00
450   |       ^
451 expected newline, `#`
452 "
453     );
454     bad!(
455         "a = 00.0",
456         "\
457 TOML parse error at line 1, column 6
458   |
459 1 | a = 00.0
460   |      ^
461 expected newline, `#`
462 "
463     );
464     bad!(
465         "a = -00.0",
466         "\
467 TOML parse error at line 1, column 7
468   |
469 1 | a = -00.0
470   |       ^
471 expected newline, `#`
472 "
473     );
474     bad!(
475         "a = +00.0",
476         "\
477 TOML parse error at line 1, column 7
478   |
479 1 | a = +00.0
480   |       ^
481 expected newline, `#`
482 "
483     );
484     bad!(
485         "a = 9223372036854775808",
486         "\
487 TOML parse error at line 1, column 5
488   |
489 1 | a = 9223372036854775808
490   |     ^
491 number too large to fit in target type
492 "
493     );
494     bad!(
495         "a = -9223372036854775809",
496         "\
497 TOML parse error at line 1, column 5
498   |
499 1 | a = -9223372036854775809
500   |     ^
501 number too small to fit in target type
502 "
503     );
504 }
505 
506 #[test]
bad_floats()507 fn bad_floats() {
508     bad!(
509         "a = 0.",
510         "\
511 TOML parse error at line 1, column 7
512   |
513 1 | a = 0.
514   |       ^
515 invalid floating-point number
516 expected digit
517 "
518     );
519     bad!(
520         "a = 0.e",
521         "\
522 TOML parse error at line 1, column 7
523   |
524 1 | a = 0.e
525   |       ^
526 invalid floating-point number
527 expected digit
528 "
529     );
530     bad!(
531         "a = 0.E",
532         "\
533 TOML parse error at line 1, column 7
534   |
535 1 | a = 0.E
536   |       ^
537 invalid floating-point number
538 expected digit
539 "
540     );
541     bad!(
542         "a = 0.0E",
543         "\
544 TOML parse error at line 1, column 9
545   |
546 1 | a = 0.0E
547   |         ^
548 invalid floating-point number
549 "
550     );
551     bad!(
552         "a = 0.0e",
553         "\
554 TOML parse error at line 1, column 9
555   |
556 1 | a = 0.0e
557   |         ^
558 invalid floating-point number
559 "
560     );
561     bad!(
562         "a = 0.0e-",
563         "\
564 TOML parse error at line 1, column 10
565   |
566 1 | a = 0.0e-
567   |          ^
568 invalid floating-point number
569 "
570     );
571     bad!(
572         "a = 0.0e+",
573         "\
574 TOML parse error at line 1, column 10
575   |
576 1 | a = 0.0e+
577   |          ^
578 invalid floating-point number
579 "
580     );
581 }
582 
583 #[test]
floats()584 fn floats() {
585     macro_rules! t {
586         ($actual:expr, $expected:expr) => {{
587             let f = format!("foo = {}", $actual);
588             println!("{}", f);
589             let a = f.parse::<Document>().unwrap();
590             assert_eq!(a["foo"].as_float().unwrap(), $expected);
591         }};
592     }
593 
594     t!("1.0", 1.0);
595     t!("1.0e0", 1.0);
596     t!("1.0e+0", 1.0);
597     t!("1.0e-0", 1.0);
598     t!("1E-0", 1.0);
599     t!("1.001e-0", 1.001);
600     t!("2e10", 2e10);
601     t!("2e+10", 2e10);
602     t!("2e-10", 2e-10);
603     t!("2_0.0", 20.0);
604     t!("2_0.0_0e1_0", 20.0e10);
605     t!("2_0.1_0e1_0", 20.1e10);
606 }
607 
608 #[test]
bare_key_names()609 fn bare_key_names() {
610     let a = "
611         foo = 3
612         foo_3 = 3
613         foo_-2--3--r23f--4-f2-4 = 3
614         _ = 3
615         - = 3
616         8 = 8
617         \"a\" = 3
618         \"!\" = 3
619         \"a^b\" = 3
620         \"\\\"\" = 3
621         \"character encoding\" = \"value\"
622         'ʎǝʞ' = \"value\"
623     "
624     .parse::<Document>()
625     .unwrap();
626     let _ = &a["foo"];
627     let _ = &a["-"];
628     let _ = &a["_"];
629     let _ = &a["8"];
630     let _ = &a["foo_3"];
631     let _ = &a["foo_-2--3--r23f--4-f2-4"];
632     let _ = &a["a"];
633     let _ = &a["!"];
634     let _ = &a["\""];
635     let _ = &a["character encoding"];
636     let _ = &a["ʎǝʞ"];
637 }
638 
639 #[test]
bad_keys()640 fn bad_keys() {
641     bad!(
642         "key\n=3",
643         "\
644 TOML parse error at line 1, column 4
645   |
646 1 | key
647   |    ^
648 expected `.`, `=`
649 "
650     );
651     bad!(
652         "key=\n3",
653         "\
654 TOML parse error at line 1, column 5
655   |
656 1 | key=
657   |     ^
658 invalid string
659 expected `\"`, `'`
660 "
661     );
662     bad!(
663         "key|=3",
664         "\
665 TOML parse error at line 1, column 4
666   |
667 1 | key|=3
668   |    ^
669 expected `.`, `=`
670 "
671     );
672     bad!(
673         "=3",
674         "\
675 TOML parse error at line 1, column 1
676   |
677 1 | =3
678   | ^
679 invalid key
680 "
681     );
682     bad!(
683         "\"\"|=3",
684         "\
685 TOML parse error at line 1, column 3
686   |
687 1 | \"\"|=3
688   |   ^
689 expected `.`, `=`
690 "
691     );
692     bad!(
693         "\"\n\"|=3",
694         "\
695 TOML parse error at line 1, column 2
696   |
697 1 | \"
698   |  ^
699 invalid basic string
700 "
701     );
702     bad!(
703         "\"\r\"|=3",
704         "\
705 TOML parse error at line 1, column 2
706   |
707 1 | \"\r\"|=3
708   |  ^
709 invalid basic string
710 "
711     );
712     bad!(
713         "''''''=3",
714         "\
715 TOML parse error at line 1, column 3
716   |
717 1 | ''''''=3
718   |   ^
719 expected `.`, `=`
720 "
721     );
722     bad!(
723         "\"\"\"\"\"\"=3",
724         "\
725 TOML parse error at line 1, column 3
726   |
727 1 | \"\"\"\"\"\"=3
728   |   ^
729 expected `.`, `=`
730 "
731     );
732     bad!(
733         "'''key'''=3",
734         "\
735 TOML parse error at line 1, column 3
736   |
737 1 | '''key'''=3
738   |   ^
739 expected `.`, `=`
740 "
741     );
742     bad!(
743         "\"\"\"key\"\"\"=3",
744         "\
745 TOML parse error at line 1, column 3
746   |
747 1 | \"\"\"key\"\"\"=3
748   |   ^
749 expected `.`, `=`
750 "
751     );
752 }
753 
754 #[test]
bad_table_names()755 fn bad_table_names() {
756     bad!(
757         "[]",
758         "\
759 TOML parse error at line 1, column 2
760   |
761 1 | []
762   |  ^
763 invalid key
764 "
765     );
766     bad!(
767         "[.]",
768         "\
769 TOML parse error at line 1, column 2
770   |
771 1 | [.]
772   |  ^
773 invalid key
774 "
775     );
776     bad!(
777         "[a.]",
778         "\
779 TOML parse error at line 1, column 3
780   |
781 1 | [a.]
782   |   ^
783 invalid table header
784 expected `.`, `]`
785 "
786     );
787     bad!(
788         "[!]",
789         "\
790 TOML parse error at line 1, column 2
791   |
792 1 | [!]
793   |  ^
794 invalid key
795 "
796     );
797     bad!(
798         "[\"\n\"]",
799         "\
800 TOML parse error at line 1, column 3
801   |
802 1 | [\"
803   |   ^
804 invalid basic string
805 "
806     );
807     bad!(
808         "[a.b]\n[a.\"b\"]",
809         "\
810 TOML parse error at line 2, column 1
811   |
812 2 | [a.\"b\"]
813   | ^
814 invalid table header
815 duplicate key `b` in table `a`
816 "
817     );
818     bad!(
819         "[']",
820         "\
821 TOML parse error at line 1, column 4
822   |
823 1 | [']
824   |    ^
825 invalid literal string
826 "
827     );
828     bad!(
829         "[''']",
830         "\
831 TOML parse error at line 1, column 4
832   |
833 1 | [''']
834   |    ^
835 invalid table header
836 expected `.`, `]`
837 "
838     );
839     bad!(
840         "['''''']",
841         "\
842 TOML parse error at line 1, column 4
843   |
844 1 | ['''''']
845   |    ^
846 invalid table header
847 expected `.`, `]`
848 "
849     );
850     bad!(
851         "['''foo''']",
852         "\
853 TOML parse error at line 1, column 4
854   |
855 1 | ['''foo''']
856   |    ^
857 invalid table header
858 expected `.`, `]`
859 "
860     );
861     bad!(
862         "[\"\"\"bar\"\"\"]",
863         "\
864 TOML parse error at line 1, column 4
865   |
866 1 | [\"\"\"bar\"\"\"]
867   |    ^
868 invalid table header
869 expected `.`, `]`
870 "
871     );
872     bad!(
873         "['\n']",
874         "\
875 TOML parse error at line 1, column 3
876   |
877 1 | ['
878   |   ^
879 invalid literal string
880 "
881     );
882     bad!(
883         "['\r\n']",
884         "\
885 TOML parse error at line 1, column 3
886   |
887 1 | ['
888   |   ^
889 invalid literal string
890 "
891     );
892 }
893 
894 #[test]
table_names()895 fn table_names() {
896     let a = "
897         [a.\"b\"]
898         [\"f f\"]
899         [\"f.f\"]
900         [\"\\\"\"]
901         ['a.a']
902         ['\"\"']
903     "
904     .parse::<Document>()
905     .unwrap();
906     println!("{:?}", a);
907     let _ = &a["a"]["b"];
908     let _ = &a["f f"];
909     let _ = &a["f.f"];
910     let _ = &a["\""];
911     let _ = &a["\"\""];
912 }
913 
914 #[test]
invalid_bare_numeral()915 fn invalid_bare_numeral() {
916     bad!(
917         "4",
918         "\
919 TOML parse error at line 1, column 2
920   |
921 1 | 4
922   |  ^
923 expected `.`, `=`
924 "
925     );
926 }
927 
928 #[test]
inline_tables()929 fn inline_tables() {
930     "a = {}".parse::<Document>().unwrap();
931     "a = {b=1}".parse::<Document>().unwrap();
932     "a = {   b   =   1    }".parse::<Document>().unwrap();
933     "a = {a=1,b=2}".parse::<Document>().unwrap();
934     "a = {a=1,b=2,c={}}".parse::<Document>().unwrap();
935 
936     bad!(
937         "a = {a=1,}",
938         "\
939 TOML parse error at line 1, column 9
940   |
941 1 | a = {a=1,}
942   |         ^
943 invalid inline table
944 expected `}`
945 "
946     );
947     bad!(
948         "a = {,}",
949         "\
950 TOML parse error at line 1, column 6
951   |
952 1 | a = {,}
953   |      ^
954 invalid inline table
955 expected `}`
956 "
957     );
958     bad!(
959         "a = {a=1,a=1}",
960         "\
961 TOML parse error at line 1, column 6
962   |
963 1 | a = {a=1,a=1}
964   |      ^
965 duplicate key `a`
966 "
967     );
968     bad!(
969         "a = {\n}",
970         "\
971 TOML parse error at line 1, column 6
972   |
973 1 | a = {
974   |      ^
975 invalid inline table
976 expected `}`
977 "
978     );
979     bad!(
980         "a = {",
981         "\
982 TOML parse error at line 1, column 6
983   |
984 1 | a = {
985   |      ^
986 invalid inline table
987 expected `}`
988 "
989     );
990 
991     "a = {a=[\n]}".parse::<Document>().unwrap();
992     "a = {\"a\"=[\n]}".parse::<Document>().unwrap();
993     "a = [\n{},\n{},\n]".parse::<Document>().unwrap();
994 }
995 
996 #[test]
number_underscores()997 fn number_underscores() {
998     macro_rules! t {
999         ($actual:expr, $expected:expr) => {{
1000             let f = format!("foo = {}", $actual);
1001             let table = f.parse::<Document>().unwrap();
1002             assert_eq!(table["foo"].as_integer().unwrap(), $expected);
1003         }};
1004     }
1005 
1006     t!("1_0", 10);
1007     t!("1_0_0", 100);
1008     t!("1_000", 1000);
1009     t!("+1_000", 1000);
1010     t!("-1_000", -1000);
1011 }
1012 
1013 #[test]
bad_underscores()1014 fn bad_underscores() {
1015     bad!(
1016         "foo = 0_",
1017         "\
1018 TOML parse error at line 1, column 8
1019   |
1020 1 | foo = 0_
1021   |        ^
1022 expected newline, `#`
1023 "
1024     );
1025     bad!(
1026         "foo = 0__0",
1027         "\
1028 TOML parse error at line 1, column 8
1029   |
1030 1 | foo = 0__0
1031   |        ^
1032 expected newline, `#`
1033 "
1034     );
1035     bad!(
1036         "foo = __0",
1037         "\
1038 TOML parse error at line 1, column 7
1039   |
1040 1 | foo = __0
1041   |       ^
1042 invalid integer
1043 expected leading digit
1044 "
1045     );
1046     bad!(
1047         "foo = 1_0_",
1048         "\
1049 TOML parse error at line 1, column 11
1050   |
1051 1 | foo = 1_0_
1052   |           ^
1053 invalid integer
1054 expected digit
1055 "
1056     );
1057 }
1058 
1059 #[test]
bad_unicode_codepoint()1060 fn bad_unicode_codepoint() {
1061     bad!(
1062         "foo = \"\\uD800\"",
1063         "\
1064 TOML parse error at line 1, column 10
1065   |
1066 1 | foo = \"\\uD800\"
1067   |          ^
1068 invalid unicode 4-digit hex code
1069 value is out of range
1070 "
1071     );
1072 }
1073 
1074 #[test]
bad_strings()1075 fn bad_strings() {
1076     bad!(
1077         "foo = \"\\uxx\"",
1078         "\
1079 TOML parse error at line 1, column 10
1080   |
1081 1 | foo = \"\\uxx\"
1082   |          ^
1083 invalid unicode 4-digit hex code
1084 "
1085     );
1086     bad!(
1087         "foo = \"\\u\"",
1088         "\
1089 TOML parse error at line 1, column 10
1090   |
1091 1 | foo = \"\\u\"
1092   |          ^
1093 invalid unicode 4-digit hex code
1094 "
1095     );
1096     bad!(
1097         "foo = \"\\",
1098         "\
1099 TOML parse error at line 1, column 8
1100   |
1101 1 | foo = \"\\
1102   |        ^
1103 invalid basic string
1104 "
1105     );
1106     bad!(
1107         "foo = '",
1108         "\
1109 TOML parse error at line 1, column 8
1110   |
1111 1 | foo = '
1112   |        ^
1113 invalid literal string
1114 "
1115     );
1116 }
1117 
1118 #[test]
empty_string()1119 fn empty_string() {
1120     assert_eq!(
1121         "foo = \"\"".parse::<Document>().unwrap()["foo"]
1122             .as_str()
1123             .unwrap(),
1124         ""
1125     );
1126 }
1127 
1128 #[test]
booleans()1129 fn booleans() {
1130     let table = "foo = true".parse::<Document>().unwrap();
1131     assert_eq!(table["foo"].as_bool(), Some(true));
1132 
1133     let table = "foo = false".parse::<Document>().unwrap();
1134     assert_eq!(table["foo"].as_bool(), Some(false));
1135 
1136     bad!(
1137         "foo = true2",
1138         "\
1139 TOML parse error at line 1, column 11
1140   |
1141 1 | foo = true2
1142   |           ^
1143 expected newline, `#`
1144 "
1145     );
1146     bad!(
1147         "foo = false2",
1148         "\
1149 TOML parse error at line 1, column 12
1150   |
1151 1 | foo = false2
1152   |            ^
1153 expected newline, `#`
1154 "
1155     );
1156     bad!(
1157         "foo = t1",
1158         "\
1159 TOML parse error at line 1, column 7
1160   |
1161 1 | foo = t1
1162   |       ^
1163 invalid string
1164 expected `\"`, `'`
1165 "
1166     );
1167     bad!(
1168         "foo = f2",
1169         "\
1170 TOML parse error at line 1, column 7
1171   |
1172 1 | foo = f2
1173   |       ^
1174 invalid string
1175 expected `\"`, `'`
1176 "
1177     );
1178 }
1179 
1180 #[test]
bad_nesting()1181 fn bad_nesting() {
1182     bad!(
1183         "
1184         a = [2]
1185         [[a]]
1186         b = 5
1187         ",
1188         "\
1189 TOML parse error at line 3, column 9
1190   |
1191 3 |         [[a]]
1192   |         ^
1193 invalid table header
1194 duplicate key `a` in document root
1195 "
1196     );
1197     bad!(
1198         "
1199         a = 1
1200         [a.b]
1201         ",
1202         "\
1203 TOML parse error at line 3, column 9
1204   |
1205 3 |         [a.b]
1206   |         ^
1207 invalid table header
1208 dotted key `a` attempted to extend non-table type (integer)
1209 "
1210     );
1211     bad!(
1212         "
1213         a = []
1214         [a.b]
1215         ",
1216         "\
1217 TOML parse error at line 3, column 9
1218   |
1219 3 |         [a.b]
1220   |         ^
1221 invalid table header
1222 dotted key `a` attempted to extend non-table type (array)
1223 "
1224     );
1225     bad!(
1226         "
1227         a = []
1228         [[a.b]]
1229         ",
1230         "\
1231 TOML parse error at line 3, column 9
1232   |
1233 3 |         [[a.b]]
1234   |         ^
1235 invalid table header
1236 dotted key `a` attempted to extend non-table type (array)
1237 "
1238     );
1239     bad!(
1240         "
1241         [a]
1242         b = { c = 2, d = {} }
1243         [a.b]
1244         c = 2
1245         ",
1246         "\
1247 TOML parse error at line 4, column 9
1248   |
1249 4 |         [a.b]
1250   |         ^
1251 invalid table header
1252 duplicate key `b` in table `a`
1253 "
1254     );
1255 }
1256 
1257 #[test]
bad_table_redefine()1258 fn bad_table_redefine() {
1259     bad!(
1260         "
1261         [a]
1262         foo=\"bar\"
1263         [a.b]
1264         foo=\"bar\"
1265         [a]
1266         ",
1267         "\
1268 TOML parse error at line 6, column 9
1269   |
1270 6 |         [a]
1271   |         ^
1272 invalid table header
1273 duplicate key `a` in document root
1274 "
1275     );
1276     bad!(
1277         "
1278         [a]
1279         foo=\"bar\"
1280         b = { foo = \"bar\" }
1281         [a]
1282         ",
1283         "\
1284 TOML parse error at line 5, column 9
1285   |
1286 5 |         [a]
1287   |         ^
1288 invalid table header
1289 duplicate key `a` in document root
1290 "
1291     );
1292     bad!(
1293         "
1294         [a]
1295         b = {}
1296         [a.b]
1297         ",
1298         "\
1299 TOML parse error at line 4, column 9
1300   |
1301 4 |         [a.b]
1302   |         ^
1303 invalid table header
1304 duplicate key `b` in table `a`
1305 "
1306     );
1307 
1308     bad!(
1309         "
1310         [a]
1311         b = {}
1312         [a]
1313         ",
1314         "\
1315 TOML parse error at line 4, column 9
1316   |
1317 4 |         [a]
1318   |         ^
1319 invalid table header
1320 duplicate key `a` in document root
1321 "
1322     );
1323 }
1324 
1325 #[test]
datetimes()1326 fn datetimes() {
1327     macro_rules! t {
1328         ($actual:expr) => {{
1329             let f = format!("foo = {}", $actual);
1330             let toml = f.parse::<Document>().expect(&format!("failed: {}", f));
1331             assert_eq!(toml["foo"].as_datetime().unwrap().to_string(), $actual);
1332         }};
1333     }
1334 
1335     t!("2016-09-09T09:09:09Z");
1336     t!("2016-09-09T09:09:09.1Z");
1337     t!("2016-09-09T09:09:09.2+10:00");
1338     t!("2016-09-09T09:09:09.123456789-02:00");
1339     bad!(
1340         "foo = 2016-09-09T09:09:09.Z",
1341         "\
1342 TOML parse error at line 1, column 26
1343   |
1344 1 | foo = 2016-09-09T09:09:09.Z
1345   |                          ^
1346 expected newline, `#`
1347 "
1348     );
1349     bad!(
1350         "foo = 2016-9-09T09:09:09Z",
1351         "\
1352 TOML parse error at line 1, column 12
1353   |
1354 1 | foo = 2016-9-09T09:09:09Z
1355   |            ^
1356 invalid date-time
1357 "
1358     );
1359     bad!(
1360         "foo = 2016-09-09T09:09:09+2:00",
1361         "\
1362 TOML parse error at line 1, column 27
1363   |
1364 1 | foo = 2016-09-09T09:09:09+2:00
1365   |                           ^
1366 invalid time offset
1367 "
1368     );
1369     bad!(
1370         "foo = 2016-09-09T09:09:09-2:00",
1371         "\
1372 TOML parse error at line 1, column 27
1373   |
1374 1 | foo = 2016-09-09T09:09:09-2:00
1375   |                           ^
1376 invalid time offset
1377 "
1378     );
1379     bad!(
1380         "foo = 2016-09-09T09:09:09Z-2:00",
1381         "\
1382 TOML parse error at line 1, column 27
1383   |
1384 1 | foo = 2016-09-09T09:09:09Z-2:00
1385   |                           ^
1386 expected newline, `#`
1387 "
1388     );
1389 }
1390 
1391 #[test]
require_newline_after_value()1392 fn require_newline_after_value() {
1393     bad!(
1394         "0=0r=false",
1395         "\
1396 TOML parse error at line 1, column 4
1397   |
1398 1 | 0=0r=false
1399   |    ^
1400 expected newline, `#`
1401 "
1402     );
1403     bad!(
1404         r#"
1405 0=""o=""m=""r=""00="0"q="""0"""e="""0"""
1406 "#,
1407         r#"TOML parse error at line 2, column 5
1408   |
1409 2 | 0=""o=""m=""r=""00="0"q="""0"""e="""0"""
1410   |     ^
1411 expected newline, `#`
1412 "#
1413     );
1414     bad!(
1415         r#"
1416 [[0000l0]]
1417 0="0"[[0000l0]]
1418 0="0"[[0000l0]]
1419 0="0"l="0"
1420 "#,
1421         r#"TOML parse error at line 3, column 6
1422   |
1423 3 | 0="0"[[0000l0]]
1424   |      ^
1425 expected newline, `#`
1426 "#
1427     );
1428     bad!(
1429         r#"
1430 0=[0]00=[0,0,0]t=["0","0","0"]s=[1000-00-00T00:00:00Z,2000-00-00T00:00:00Z]
1431 "#,
1432         r#"TOML parse error at line 2, column 6
1433   |
1434 2 | 0=[0]00=[0,0,0]t=["0","0","0"]s=[1000-00-00T00:00:00Z,2000-00-00T00:00:00Z]
1435   |      ^
1436 expected newline, `#`
1437 "#
1438     );
1439     bad!(
1440         r#"
1441 0=0r0=0r=false
1442 "#,
1443         r#"TOML parse error at line 2, column 4
1444   |
1445 2 | 0=0r0=0r=false
1446   |    ^
1447 expected newline, `#`
1448 "#
1449     );
1450     bad!(
1451         r#"
1452 0=0r0=0r=falsefal=false
1453 "#,
1454         r#"TOML parse error at line 2, column 4
1455   |
1456 2 | 0=0r0=0r=falsefal=false
1457   |    ^
1458 expected newline, `#`
1459 "#
1460     );
1461 }
1462 
1463 #[test]
dont_use_dotted_key_prefix_on_table_fuzz_57049()1464 fn dont_use_dotted_key_prefix_on_table_fuzz_57049() {
1465     // This could generate
1466     // ```toml
1467     // [
1468     // p.o]
1469     // ```
1470     let input = r#"
1471 p.a=4
1472 [p.o]
1473 "#;
1474     let document = input.parse::<Document>().unwrap();
1475     let actual = document.to_string();
1476     assert_eq(input, actual);
1477 }
1478 
1479 #[test]
despan_keys()1480 fn despan_keys() {
1481     let mut doc = r#"aaaaaa = 1"#.parse::<Document>().unwrap();
1482     let key = "bbb".parse::<Key>().unwrap();
1483     let table = doc.as_table_mut();
1484     table.insert_formatted(
1485         &key,
1486         toml_edit::Item::Value(Value::Integer(toml_edit::Formatted::new(2))),
1487     );
1488 
1489     assert_eq!(doc.to_string(), "aaaaaa = 1\nbbb = 2\n");
1490 }
1491 
1492 #[test]
dotted_key_comment_roundtrip()1493 fn dotted_key_comment_roundtrip() {
1494     let input = r###"
1495 rust.unsafe_op_in_unsafe_fn = "deny"
1496 
1497 rust.explicit_outlives_requirements = "warn"
1498 # rust.unused_crate_dependencies = "warn"
1499 
1500 clippy.cast_lossless = "warn"
1501 clippy.doc_markdown = "warn"
1502 clippy.exhaustive_enums = "warn"
1503 "###;
1504     let expected = input;
1505 
1506     let manifest: toml_edit::Document = input.parse().unwrap();
1507     let actual = manifest.to_string();
1508 
1509     assert_eq(expected, actual);
1510 }
1511