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