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