• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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