1 package toml_test
2
3 import (
4 "bytes"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "math"
9 "strconv"
10 "strings"
11 "testing"
12 "time"
13
14 "github.com/pelletier/go-toml/v2"
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17 )
18
19 type unmarshalTextKey struct {
20 A string
21 B string
22 }
23
24 func (k *unmarshalTextKey) UnmarshalText(text []byte) error {
25 parts := strings.Split(string(text), "-")
26 if len(parts) != 2 {
27 return fmt.Errorf("invalid text key: %s", text)
28 }
29 k.A = parts[0]
30 k.B = parts[1]
31 return nil
32 }
33
34 type unmarshalBadTextKey struct{}
35
36 func (k *unmarshalBadTextKey) UnmarshalText(text []byte) error {
37 return fmt.Errorf("error")
38 }
39
40 func ExampleDecoder_DisallowUnknownFields() {
41 type S struct {
42 Key1 string
43 Key3 string
44 }
45 doc := `
46 key1 = "value1"
47 key2 = "value2"
48 key3 = "value3"
49 `
50 r := strings.NewReader(doc)
51 d := toml.NewDecoder(r)
52 d.DisallowUnknownFields()
53 s := S{}
54 err := d.Decode(&s)
55
56 fmt.Println(err.Error())
57
58 var details *toml.StrictMissingError
59 if !errors.As(err, &details) {
60 panic(fmt.Sprintf("err should have been a *toml.StrictMissingError, but got %s (%T)", err, err))
61 }
62
63 fmt.Println(details.String())
64
65
66
67
68
69
70 }
71
72 func ExampleUnmarshal() {
73 type MyConfig struct {
74 Version int
75 Name string
76 Tags []string
77 }
78
79 doc := `
80 version = 2
81 name = "go-toml"
82 tags = ["go", "toml"]
83 `
84
85 var cfg MyConfig
86 err := toml.Unmarshal([]byte(doc), &cfg)
87 if err != nil {
88 panic(err)
89 }
90 fmt.Println("version:", cfg.Version)
91 fmt.Println("name:", cfg.Name)
92 fmt.Println("tags:", cfg.Tags)
93
94
95
96
97 }
98
99 type badReader struct{}
100
101 func (r *badReader) Read([]byte) (int, error) {
102 return 0, fmt.Errorf("testing error")
103 }
104
105 func TestDecodeReaderError(t *testing.T) {
106 r := &badReader{}
107
108 dec := toml.NewDecoder(r)
109 m := map[string]interface{}{}
110 err := dec.Decode(&m)
111 require.Error(t, err)
112 }
113
114
115 func TestUnmarshal_Integers(t *testing.T) {
116 examples := []struct {
117 desc string
118 input string
119 expected int64
120 err bool
121 }{
122 {
123 desc: "integer just digits",
124 input: `1234`,
125 expected: 1234,
126 },
127 {
128 desc: "integer zero",
129 input: `0`,
130 expected: 0,
131 },
132 {
133 desc: "integer sign",
134 input: `+99`,
135 expected: 99,
136 },
137 {
138 desc: "integer decimal underscore",
139 input: `123_456`,
140 expected: 123456,
141 },
142 {
143 desc: "integer hex uppercase",
144 input: `0xDEADBEEF`,
145 expected: 0xDEADBEEF,
146 },
147 {
148 desc: "integer hex lowercase",
149 input: `0xdead_beef`,
150 expected: 0xDEADBEEF,
151 },
152 {
153 desc: "integer octal",
154 input: `0o01234567`,
155 expected: 0o01234567,
156 },
157 {
158 desc: "integer binary",
159 input: `0b11010110`,
160 expected: 0b11010110,
161 },
162 {
163 desc: "double underscore",
164 input: "12__3",
165 err: true,
166 },
167 {
168 desc: "starts with underscore",
169 input: "_1",
170 err: true,
171 },
172 {
173 desc: "ends with underscore",
174 input: "1_",
175 err: true,
176 },
177 }
178
179 type doc struct {
180 A int64
181 }
182
183 for _, e := range examples {
184 e := e
185 t.Run(e.desc, func(t *testing.T) {
186 doc := doc{}
187 err := toml.Unmarshal([]byte(`A = `+e.input), &doc)
188 if e.err {
189 require.Error(t, err)
190 } else {
191 require.NoError(t, err)
192 assert.Equal(t, e.expected, doc.A)
193 }
194 })
195 }
196 }
197
198
199 func TestUnmarshal_Floats(t *testing.T) {
200 examples := []struct {
201 desc string
202 input string
203 expected float64
204 testFn func(t *testing.T, v float64)
205 err bool
206 }{
207
208 {
209 desc: "float pi",
210 input: `3.1415`,
211 expected: 3.1415,
212 },
213 {
214 desc: "float negative",
215 input: `-0.01`,
216 expected: -0.01,
217 },
218 {
219 desc: "float signed exponent",
220 input: `5e+22`,
221 expected: 5e+22,
222 },
223 {
224 desc: "float exponent lowercase",
225 input: `1e06`,
226 expected: 1e06,
227 },
228 {
229 desc: "float exponent uppercase",
230 input: `-2E-2`,
231 expected: -2e-2,
232 },
233 {
234 desc: "float exponent zero",
235 input: `0e0`,
236 expected: 0.0,
237 },
238 {
239 desc: "float upper exponent zero",
240 input: `0E0`,
241 expected: 0.0,
242 },
243 {
244 desc: "float zero without decimals",
245 input: `0`,
246 expected: 0.0,
247 },
248 {
249 desc: "float fractional with exponent",
250 input: `6.626e-34`,
251 expected: 6.626e-34,
252 },
253 {
254 desc: "float underscores",
255 input: `224_617.445_991_228`,
256 expected: 224_617.445_991_228,
257 },
258 {
259 desc: "inf",
260 input: `inf`,
261 expected: math.Inf(+1),
262 },
263 {
264 desc: "inf negative",
265 input: `-inf`,
266 expected: math.Inf(-1),
267 },
268 {
269 desc: "inf positive",
270 input: `+inf`,
271 expected: math.Inf(+1),
272 },
273 {
274 desc: "nan",
275 input: `nan`,
276 testFn: func(t *testing.T, v float64) {
277 t.Helper()
278 assert.True(t, math.IsNaN(v))
279 },
280 },
281 {
282 desc: "nan negative",
283 input: `-nan`,
284 testFn: func(t *testing.T, v float64) {
285 t.Helper()
286 assert.True(t, math.IsNaN(v))
287 },
288 },
289 {
290 desc: "nan positive",
291 input: `+nan`,
292 testFn: func(t *testing.T, v float64) {
293 t.Helper()
294 assert.True(t, math.IsNaN(v))
295 },
296 },
297 {
298 desc: "underscore after integer part",
299 input: `1_e2`,
300 err: true,
301 },
302 {
303 desc: "underscore after integer part",
304 input: `1.0_e2`,
305 err: true,
306 },
307 {
308 desc: "leading zero in positive float",
309 input: `+0_0.0`,
310 err: true,
311 },
312 }
313
314 type doc struct {
315 A float64
316 }
317
318 for _, e := range examples {
319 e := e
320 t.Run(e.desc, func(t *testing.T) {
321 doc := doc{}
322 err := toml.Unmarshal([]byte(`A = `+e.input), &doc)
323 if e.err {
324 require.Error(t, err)
325 } else {
326 require.NoError(t, err)
327 if e.testFn != nil {
328 e.testFn(t, doc.A)
329 } else {
330 assert.Equal(t, e.expected, doc.A)
331 }
332 }
333 })
334 }
335 }
336
337
338 func TestUnmarshal(t *testing.T) {
339 type test struct {
340 target interface{}
341 expected interface{}
342 err bool
343 assert func(t *testing.T, test test)
344 }
345 examples := []struct {
346 skip bool
347 desc string
348 input string
349 gen func() test
350 }{
351 {
352 desc: "kv string",
353 input: `A = "foo"`,
354 gen: func() test {
355 type doc struct {
356 A string
357 }
358
359 return test{
360 target: &doc{},
361 expected: &doc{A: "foo"},
362 }
363 },
364 },
365 {
366 desc: "kv literal string",
367 input: `A = 'foo 🙂 '`,
368 gen: func() test {
369 type doc struct {
370 A string
371 }
372
373 return test{
374 target: &doc{},
375 expected: &doc{A: "foo 🙂 "},
376 }
377 },
378 },
379 {
380 desc: "kv text key",
381 input: `a-1 = "foo"`,
382 gen: func() test {
383 type doc = map[unmarshalTextKey]string
384
385 return test{
386 target: &doc{},
387 expected: &doc{{A: "a", B: "1"}: "foo"},
388 }
389 },
390 },
391 {
392 desc: "table text key",
393 input: `["a-1"]
394 foo = "bar"`,
395 gen: func() test {
396 type doc = map[unmarshalTextKey]map[string]string
397
398 return test{
399 target: &doc{},
400 expected: &doc{{A: "a", B: "1"}: map[string]string{"foo": "bar"}},
401 }
402 },
403 },
404 {
405 desc: "kv ptr text key",
406 input: `a-1 = "foo"`,
407 gen: func() test {
408 type doc = map[*unmarshalTextKey]string
409
410 return test{
411 target: &doc{},
412 expected: &doc{{A: "a", B: "1"}: "foo"},
413 assert: func(t *testing.T, test test) {
414
415
416
417
418
419 expected := make(map[unmarshalTextKey]string)
420 for k, v := range *(test.expected.(*doc)) {
421 expected[*k] = v
422 }
423 got := make(map[unmarshalTextKey]string)
424 for k, v := range *(test.target.(*doc)) {
425 got[*k] = v
426 }
427 assert.Equal(t, expected, got)
428 },
429 }
430 },
431 },
432 {
433 desc: "kv bad text key",
434 input: `a-1 = "foo"`,
435 gen: func() test {
436 type doc = map[unmarshalBadTextKey]string
437
438 return test{
439 target: &doc{},
440 err: true,
441 }
442 },
443 },
444 {
445 desc: "kv bad ptr text key",
446 input: `a-1 = "foo"`,
447 gen: func() test {
448 type doc = map[*unmarshalBadTextKey]string
449
450 return test{
451 target: &doc{},
452 err: true,
453 }
454 },
455 },
456 {
457 desc: "table bad text key",
458 input: `["a-1"]
459 foo = "bar"`,
460 gen: func() test {
461 type doc = map[unmarshalBadTextKey]map[string]string
462
463 return test{
464 target: &doc{},
465 err: true,
466 }
467 },
468 },
469 {
470 desc: "time.time with negative zone",
471 input: `a = 1979-05-27T00:32:00-07:00 `,
472 gen: func() test {
473 var v map[string]time.Time
474
475 return test{
476 target: &v,
477 expected: &map[string]time.Time{
478 "a": time.Date(1979, 5, 27, 0, 32, 0, 0, time.FixedZone("", -7*3600)),
479 },
480 }
481 },
482 },
483 {
484 desc: "time.time with positive zone",
485 input: `a = 1979-05-27T00:32:00+07:00`,
486 gen: func() test {
487 var v map[string]time.Time
488
489 return test{
490 target: &v,
491 expected: &map[string]time.Time{
492 "a": time.Date(1979, 5, 27, 0, 32, 0, 0, time.FixedZone("", 7*3600)),
493 },
494 }
495 },
496 },
497 {
498 desc: "time.time with zone and fractional",
499 input: `a = 1979-05-27T00:32:00.999999-07:00`,
500 gen: func() test {
501 var v map[string]time.Time
502
503 return test{
504 target: &v,
505 expected: &map[string]time.Time{
506 "a": time.Date(1979, 5, 27, 0, 32, 0, 999999000, time.FixedZone("", -7*3600)),
507 },
508 }
509 },
510 },
511 {
512 desc: "local datetime into time.Time",
513 input: `a = 1979-05-27T00:32:00`,
514 gen: func() test {
515 type doc struct {
516 A time.Time
517 }
518
519 return test{
520 target: &doc{},
521 expected: &doc{
522 A: time.Date(1979, 5, 27, 0, 32, 0, 0, time.Local),
523 },
524 }
525 },
526 },
527 {
528 desc: "local datetime into interface",
529 input: `a = 1979-05-27T00:32:00`,
530 gen: func() test {
531 type doc struct {
532 A interface{}
533 }
534
535 return test{
536 target: &doc{},
537 expected: &doc{
538 A: toml.LocalDateTime{
539 toml.LocalDate{1979, 5, 27},
540 toml.LocalTime{0, 32, 0, 0, 0},
541 },
542 },
543 }
544 },
545 },
546 {
547 desc: "local date into interface",
548 input: `a = 1979-05-27`,
549 gen: func() test {
550 type doc struct {
551 A interface{}
552 }
553
554 return test{
555 target: &doc{},
556 expected: &doc{
557 A: toml.LocalDate{1979, 5, 27},
558 },
559 }
560 },
561 },
562 {
563 desc: "local leap-day date into interface",
564 input: `a = 2020-02-29`,
565 gen: func() test {
566 type doc struct {
567 A interface{}
568 }
569
570 return test{
571 target: &doc{},
572 expected: &doc{
573 A: toml.LocalDate{2020, 2, 29},
574 },
575 }
576 },
577 },
578 {
579 desc: "local-time with nano second",
580 input: `a = 12:08:05.666666666`,
581 gen: func() test {
582 var v map[string]interface{}
583
584 return test{
585 target: &v,
586 expected: &map[string]interface{}{
587 "a": toml.LocalTime{Hour: 12, Minute: 8, Second: 5, Nanosecond: 666666666, Precision: 9},
588 },
589 }
590 },
591 },
592 {
593 desc: "local-time",
594 input: `a = 12:08:05`,
595 gen: func() test {
596 var v map[string]interface{}
597
598 return test{
599 target: &v,
600 expected: &map[string]interface{}{
601 "a": toml.LocalTime{Hour: 12, Minute: 8, Second: 5},
602 },
603 }
604 },
605 },
606 {
607 desc: "local-time missing digit",
608 input: `a = 12:08:0`,
609 gen: func() test {
610 var v map[string]interface{}
611
612 return test{
613 target: &v,
614 err: true,
615 }
616 },
617 },
618 {
619 desc: "local-time extra digit",
620 input: `a = 12:08:000`,
621 gen: func() test {
622 var v map[string]interface{}
623
624 return test{
625 target: &v,
626 err: true,
627 }
628 },
629 },
630 {
631 desc: "issue 475 - space between dots in key",
632 input: `fruit. color = "yellow"
633 fruit . flavor = "banana"`,
634 gen: func() test {
635 m := map[string]interface{}{}
636
637 return test{
638 target: &m,
639 expected: &map[string]interface{}{
640 "fruit": map[string]interface{}{
641 "color": "yellow",
642 "flavor": "banana",
643 },
644 },
645 }
646 },
647 },
648 {
649 desc: "issue 427 - quotation marks in key",
650 input: `'"a"' = 1
651 "\"b\"" = 2`,
652 gen: func() test {
653 m := map[string]interface{}{}
654
655 return test{
656 target: &m,
657 expected: &map[string]interface{}{
658 `"a"`: int64(1),
659 `"b"`: int64(2),
660 },
661 }
662 },
663 },
664 {
665 desc: "issue 739 - table redefinition",
666 input: `
667 [foo.bar.baz]
668 wibble = 'wobble'
669
670 [foo]
671
672 [foo.bar]
673 huey = 'dewey'
674 `,
675 gen: func() test {
676 m := map[string]interface{}{}
677
678 return test{
679 target: &m,
680 expected: &map[string]interface{}{
681 `foo`: map[string]interface{}{
682 "bar": map[string]interface{}{
683 "huey": "dewey",
684 "baz": map[string]interface{}{
685 "wibble": "wobble",
686 },
687 },
688 },
689 },
690 }
691 },
692 },
693 {
694 desc: "multiline basic string",
695 input: `A = """\
696 Test"""`,
697 gen: func() test {
698 type doc struct {
699 A string
700 }
701
702 return test{
703 target: &doc{},
704 expected: &doc{A: "Test"},
705 }
706 },
707 },
708 {
709 desc: "multiline literal string with windows newline",
710 input: "A = '''\r\nTest'''",
711 gen: func() test {
712 type doc struct {
713 A string
714 }
715
716 return test{
717 target: &doc{},
718 expected: &doc{A: "Test"},
719 }
720 },
721 },
722 {
723 desc: "multiline basic string with windows newline",
724 input: "A = \"\"\"\r\nTe\r\nst\"\"\"",
725 gen: func() test {
726 type doc struct {
727 A string
728 }
729
730 return test{
731 target: &doc{},
732 expected: &doc{A: "Te\r\nst"},
733 }
734 },
735 },
736 {
737 desc: "multiline basic string escapes",
738 input: `A = """
739 \\\b\f\n\r\t\uffff\U0001D11E"""`,
740 gen: func() test {
741 type doc struct {
742 A string
743 }
744
745 return test{
746 target: &doc{},
747 expected: &doc{A: "\\\b\f\n\r\t\uffff\U0001D11E"},
748 }
749 },
750 },
751 {
752 desc: "basic string escapes",
753 input: `A = "\\\b\f\n\r\t\uffff\U0001D11E"`,
754 gen: func() test {
755 type doc struct {
756 A string
757 }
758
759 return test{
760 target: &doc{},
761 expected: &doc{A: "\\\b\f\n\r\t\uffff\U0001D11E"},
762 }
763 },
764 },
765 {
766 desc: "spaces around dotted keys",
767 input: "a . b = 1",
768 gen: func() test {
769 return test{
770 target: &map[string]map[string]interface{}{},
771 expected: &map[string]map[string]interface{}{"a": {"b": int64(1)}},
772 }
773 },
774 },
775 {
776 desc: "kv bool true",
777 input: `A = true`,
778 gen: func() test {
779 type doc struct {
780 A bool
781 }
782
783 return test{
784 target: &doc{},
785 expected: &doc{A: true},
786 }
787 },
788 },
789 {
790 desc: "kv bool false",
791 input: `A = false`,
792 gen: func() test {
793 type doc struct {
794 A bool
795 }
796
797 return test{
798 target: &doc{A: true},
799 expected: &doc{A: false},
800 }
801 },
802 },
803 {
804 desc: "string array",
805 input: `A = ["foo", "bar"]`,
806 gen: func() test {
807 type doc struct {
808 A []string
809 }
810
811 return test{
812 target: &doc{},
813 expected: &doc{A: []string{"foo", "bar"}},
814 }
815 },
816 },
817 {
818 desc: "long string array into []string",
819 input: `A = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17"]`,
820 gen: func() test {
821 type doc struct {
822 A []string
823 }
824
825 return test{
826 target: &doc{},
827 expected: &doc{A: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17"}},
828 }
829 },
830 },
831 {
832 desc: "long string array into []interface{}",
833 input: `A = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14",
834 "15","16","17"]`,
835 gen: func() test {
836 type doc struct {
837 A []interface{}
838 }
839
840 return test{
841 target: &doc{},
842 expected: &doc{A: []interface{}{"0", "1", "2", "3", "4", "5", "6",
843 "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17"}},
844 }
845 },
846 },
847 {
848 desc: "standard table",
849 input: `[A]
850 B = "data"`,
851 gen: func() test {
852 type A struct {
853 B string
854 }
855 type doc struct {
856 A A
857 }
858
859 return test{
860 target: &doc{},
861 expected: &doc{A: A{B: "data"}},
862 }
863 },
864 },
865 {
866 desc: "standard empty table",
867 input: `[A]`,
868 gen: func() test {
869 var v map[string]interface{}
870
871 return test{
872 target: &v,
873 expected: &map[string]interface{}{`A`: map[string]interface{}{}},
874 }
875 },
876 },
877 {
878 desc: "inline table",
879 input: `Name = {First = "hello", Last = "world"}`,
880 gen: func() test {
881 type name struct {
882 First string
883 Last string
884 }
885 type doc struct {
886 Name name
887 }
888
889 return test{
890 target: &doc{},
891 expected: &doc{Name: name{
892 First: "hello",
893 Last: "world",
894 }},
895 }
896 },
897 },
898 {
899 desc: "inline empty table",
900 input: `A = {}`,
901 gen: func() test {
902 var v map[string]interface{}
903
904 return test{
905 target: &v,
906 expected: &map[string]interface{}{`A`: map[string]interface{}{}},
907 }
908 },
909 },
910 {
911 desc: "inline table inside array",
912 input: `Names = [{First = "hello", Last = "world"}, {First = "ab", Last = "cd"}]`,
913 gen: func() test {
914 type name struct {
915 First string
916 Last string
917 }
918 type doc struct {
919 Names []name
920 }
921
922 return test{
923 target: &doc{},
924 expected: &doc{
925 Names: []name{
926 {
927 First: "hello",
928 Last: "world",
929 },
930 {
931 First: "ab",
932 Last: "cd",
933 },
934 },
935 },
936 }
937 },
938 },
939 {
940 desc: "into map[string]interface{}",
941 input: `A = "foo"`,
942 gen: func() test {
943 doc := map[string]interface{}{}
944
945 return test{
946 target: &doc,
947 expected: &map[string]interface{}{
948 "A": "foo",
949 },
950 }
951 },
952 },
953 {
954 desc: "multi keys of different types into map[string]interface{}",
955 input: `A = "foo"
956 B = 42`,
957 gen: func() test {
958 doc := map[string]interface{}{}
959
960 return test{
961 target: &doc,
962 expected: &map[string]interface{}{
963 "A": "foo",
964 "B": int64(42),
965 },
966 }
967 },
968 },
969 {
970 desc: "slice in a map[string]interface{}",
971 input: `A = ["foo", "bar"]`,
972 gen: func() test {
973 doc := map[string]interface{}{}
974
975 return test{
976 target: &doc,
977 expected: &map[string]interface{}{
978 "A": []interface{}{"foo", "bar"},
979 },
980 }
981 },
982 },
983 {
984 desc: "string into map[string]string",
985 input: `A = "foo"`,
986 gen: func() test {
987 doc := map[string]string{}
988
989 return test{
990 target: &doc,
991 expected: &map[string]string{
992 "A": "foo",
993 },
994 }
995 },
996 },
997 {
998 desc: "float64 into map[string]string",
999 input: `A = 42.0`,
1000 gen: func() test {
1001 doc := map[string]string{}
1002
1003 return test{
1004 target: &doc,
1005 err: true,
1006 }
1007 },
1008 },
1009 {
1010 desc: "one-level one-element array table",
1011 input: `[[First]]
1012 Second = "hello"`,
1013 gen: func() test {
1014 type First struct {
1015 Second string
1016 }
1017 type Doc struct {
1018 First []First
1019 }
1020
1021 return test{
1022 target: &Doc{},
1023 expected: &Doc{
1024 First: []First{
1025 {
1026 Second: "hello",
1027 },
1028 },
1029 },
1030 }
1031 },
1032 },
1033 {
1034 desc: "one-level multi-element array table",
1035 input: `[[Products]]
1036 Name = "Hammer"
1037 Sku = 738594937
1038
1039 [[Products]] # empty table within the array
1040
1041 [[Products]]
1042 Name = "Nail"
1043 Sku = 284758393
1044
1045 Color = "gray"`,
1046 gen: func() test {
1047 type Product struct {
1048 Name string
1049 Sku int64
1050 Color string
1051 }
1052 type Doc struct {
1053 Products []Product
1054 }
1055
1056 return test{
1057 target: &Doc{},
1058 expected: &Doc{
1059 Products: []Product{
1060 {Name: "Hammer", Sku: 738594937},
1061 {},
1062 {Name: "Nail", Sku: 284758393, Color: "gray"},
1063 },
1064 },
1065 }
1066 },
1067 },
1068 {
1069 desc: "one-level multi-element array table to map",
1070 input: `[[Products]]
1071 Name = "Hammer"
1072 Sku = 738594937
1073
1074 [[Products]] # empty table within the array
1075
1076 [[Products]]
1077 Name = "Nail"
1078 Sku = 284758393
1079
1080 Color = "gray"`,
1081 gen: func() test {
1082 return test{
1083 target: &map[string]interface{}{},
1084 expected: &map[string]interface{}{
1085 "Products": []interface{}{
1086 map[string]interface{}{
1087 "Name": "Hammer",
1088 "Sku": int64(738594937),
1089 },
1090 map[string]interface{}{},
1091 map[string]interface{}{
1092 "Name": "Nail",
1093 "Sku": int64(284758393),
1094 "Color": "gray",
1095 },
1096 },
1097 },
1098 }
1099 },
1100 },
1101 {
1102 desc: "sub-table in array table",
1103 input: `[[Fruits]]
1104 Name = "apple"
1105
1106 [Fruits.Physical] # subtable
1107 Color = "red"
1108 Shape = "round"`,
1109 gen: func() test {
1110 return test{
1111 target: &map[string]interface{}{},
1112 expected: &map[string]interface{}{
1113 "Fruits": []interface{}{
1114 map[string]interface{}{
1115 "Name": "apple",
1116 "Physical": map[string]interface{}{
1117 "Color": "red",
1118 "Shape": "round",
1119 },
1120 },
1121 },
1122 },
1123 }
1124 },
1125 },
1126 {
1127 desc: "multiple sub-table in array tables",
1128 input: `[[Fruits]]
1129 Name = "apple"
1130
1131 [[Fruits.Varieties]] # nested array of tables
1132 Name = "red delicious"
1133
1134 [[Fruits.Varieties]]
1135 Name = "granny smith"
1136
1137 [[Fruits]]
1138 Name = "banana"
1139
1140 [[Fruits.Varieties]]
1141 Name = "plantain"`,
1142 gen: func() test {
1143 return test{
1144 target: &map[string]interface{}{},
1145 expected: &map[string]interface{}{
1146 "Fruits": []interface{}{
1147 map[string]interface{}{
1148 "Name": "apple",
1149 "Varieties": []interface{}{
1150 map[string]interface{}{
1151 "Name": "red delicious",
1152 },
1153 map[string]interface{}{
1154 "Name": "granny smith",
1155 },
1156 },
1157 },
1158 map[string]interface{}{
1159 "Name": "banana",
1160 "Varieties": []interface{}{
1161 map[string]interface{}{
1162 "Name": "plantain",
1163 },
1164 },
1165 },
1166 },
1167 },
1168 }
1169 },
1170 },
1171 {
1172 desc: "multiple sub-table in array tables into structs",
1173 input: `[[Fruits]]
1174 Name = "apple"
1175
1176 [[Fruits.Varieties]] # nested array of tables
1177 Name = "red delicious"
1178
1179 [[Fruits.Varieties]]
1180 Name = "granny smith"
1181
1182 [[Fruits]]
1183 Name = "banana"
1184
1185 [[Fruits.Varieties]]
1186 Name = "plantain"`,
1187 gen: func() test {
1188 type Variety struct {
1189 Name string
1190 }
1191 type Fruit struct {
1192 Name string
1193 Varieties []Variety
1194 }
1195 type doc struct {
1196 Fruits []Fruit
1197 }
1198
1199 return test{
1200 target: &doc{},
1201 expected: &doc{
1202 Fruits: []Fruit{
1203 {
1204 Name: "apple",
1205 Varieties: []Variety{
1206 {Name: "red delicious"},
1207 {Name: "granny smith"},
1208 },
1209 },
1210 {
1211 Name: "banana",
1212 Varieties: []Variety{
1213 {Name: "plantain"},
1214 },
1215 },
1216 },
1217 },
1218 }
1219 },
1220 },
1221 {
1222 desc: "array table into interface in struct",
1223 input: `[[foo]]
1224 bar = "hello"`,
1225 gen: func() test {
1226 type doc struct {
1227 Foo interface{}
1228 }
1229 return test{
1230 target: &doc{},
1231 expected: &doc{
1232 Foo: []interface{}{
1233 map[string]interface{}{
1234 "bar": "hello",
1235 },
1236 },
1237 },
1238 }
1239 },
1240 },
1241 {
1242 desc: "array table into interface in struct already initialized with right type",
1243 input: `[[foo]]
1244 bar = "hello"`,
1245 gen: func() test {
1246 type doc struct {
1247 Foo interface{}
1248 }
1249 return test{
1250 target: &doc{
1251 Foo: []interface{}{},
1252 },
1253 expected: &doc{
1254 Foo: []interface{}{
1255 map[string]interface{}{
1256 "bar": "hello",
1257 },
1258 },
1259 },
1260 }
1261 },
1262 },
1263 {
1264 desc: "array table into interface in struct already initialized with wrong type",
1265 input: `[[foo]]
1266 bar = "hello"`,
1267 gen: func() test {
1268 type doc struct {
1269 Foo interface{}
1270 }
1271 return test{
1272 target: &doc{
1273 Foo: []string{},
1274 },
1275 expected: &doc{
1276 Foo: []interface{}{
1277 map[string]interface{}{
1278 "bar": "hello",
1279 },
1280 },
1281 },
1282 }
1283 },
1284 },
1285 {
1286 desc: "array table into maps with pointer on last key",
1287 input: `[[foo]]
1288 bar = "hello"`,
1289 gen: func() test {
1290 type doc struct {
1291 Foo **[]interface{}
1292 }
1293 x := &[]interface{}{
1294 map[string]interface{}{
1295 "bar": "hello",
1296 },
1297 }
1298 return test{
1299 target: &doc{},
1300 expected: &doc{
1301 Foo: &x,
1302 },
1303 }
1304 },
1305 },
1306 {
1307 desc: "array table into maps with pointer on intermediate key",
1308 input: `[[foo.foo2]]
1309 bar = "hello"`,
1310 gen: func() test {
1311 type doc struct {
1312 Foo **map[string]interface{}
1313 }
1314 x := &map[string]interface{}{
1315 "foo2": []interface{}{
1316 map[string]interface{}{
1317 "bar": "hello",
1318 },
1319 },
1320 }
1321 return test{
1322 target: &doc{},
1323 expected: &doc{
1324 Foo: &x,
1325 },
1326 }
1327 },
1328 },
1329 {
1330 desc: "array table into maps with pointer on last key with invalid leaf type",
1331 input: `[[foo]]
1332 bar = "hello"`,
1333 gen: func() test {
1334 type doc struct {
1335 Foo **[]map[string]int
1336 }
1337 return test{
1338 target: &doc{},
1339 err: true,
1340 }
1341 },
1342 },
1343 {
1344 desc: "unexported struct fields are ignored",
1345 input: `foo = "bar"`,
1346 gen: func() test {
1347 type doc struct {
1348 foo string
1349 }
1350 return test{
1351 target: &doc{},
1352 expected: &doc{},
1353 }
1354 },
1355 },
1356 {
1357 desc: "array table into nil ptr",
1358 input: `[[foo]]
1359 bar = "hello"`,
1360 gen: func() test {
1361 type doc struct {
1362 Foo *[]interface{}
1363 }
1364 return test{
1365 target: &doc{},
1366 expected: &doc{
1367 Foo: &[]interface{}{
1368 map[string]interface{}{
1369 "bar": "hello",
1370 },
1371 },
1372 },
1373 }
1374 },
1375 },
1376 {
1377 desc: "array table into nil ptr of invalid type",
1378 input: `[[foo]]
1379 bar = "hello"`,
1380 gen: func() test {
1381 type doc struct {
1382 Foo *string
1383 }
1384 return test{
1385 target: &doc{},
1386 err: true,
1387 }
1388 },
1389 },
1390 {
1391 desc: "array table with intermediate ptr",
1392 input: `[[foo.bar]]
1393 bar = "hello"`,
1394 gen: func() test {
1395 type doc struct {
1396 Foo *map[string]interface{}
1397 }
1398 return test{
1399 target: &doc{},
1400 expected: &doc{
1401 Foo: &map[string]interface{}{
1402 "bar": []interface{}{
1403 map[string]interface{}{
1404 "bar": "hello",
1405 },
1406 },
1407 },
1408 },
1409 }
1410 },
1411 },
1412 {
1413 desc: "unmarshal array into interface that contains a slice",
1414 input: `a = [1,2,3]`,
1415 gen: func() test {
1416 type doc struct {
1417 A interface{}
1418 }
1419 return test{
1420 target: &doc{
1421 A: []string{},
1422 },
1423 expected: &doc{
1424 A: []interface{}{
1425 int64(1),
1426 int64(2),
1427 int64(3),
1428 },
1429 },
1430 }
1431 },
1432 },
1433 {
1434 desc: "unmarshal array into interface that contains a []interface{}",
1435 input: `a = [1,2,3]`,
1436 gen: func() test {
1437 type doc struct {
1438 A interface{}
1439 }
1440 return test{
1441 target: &doc{
1442 A: []interface{}{},
1443 },
1444 expected: &doc{
1445 A: []interface{}{
1446 int64(1),
1447 int64(2),
1448 int64(3),
1449 },
1450 },
1451 }
1452 },
1453 },
1454 {
1455 desc: "unmarshal key into map with existing value",
1456 input: `a = "new"`,
1457 gen: func() test {
1458 return test{
1459 target: &map[string]interface{}{"a": "old"},
1460 expected: &map[string]interface{}{"a": "new"},
1461 }
1462 },
1463 },
1464 {
1465 desc: "unmarshal key into map with existing value",
1466 input: `a.b = "new"`,
1467 gen: func() test {
1468 type doc struct {
1469 A interface{}
1470 }
1471 return test{
1472 target: &doc{},
1473 expected: &doc{
1474 A: map[string]interface{}{
1475 "b": "new",
1476 },
1477 },
1478 }
1479 },
1480 },
1481 {
1482 desc: "unmarshal array into struct field with existing array",
1483 input: `a = [1,2]`,
1484 gen: func() test {
1485 type doc struct {
1486 A []int
1487 }
1488 return test{
1489 target: &doc{},
1490 expected: &doc{
1491 A: []int{1, 2},
1492 },
1493 }
1494 },
1495 },
1496 {
1497 desc: "unmarshal inline table into map",
1498 input: `a = {b="hello"}`,
1499 gen: func() test {
1500 type doc struct {
1501 A map[string]interface{}
1502 }
1503 return test{
1504 target: &doc{},
1505 expected: &doc{
1506 A: map[string]interface{}{
1507 "b": "hello",
1508 },
1509 },
1510 }
1511 },
1512 },
1513 {
1514 desc: "unmarshal inline table into map of incorrect type",
1515 input: `a = {b="hello"}`,
1516 gen: func() test {
1517 type doc struct {
1518 A map[string]int
1519 }
1520 return test{
1521 target: &doc{},
1522 err: true,
1523 }
1524 },
1525 },
1526 {
1527 desc: "slice pointer in slice pointer",
1528 input: `A = ["Hello"]`,
1529 gen: func() test {
1530 type doc struct {
1531 A *[]*string
1532 }
1533 hello := "Hello"
1534
1535 return test{
1536 target: &doc{},
1537 expected: &doc{
1538 A: &[]*string{&hello},
1539 },
1540 }
1541 },
1542 },
1543 {
1544 desc: "interface holding a string",
1545 input: `A = "Hello"`,
1546 gen: func() test {
1547 type doc struct {
1548 A interface{}
1549 }
1550 return test{
1551 target: &doc{},
1552 expected: &doc{
1553 A: "Hello",
1554 },
1555 }
1556 },
1557 },
1558 {
1559 desc: "map of bools",
1560 input: `A = true`,
1561 gen: func() test {
1562 return test{
1563 target: &map[string]bool{},
1564 expected: &map[string]bool{"A": true},
1565 }
1566 },
1567 },
1568 {
1569 desc: "map of int64",
1570 input: `A = 42`,
1571 gen: func() test {
1572 return test{
1573 target: &map[string]int64{},
1574 expected: &map[string]int64{"A": 42},
1575 }
1576 },
1577 },
1578 {
1579 desc: "map of float64",
1580 input: `A = 4.2`,
1581 gen: func() test {
1582 return test{
1583 target: &map[string]float64{},
1584 expected: &map[string]float64{"A": 4.2},
1585 }
1586 },
1587 },
1588 {
1589 desc: "array of int in map",
1590 input: `A = [1,2,3]`,
1591 gen: func() test {
1592 return test{
1593 target: &map[string][3]int{},
1594 expected: &map[string][3]int{"A": {1, 2, 3}},
1595 }
1596 },
1597 },
1598 {
1599 desc: "array of int in map with too many elements",
1600 input: `A = [1,2,3,4,5]`,
1601 gen: func() test {
1602 return test{
1603 target: &map[string][3]int{},
1604 expected: &map[string][3]int{"A": {1, 2, 3}},
1605 }
1606 },
1607 },
1608 {
1609 desc: "array of int in map with invalid element",
1610 input: `A = [1,2,false]`,
1611 gen: func() test {
1612 return test{
1613 target: &map[string][3]int{},
1614 err: true,
1615 }
1616 },
1617 },
1618 {
1619 desc: "nested arrays",
1620 input: `
1621 [[A]]
1622 [[A.B]]
1623 C = 1
1624 [[A]]
1625 [[A.B]]
1626 C = 2`,
1627 gen: func() test {
1628 type leaf struct {
1629 C int
1630 }
1631 type inner struct {
1632 B [2]leaf
1633 }
1634 type s struct {
1635 A [2]inner
1636 }
1637 return test{
1638 target: &s{},
1639 expected: &s{A: [2]inner{
1640 {B: [2]leaf{
1641 {C: 1},
1642 }},
1643 {B: [2]leaf{
1644 {C: 2},
1645 }},
1646 }},
1647 }
1648 },
1649 },
1650 {
1651 desc: "nested arrays too many",
1652 input: `
1653 [[A]]
1654 [[A.B]]
1655 C = 1
1656 [[A.B]]
1657 C = 2`,
1658 gen: func() test {
1659 type leaf struct {
1660 C int
1661 }
1662 type inner struct {
1663 B [1]leaf
1664 }
1665 type s struct {
1666 A [1]inner
1667 }
1668 return test{
1669 target: &s{},
1670 err: true,
1671 }
1672 },
1673 },
1674 {
1675 desc: "empty array table in interface{}",
1676 input: `[[products]]`,
1677 gen: func() test {
1678 return test{
1679 target: &map[string]interface{}{},
1680 expected: &map[string]interface{}{
1681 "products": []interface{}{
1682 map[string]interface{}{},
1683 },
1684 },
1685 }
1686 },
1687 },
1688 {
1689 desc: "into map with invalid key type",
1690 input: `A = "hello"`,
1691 gen: func() test {
1692 return test{
1693 target: &map[int]string{},
1694 err: true,
1695 }
1696 },
1697 },
1698 {
1699 desc: "empty map into map with invalid key type",
1700 input: ``,
1701 gen: func() test {
1702 return test{
1703 target: &map[int]string{},
1704 expected: &map[int]string{},
1705 }
1706 },
1707 },
1708 {
1709 desc: "into map with convertible key type",
1710 input: `A = "hello"`,
1711 gen: func() test {
1712 type foo string
1713 return test{
1714 target: &map[foo]string{},
1715 expected: &map[foo]string{
1716 "A": "hello",
1717 },
1718 }
1719 },
1720 },
1721 {
1722 desc: "array of int in struct",
1723 input: `A = [1,2,3]`,
1724 gen: func() test {
1725 type s struct {
1726 A [3]int
1727 }
1728 return test{
1729 target: &s{},
1730 expected: &s{A: [3]int{1, 2, 3}},
1731 }
1732 },
1733 },
1734 {
1735 desc: "array of int in struct",
1736 input: `[A]
1737 b = 42`,
1738 gen: func() test {
1739 type s struct {
1740 A *map[string]interface{}
1741 }
1742 return test{
1743 target: &s{},
1744 expected: &s{A: &map[string]interface{}{"b": int64(42)}},
1745 }
1746 },
1747 },
1748 {
1749 desc: "assign bool to float",
1750 input: `A = true`,
1751 gen: func() test {
1752 return test{
1753 target: &map[string]float64{},
1754 err: true,
1755 }
1756 },
1757 },
1758 {
1759 desc: "interface holding a struct",
1760 input: `[A]
1761 B = "After"`,
1762 gen: func() test {
1763 type inner struct {
1764 B interface{}
1765 }
1766 type doc struct {
1767 A interface{}
1768 }
1769
1770 return test{
1771 target: &doc{
1772 A: inner{
1773 B: "Before",
1774 },
1775 },
1776 expected: &doc{
1777 A: map[string]interface{}{
1778 "B": "After",
1779 },
1780 },
1781 }
1782 },
1783 },
1784 {
1785 desc: "array of structs with table arrays",
1786 input: `[[A]]
1787 B = "one"
1788 [[A]]
1789 B = "two"`,
1790 gen: func() test {
1791 type inner struct {
1792 B string
1793 }
1794 type doc struct {
1795 A [4]inner
1796 }
1797
1798 return test{
1799 target: &doc{},
1800 expected: &doc{
1801 A: [4]inner{
1802 {B: "one"},
1803 {B: "two"},
1804 },
1805 },
1806 }
1807 },
1808 },
1809 {
1810 desc: "windows line endings",
1811 input: "A = 1\r\n\r\nB = 2",
1812 gen: func() test {
1813 doc := map[string]interface{}{}
1814
1815 return test{
1816 target: &doc,
1817 expected: &map[string]interface{}{
1818 "A": int64(1),
1819 "B": int64(2),
1820 },
1821 }
1822 },
1823 },
1824 {
1825 desc: "dangling CR",
1826 input: "A = 1\r",
1827 gen: func() test {
1828 doc := map[string]interface{}{}
1829
1830 return test{
1831 target: &doc,
1832 err: true,
1833 }
1834 },
1835 },
1836 {
1837 desc: "missing NL after CR",
1838 input: "A = 1\rB = 2",
1839 gen: func() test {
1840 doc := map[string]interface{}{}
1841
1842 return test{
1843 target: &doc,
1844 err: true,
1845 }
1846 },
1847 },
1848 {
1849 desc: "no newline (#526)",
1850 input: `a = 1z = 2`,
1851 gen: func() test {
1852 m := map[string]interface{}{}
1853
1854 return test{
1855 target: &m,
1856 err: true,
1857 }
1858 },
1859 },
1860 {
1861 desc: "mismatch types int to string",
1862 input: `A = 42`,
1863 gen: func() test {
1864 type S struct {
1865 A string
1866 }
1867 return test{
1868 target: &S{},
1869 err: true,
1870 }
1871 },
1872 },
1873 {
1874 desc: "mismatch types array of int to interface with non-slice",
1875 input: `A = [42]`,
1876 gen: func() test {
1877 type S struct {
1878 A string
1879 }
1880 return test{
1881 target: &S{},
1882 err: true,
1883 }
1884 },
1885 },
1886 {
1887 desc: "comment with CRLF",
1888 input: "# foo\r\na=2",
1889 gen: func() test {
1890 doc := map[string]interface{}{}
1891
1892 return test{
1893 target: &doc,
1894 expected: &map[string]interface{}{"a": int64(2)},
1895 }
1896 },
1897 },
1898 {
1899 desc: "comment that looks like a date",
1900 input: "a=19#9-",
1901 gen: func() test {
1902 doc := map[string]interface{}{}
1903
1904 return test{
1905 target: &doc,
1906 expected: &map[string]interface{}{"a": int64(19)},
1907 }
1908 },
1909 },
1910 {
1911 desc: "comment that looks like a date",
1912 input: "a=199#-",
1913 gen: func() test {
1914 doc := map[string]interface{}{}
1915
1916 return test{
1917 target: &doc,
1918 expected: &map[string]interface{}{"a": int64(199)},
1919 }
1920 },
1921 },
1922 {
1923 desc: "kv that points to a slice",
1924 input: "a.b.c = 'foo'",
1925 gen: func() test {
1926 doc := map[string][]string{}
1927 return test{
1928 target: &doc,
1929 err: true,
1930 }
1931 },
1932 },
1933 {
1934 desc: "kv that points to a pointer to a slice",
1935 input: "a.b.c = 'foo'",
1936 gen: func() test {
1937 doc := map[string]*[]string{}
1938 return test{
1939 target: &doc,
1940 err: true,
1941 }
1942 },
1943 },
1944 }
1945
1946 for _, e := range examples {
1947 e := e
1948 t.Run(e.desc, func(t *testing.T) {
1949 if e.skip {
1950 t.Skip()
1951 }
1952 test := e.gen()
1953 if test.err && test.expected != nil {
1954 panic("invalid test: cannot expect both an error and a value")
1955 }
1956 err := toml.Unmarshal([]byte(e.input), test.target)
1957 if test.err {
1958 if err == nil {
1959 t.Log("=>", test.target)
1960 }
1961 require.Error(t, err)
1962 } else {
1963 require.NoError(t, err)
1964 if test.assert != nil {
1965 test.assert(t, test)
1966 } else {
1967 assert.Equal(t, test.expected, test.target)
1968 }
1969 }
1970 })
1971 }
1972 }
1973
1974 func TestUnmarshalOverflows(t *testing.T) {
1975 examples := []struct {
1976 t interface{}
1977 errors []string
1978 }{
1979 {
1980 t: &map[string]int32{},
1981 errors: []string{`-2147483649`, `2147483649`},
1982 },
1983 {
1984 t: &map[string]int16{},
1985 errors: []string{`-2147483649`, `2147483649`},
1986 },
1987 {
1988 t: &map[string]int8{},
1989 errors: []string{`-2147483649`, `2147483649`},
1990 },
1991 {
1992 t: &map[string]int{},
1993 errors: []string{`-19223372036854775808`, `9223372036854775808`},
1994 },
1995 {
1996 t: &map[string]uint64{},
1997 errors: []string{`-1`, `18446744073709551616`},
1998 },
1999 {
2000 t: &map[string]uint32{},
2001 errors: []string{`-1`, `18446744073709551616`},
2002 },
2003 {
2004 t: &map[string]uint16{},
2005 errors: []string{`-1`, `18446744073709551616`},
2006 },
2007 {
2008 t: &map[string]uint8{},
2009 errors: []string{`-1`, `18446744073709551616`},
2010 },
2011 {
2012 t: &map[string]uint{},
2013 errors: []string{`-1`, `18446744073709551616`},
2014 },
2015 }
2016
2017 for _, e := range examples {
2018 e := e
2019 for _, v := range e.errors {
2020 v := v
2021 t.Run(fmt.Sprintf("%T %s", e.t, v), func(t *testing.T) {
2022 doc := "A = " + v
2023 err := toml.Unmarshal([]byte(doc), e.t)
2024 t.Log("input:", doc)
2025 require.Error(t, err)
2026 })
2027 }
2028 t.Run(fmt.Sprintf("%T ok", e.t), func(t *testing.T) {
2029 doc := "A = 1"
2030 err := toml.Unmarshal([]byte(doc), e.t)
2031 t.Log("input:", doc)
2032 require.NoError(t, err)
2033 })
2034 }
2035 }
2036
2037 func TestUnmarshalErrors(t *testing.T) {
2038 type mystruct struct {
2039 Bar string
2040 }
2041
2042 data := `bar = 42`
2043
2044 s := mystruct{}
2045 err := toml.Unmarshal([]byte(data), &s)
2046 require.Error(t, err)
2047
2048 require.Equal(t, "toml: cannot decode TOML integer into struct field toml_test.mystruct.Bar of type string", err.Error())
2049 }
2050
2051 func TestUnmarshalStringInvalidStructField(t *testing.T) {
2052 type Server struct {
2053 Path string
2054 Port int
2055 }
2056
2057 type Cfg struct {
2058 Server Server
2059 }
2060
2061 var cfg Cfg
2062
2063 data := `[server]
2064 path = "/my/path"
2065 port = "bad"
2066 `
2067
2068 file := strings.NewReader(data)
2069 err := toml.NewDecoder(file).Decode(&cfg)
2070 require.Error(t, err)
2071
2072 x := err.(*toml.DecodeError)
2073 require.Equal(t, "toml: cannot decode TOML string into struct field toml_test.Server.Port of type int", x.Error())
2074 expected := `1| [server]
2075 2| path = "/my/path"
2076 3| port = "bad"
2077 | ~~~~~ cannot decode TOML string into struct field toml_test.Server.Port of type int`
2078
2079 require.Equal(t, expected, x.String())
2080 }
2081
2082 func TestUnmarshalIntegerInvalidStructField(t *testing.T) {
2083 type Server struct {
2084 Path string
2085 Port int
2086 }
2087
2088 type Cfg struct {
2089 Server Server
2090 }
2091
2092 var cfg Cfg
2093
2094 data := `[server]
2095 path = 100
2096 port = 50
2097 `
2098
2099 file := strings.NewReader(data)
2100 err := toml.NewDecoder(file).Decode(&cfg)
2101 require.Error(t, err)
2102
2103 x := err.(*toml.DecodeError)
2104 require.Equal(t, "toml: cannot decode TOML integer into struct field toml_test.Server.Path of type string", x.Error())
2105 expected := `1| [server]
2106 2| path = 100
2107 | ~~~ cannot decode TOML integer into struct field toml_test.Server.Path of type string
2108 3| port = 50`
2109
2110 require.Equal(t, expected, x.String())
2111 }
2112
2113 func TestUnmarshalInvalidTarget(t *testing.T) {
2114 x := "foo"
2115 err := toml.Unmarshal([]byte{}, x)
2116 require.Error(t, err)
2117
2118 var m *map[string]interface{}
2119 err = toml.Unmarshal([]byte{}, m)
2120 require.Error(t, err)
2121 }
2122
2123 func TestUnmarshalFloat32(t *testing.T) {
2124 t.Run("fits", func(t *testing.T) {
2125 doc := "A = 1.2"
2126 err := toml.Unmarshal([]byte(doc), &map[string]float32{})
2127 require.NoError(t, err)
2128 })
2129 t.Run("overflows", func(t *testing.T) {
2130 doc := "A = 4.40282346638528859811704183484516925440e+38"
2131 err := toml.Unmarshal([]byte(doc), &map[string]float32{})
2132 require.Error(t, err)
2133 })
2134 }
2135
2136 func TestDecoderStrict(t *testing.T) {
2137 examples := []struct {
2138 desc string
2139 input string
2140 expected string
2141 target interface{}
2142 }{
2143 {
2144 desc: "multiple missing root keys",
2145 input: `
2146 key1 = "value1"
2147 key2 = "missing2"
2148 key3 = "missing3"
2149 key4 = "value4"
2150 `,
2151 expected: `2| key1 = "value1"
2152 3| key2 = "missing2"
2153 | ~~~~ missing field
2154 4| key3 = "missing3"
2155 5| key4 = "value4"
2156 ---
2157 2| key1 = "value1"
2158 3| key2 = "missing2"
2159 4| key3 = "missing3"
2160 | ~~~~ missing field
2161 5| key4 = "value4"`,
2162 target: &struct {
2163 Key1 string
2164 Key4 string
2165 }{},
2166 },
2167 {
2168 desc: "multi-part key",
2169 input: `a.short.key="foo"`,
2170 expected: `1| a.short.key="foo"
2171 | ~~~~~~~~~~~ missing field`,
2172 },
2173 {
2174 desc: "missing table",
2175 input: `
2176 [foo]
2177 bar = 42
2178 `,
2179 expected: `2| [foo]
2180 | ~~~ missing table
2181 3| bar = 42`,
2182 },
2183
2184 {
2185 desc: "missing array table",
2186 input: `
2187 [[foo]]
2188 bar = 42`,
2189 expected: `2| [[foo]]
2190 | ~~~ missing table
2191 3| bar = 42`,
2192 },
2193 }
2194
2195 for _, e := range examples {
2196 e := e
2197 t.Run(e.desc, func(t *testing.T) {
2198 t.Run("strict", func(t *testing.T) {
2199 r := strings.NewReader(e.input)
2200 d := toml.NewDecoder(r)
2201 d.DisallowUnknownFields()
2202 x := e.target
2203 if x == nil {
2204 x = &struct{}{}
2205 }
2206 err := d.Decode(x)
2207
2208 var tsm *toml.StrictMissingError
2209 if errors.As(err, &tsm) {
2210 assert.Equal(t, e.expected, tsm.String())
2211 } else {
2212 t.Fatalf("err should have been a *toml.StrictMissingError, but got %s (%T)", err, err)
2213 }
2214 })
2215
2216 t.Run("default", func(t *testing.T) {
2217 r := strings.NewReader(e.input)
2218 d := toml.NewDecoder(r)
2219 x := e.target
2220 if x == nil {
2221 x = &struct{}{}
2222 }
2223 err := d.Decode(x)
2224 require.NoError(t, err)
2225 })
2226 })
2227 }
2228 }
2229
2230 func TestIssue252(t *testing.T) {
2231 type config struct {
2232 Val1 string `toml:"val1"`
2233 Val2 string `toml:"val2"`
2234 }
2235
2236 configFile := []byte(
2237 `
2238 val1 = "test1"
2239 `)
2240
2241 cfg := &config{
2242 Val2: "test2",
2243 }
2244
2245 err := toml.Unmarshal(configFile, cfg)
2246 require.NoError(t, err)
2247 require.Equal(t, "test2", cfg.Val2)
2248 }
2249
2250 func TestIssue287(t *testing.T) {
2251 b := `y=[[{}]]`
2252 v := map[string]interface{}{}
2253 err := toml.Unmarshal([]byte(b), &v)
2254 require.NoError(t, err)
2255
2256 expected := map[string]interface{}{
2257 "y": []interface{}{
2258 []interface{}{
2259 map[string]interface{}{},
2260 },
2261 },
2262 }
2263 require.Equal(t, expected, v)
2264 }
2265
2266 type (
2267 Map458 map[string]interface{}
2268 Slice458 []interface{}
2269 )
2270
2271 func (m Map458) A(s string) Slice458 {
2272 return m[s].([]interface{})
2273 }
2274
2275 func TestIssue458(t *testing.T) {
2276 s := []byte(`[[package]]
2277 dependencies = ["regex"]
2278 name = "decode"
2279 version = "0.1.0"`)
2280 m := Map458{}
2281 err := toml.Unmarshal(s, &m)
2282 require.NoError(t, err)
2283 a := m.A("package")
2284 expected := Slice458{
2285 map[string]interface{}{
2286 "dependencies": []interface{}{"regex"},
2287 "name": "decode",
2288 "version": "0.1.0",
2289 },
2290 }
2291 assert.Equal(t, expected, a)
2292 }
2293
2294 type Integer484 struct {
2295 Value int
2296 }
2297
2298 func (i Integer484) MarshalText() ([]byte, error) {
2299 return []byte(strconv.Itoa(i.Value)), nil
2300 }
2301
2302 func (i *Integer484) UnmarshalText(data []byte) error {
2303 conv, err := strconv.Atoi(string(data))
2304 if err != nil {
2305 return fmt.Errorf("UnmarshalText: %w", err)
2306 }
2307 i.Value = conv
2308
2309 return nil
2310 }
2311
2312 type Config484 struct {
2313 Integers []Integer484 `toml:"integers"`
2314 }
2315
2316 func TestIssue484(t *testing.T) {
2317 raw := []byte(`integers = ["1","2","3","100"]`)
2318
2319 var cfg Config484
2320 err := toml.Unmarshal(raw, &cfg)
2321 require.NoError(t, err)
2322 assert.Equal(t, Config484{
2323 Integers: []Integer484{{1}, {2}, {3}, {100}},
2324 }, cfg)
2325 }
2326
2327 func TestIssue494(t *testing.T) {
2328 data := `
2329 foo = 2021-04-08
2330 bar = 2021-04-08
2331 `
2332
2333 type s struct {
2334 Foo time.Time `toml:"foo"`
2335 Bar time.Time `toml:"bar"`
2336 }
2337 ss := new(s)
2338 err := toml.Unmarshal([]byte(data), ss)
2339 require.NoError(t, err)
2340 }
2341
2342 func TestIssue508(t *testing.T) {
2343 type head struct {
2344 Title string `toml:"title"`
2345 }
2346
2347 type text struct {
2348 head
2349 }
2350
2351 b := []byte(`title = "This is a title"`)
2352
2353 t1 := text{}
2354 err := toml.Unmarshal(b, &t1)
2355 require.NoError(t, err)
2356 require.Equal(t, "This is a title", t1.head.Title)
2357 }
2358
2359 func TestIssue507(t *testing.T) {
2360 data := []byte{'0', '=', '\n', '0', 'a', 'm', 'e'}
2361 m := map[string]interface{}{}
2362 err := toml.Unmarshal(data, &m)
2363 require.Error(t, err)
2364 }
2365
2366 type uuid [16]byte
2367
2368 func (u *uuid) UnmarshalText(text []byte) (err error) {
2369
2370
2371
2372
2373 placeholder := bytes.Repeat([]byte{0xAA}, 16)
2374 copy(u[:], placeholder)
2375 return nil
2376 }
2377
2378 func TestIssue564(t *testing.T) {
2379 type Config struct {
2380 ID uuid
2381 }
2382
2383 var config Config
2384
2385 err := toml.Unmarshal([]byte(`id = "0818a52b97b94768941ba1172c76cf6c"`), &config)
2386 require.NoError(t, err)
2387 require.Equal(t, uuid{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}, config.ID)
2388 }
2389
2390 func TestIssue575(t *testing.T) {
2391 b := []byte(`
2392 [pkg.cargo]
2393 version = "0.55.0 (5ae8d74b3 2021-06-22)"
2394 git_commit_hash = "a178d0322ce20e33eac124758e837cbd80a6f633"
2395 [pkg.cargo.target.aarch64-apple-darwin]
2396 available = true
2397 url = "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-apple-darwin.tar.gz"
2398 hash = "7bac3901d8eb6a4191ffeebe75b29c78bcb270158ec901addb31f588d965d35d"
2399 xz_url = "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-apple-darwin.tar.xz"
2400 xz_hash = "5207644fd6379f3e5b8ae60016b854efa55a381b0c363bff7f9b2f25bfccc430"
2401
2402 [pkg.cargo.target.aarch64-pc-windows-msvc]
2403 available = true
2404 url = "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-pc-windows-msvc.tar.gz"
2405 hash = "eb8ccd9b1f6312b06dc749c17896fa4e9c163661c273dcb61cd7a48376227f6d"
2406 xz_url = "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-pc-windows-msvc.tar.xz"
2407 xz_hash = "1a48f723fea1f17d786ce6eadd9d00914d38062d28fd9c455ed3c3801905b388"
2408 `)
2409
2410 type target struct {
2411 XZ_URL string
2412 }
2413
2414 type pkg struct {
2415 Target map[string]target
2416 }
2417
2418 type doc struct {
2419 Pkg map[string]pkg
2420 }
2421
2422 var dist doc
2423 err := toml.Unmarshal(b, &dist)
2424 require.NoError(t, err)
2425
2426 expected := doc{
2427 Pkg: map[string]pkg{
2428 "cargo": {
2429 Target: map[string]target{
2430 "aarch64-apple-darwin": {
2431 XZ_URL: "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-apple-darwin.tar.xz",
2432 },
2433 "aarch64-pc-windows-msvc": {
2434 XZ_URL: "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-pc-windows-msvc.tar.xz",
2435 },
2436 },
2437 },
2438 },
2439 }
2440
2441 require.Equal(t, expected, dist)
2442 }
2443
2444 func TestIssue579(t *testing.T) {
2445 var v interface{}
2446 err := toml.Unmarshal([]byte(`[foo`), &v)
2447 require.Error(t, err)
2448 }
2449
2450 func TestIssue581(t *testing.T) {
2451 var v interface{}
2452 err := toml.Unmarshal([]byte(`P=[#`), &v)
2453 require.Error(t, err)
2454 }
2455
2456 func TestIssue585(t *testing.T) {
2457 var v interface{}
2458 err := toml.Unmarshal([]byte(`a=1979-05127T 0`), &v)
2459 require.Error(t, err)
2460 }
2461
2462 func TestIssue586(t *testing.T) {
2463 var v interface{}
2464 err := toml.Unmarshal([]byte(`a={ `), &v)
2465 require.Error(t, err)
2466 }
2467
2468 func TestIssue588(t *testing.T) {
2469 var v interface{}
2470 err := toml.Unmarshal([]byte(`a=[1#`), &v)
2471 require.Error(t, err)
2472 }
2473
2474
2475 func TestIssue600(t *testing.T) {
2476 var v interface{}
2477 err := toml.Unmarshal([]byte(`a=1979-05-27t00:32:00z`), &v)
2478 require.NoError(t, err)
2479 }
2480
2481 func TestIssue596(t *testing.T) {
2482 var v interface{}
2483 err := toml.Unmarshal([]byte(`a=1979-05-27T90:+2:99`), &v)
2484 require.Error(t, err)
2485 }
2486
2487 func TestIssue602(t *testing.T) {
2488 var v interface{}
2489 err := toml.Unmarshal([]byte(""), &v)
2490 require.NoError(t, err)
2491
2492 var expected interface{} = map[string]interface{}{}
2493
2494 require.Equal(t, expected, v)
2495 }
2496
2497 func TestIssue623(t *testing.T) {
2498 definition := struct {
2499 Things []string
2500 }{}
2501
2502 values := `[things]
2503 foo = "bar"`
2504
2505 err := toml.Unmarshal([]byte(values), &definition)
2506 require.Error(t, err)
2507 }
2508
2509 func TestIssue631(t *testing.T) {
2510 v := map[string]interface{}{}
2511 err := toml.Unmarshal([]byte("\"\\b\u007f\"= 2"), &v)
2512 require.Error(t, err)
2513 }
2514
2515 func TestIssue658(t *testing.T) {
2516 var v map[string]interface{}
2517 err := toml.Unmarshal([]byte("e={b=1,b=4}"), &v)
2518 require.Error(t, err)
2519 }
2520
2521 func TestIssue662(t *testing.T) {
2522 var v map[string]interface{}
2523 err := toml.Unmarshal([]byte("a=[{b=1,b=2}]"), &v)
2524 require.Error(t, err)
2525 }
2526
2527 func TestIssue666(t *testing.T) {
2528 var v map[string]interface{}
2529 err := toml.Unmarshal([]byte("a={}\na={}"), &v)
2530 require.Error(t, err)
2531 }
2532
2533 func TestIssue677(t *testing.T) {
2534 doc := `
2535 [Build]
2536 Name = "publication build"
2537
2538 [[Build.Dependencies]]
2539 Name = "command"
2540 Program = "hugo"
2541 `
2542
2543 type _tomlJob struct {
2544 Dependencies []map[string]interface{}
2545 }
2546
2547 type tomlParser struct {
2548 Build *_tomlJob
2549 }
2550
2551 p := tomlParser{}
2552
2553 err := toml.Unmarshal([]byte(doc), &p)
2554 require.NoError(t, err)
2555
2556 expected := tomlParser{
2557 Build: &_tomlJob{
2558 Dependencies: []map[string]interface{}{
2559 {
2560 "Name": "command",
2561 "Program": "hugo",
2562 },
2563 },
2564 },
2565 }
2566 require.Equal(t, expected, p)
2567 }
2568
2569 func TestIssue701(t *testing.T) {
2570
2571
2572
2573
2574
2575
2576
2577 docs := []string{
2578 `
2579 a={}
2580 [a.b]
2581 z=0
2582 `,
2583 `
2584 a={}
2585 [[a.b]]
2586 z=0
2587 `,
2588 }
2589
2590 for _, doc := range docs {
2591 var v interface{}
2592 err := toml.Unmarshal([]byte(doc), &v)
2593 assert.Error(t, err)
2594 }
2595 }
2596
2597 func TestIssue703(t *testing.T) {
2598 var v interface{}
2599 err := toml.Unmarshal([]byte("[a]\nx.y=0\n[a.x]"), &v)
2600 require.Error(t, err)
2601 }
2602
2603 func TestIssue708(t *testing.T) {
2604 v := map[string]string{}
2605 err := toml.Unmarshal([]byte("0=\"\"\"\\\r\n\"\"\""), &v)
2606 require.NoError(t, err)
2607 require.Equal(t, map[string]string{"0": ""}, v)
2608 }
2609
2610 func TestIssue710(t *testing.T) {
2611 v := map[string]toml.LocalTime{}
2612 err := toml.Unmarshal([]byte(`0=00:00:00.0000000000`), &v)
2613 require.NoError(t, err)
2614 require.Equal(t, map[string]toml.LocalTime{"0": {Precision: 9}}, v)
2615 v1 := map[string]toml.LocalTime{}
2616 err = toml.Unmarshal([]byte(`0=00:00:00.0000000001`), &v1)
2617 require.NoError(t, err)
2618 require.Equal(t, map[string]toml.LocalTime{"0": {Precision: 9}}, v1)
2619 v2 := map[string]toml.LocalTime{}
2620 err = toml.Unmarshal([]byte(`0=00:00:00.1111111119`), &v2)
2621 require.NoError(t, err)
2622 require.Equal(t, map[string]toml.LocalTime{"0": {Nanosecond: 111111111, Precision: 9}}, v2)
2623 }
2624
2625 func TestIssue715(t *testing.T) {
2626 var v interface{}
2627 err := toml.Unmarshal([]byte("0=+"), &v)
2628 require.Error(t, err)
2629
2630 err = toml.Unmarshal([]byte("0=-"), &v)
2631 require.Error(t, err)
2632
2633 err = toml.Unmarshal([]byte("0=+A"), &v)
2634 require.Error(t, err)
2635 }
2636
2637 func TestIssue714(t *testing.T) {
2638 var v interface{}
2639 err := toml.Unmarshal([]byte("0."), &v)
2640 require.Error(t, err)
2641
2642 err = toml.Unmarshal([]byte("0={0=0,"), &v)
2643 require.Error(t, err)
2644 }
2645
2646 func TestIssue772(t *testing.T) {
2647 type FileHandling struct {
2648 FilePattern string `toml:"pattern"`
2649 }
2650
2651 type Config struct {
2652 FileHandling `toml:"filehandling"`
2653 }
2654
2655 var defaultConfigFile = []byte(`
2656 [filehandling]
2657 pattern = "reach-masterdev-"`)
2658
2659 config := Config{}
2660 err := toml.Unmarshal(defaultConfigFile, &config)
2661 require.NoError(t, err)
2662 require.Equal(t, "reach-masterdev-", config.FileHandling.FilePattern)
2663 }
2664
2665 func TestIssue774(t *testing.T) {
2666 type ScpData struct {
2667 Host string `json:"host"`
2668 }
2669
2670 type GenConfig struct {
2671 SCP []ScpData `toml:"scp" comment:"Array of Secure Copy Configurations"`
2672 }
2673
2674 c := &GenConfig{}
2675 c.SCP = []ScpData{{Host: "main.domain.com"}}
2676
2677 b, err := toml.Marshal(c)
2678 require.NoError(t, err)
2679
2680 expected := `# Array of Secure Copy Configurations
2681 [[scp]]
2682 Host = 'main.domain.com'
2683 `
2684
2685 require.Equal(t, expected, string(b))
2686 }
2687
2688 func TestIssue799(t *testing.T) {
2689 const testTOML = `
2690 # notice the double brackets
2691 [[test]]
2692 answer = 42
2693 `
2694
2695 var s struct {
2696
2697 Test map[string]int `toml:"test"`
2698 }
2699
2700 err := toml.Unmarshal([]byte(testTOML), &s)
2701 require.Error(t, err)
2702 }
2703
2704 func TestIssue807(t *testing.T) {
2705 type A struct {
2706 Name string `toml:"name"`
2707 }
2708
2709 type M struct {
2710 *A
2711 }
2712
2713 var m M
2714 err := toml.Unmarshal([]byte(`name = 'foo'`), &m)
2715 require.NoError(t, err)
2716 require.Equal(t, "foo", m.Name)
2717 }
2718
2719 func TestIssue850(t *testing.T) {
2720 data := make(map[string]string)
2721 err := toml.Unmarshal([]byte("foo = {}"), &data)
2722 require.Error(t, err)
2723 }
2724
2725 func TestIssue851(t *testing.T) {
2726 type Target struct {
2727 Params map[string]string `toml:"params"`
2728 }
2729
2730 content := "params = {a=\"1\",b=\"2\"}"
2731 var target Target
2732 err := toml.Unmarshal([]byte(content), &target)
2733 require.NoError(t, err)
2734 require.Equal(t, map[string]string{"a": "1", "b": "2"}, target.Params)
2735 err = toml.Unmarshal([]byte(content), &target)
2736 require.NoError(t, err)
2737 require.Equal(t, map[string]string{"a": "1", "b": "2"}, target.Params)
2738 }
2739
2740 func TestIssue866(t *testing.T) {
2741 type Pipeline struct {
2742 Mapping map[string]struct {
2743 Req [][]string `toml:"req"`
2744 Res [][]string `toml:"res"`
2745 } `toml:"mapping"`
2746 }
2747
2748 type Pipelines struct {
2749 PipelineMapping map[string]*Pipeline `toml:"pipelines"`
2750 }
2751
2752 var badToml = `
2753 [pipelines.register]
2754 mapping.inst.req = [
2755 ["param1", "value1"],
2756 ]
2757 mapping.inst.res = [
2758 ["param2", "value2"],
2759 ]
2760 `
2761
2762 pipelines := new(Pipelines)
2763 if err := toml.NewDecoder(bytes.NewBufferString(badToml)).DisallowUnknownFields().Decode(pipelines); err != nil {
2764 t.Fatal(err)
2765 }
2766 if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" {
2767 t.Fatal("unmarshal failed with mismatch value")
2768 }
2769
2770 var goodTooToml = `
2771 [pipelines.register]
2772 mapping.inst.req = [
2773 ["param1", "value1"],
2774 ]
2775 `
2776
2777 pipelines = new(Pipelines)
2778 if err := toml.NewDecoder(bytes.NewBufferString(goodTooToml)).DisallowUnknownFields().Decode(pipelines); err != nil {
2779 t.Fatal(err)
2780 }
2781 if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" {
2782 t.Fatal("unmarshal failed with mismatch value")
2783 }
2784
2785 var goodToml = `
2786 [pipelines.register.mapping.inst]
2787 req = [
2788 ["param1", "value1"],
2789 ]
2790 res = [
2791 ["param2", "value2"],
2792 ]
2793 `
2794
2795 pipelines = new(Pipelines)
2796 if err := toml.NewDecoder(bytes.NewBufferString(goodToml)).DisallowUnknownFields().Decode(pipelines); err != nil {
2797 t.Fatal(err)
2798 }
2799 if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" {
2800 t.Fatal("unmarshal failed with mismatch value")
2801 }
2802 }
2803
2804 func TestIssue915(t *testing.T) {
2805 type blah struct {
2806 A string `toml:"a"`
2807 }
2808
2809 type config struct {
2810 Fizz string `toml:"fizz"`
2811 blah `toml:"blah"`
2812 }
2813
2814 b := []byte(`
2815 fizz = "abc"
2816 blah.a = "def"`)
2817 var cfg config
2818 err := toml.Unmarshal(b, &cfg)
2819 require.NoError(t, err)
2820
2821 require.Equal(t, "abc", cfg.Fizz)
2822 require.Equal(t, "def", cfg.blah.A)
2823 require.Equal(t, "def", cfg.A)
2824 }
2825
2826 func TestUnmarshalDecodeErrors(t *testing.T) {
2827 examples := []struct {
2828 desc string
2829 data string
2830 msg string
2831 }{
2832 {
2833 desc: "local date with invalid digit",
2834 data: `a = 20x1-05-21`,
2835 },
2836 {
2837 desc: "local time with fractional",
2838 data: `a = 11:22:33.x`,
2839 },
2840 {
2841 desc: "wrong time offset separator",
2842 data: `a = 1979-05-27T00:32:00.-07:00`,
2843 },
2844 {
2845 desc: "wrong time offset separator",
2846 data: `a = 1979-05-27T00:32:00Z07:00`,
2847 },
2848 {
2849 desc: "float with double _",
2850 data: `flt8 = 224_617.445_991__228`,
2851 },
2852 {
2853 desc: "float with double .",
2854 data: `flt8 = 1..2`,
2855 },
2856 {
2857 desc: "number with plus sign and leading underscore",
2858 data: `a = +_0`,
2859 },
2860 {
2861 desc: "number with negative sign and leading underscore",
2862 data: `a = -_0`,
2863 },
2864 {
2865 desc: "exponent with plus sign and leading underscore",
2866 data: `a = 0e+_0`,
2867 },
2868 {
2869 desc: "exponent with negative sign and leading underscore",
2870 data: `a = 0e-_0`,
2871 },
2872 {
2873 desc: "int with wrong base",
2874 data: `a = 0f2`,
2875 },
2876 {
2877 desc: "int hex with double underscore",
2878 data: `a = 0xFFF__FFF`,
2879 },
2880 {
2881 desc: "int hex very large",
2882 data: `a = 0xFFFFFFFFFFFFFFFFF`,
2883 },
2884 {
2885 desc: "int oct with double underscore",
2886 data: `a = 0o777__77`,
2887 },
2888 {
2889 desc: "int oct very large",
2890 data: `a = 0o77777777777777777777777`,
2891 },
2892 {
2893 desc: "int bin with double underscore",
2894 data: `a = 0b111__111`,
2895 },
2896 {
2897 desc: "int bin very large",
2898 data: `a = 0b11111111111111111111111111111111111111111111111111111111111111111111111111111`,
2899 },
2900 {
2901 desc: "int dec very large",
2902 data: `a = 999999999999999999999999`,
2903 },
2904 {
2905 desc: "literal string with new lines",
2906 data: `a = 'hello
2907 world'`,
2908 msg: `literal strings cannot have new lines`,
2909 },
2910 {
2911 desc: "unterminated literal string",
2912 data: `a = 'hello`,
2913 msg: `unterminated literal string`,
2914 },
2915 {
2916 desc: "unterminated multiline literal string",
2917 data: `a = '''hello`,
2918 msg: `multiline literal string not terminated by '''`,
2919 },
2920 {
2921 desc: "basic string with new lines",
2922 data: `a = "hello
2923 "`,
2924 msg: `basic strings cannot have new lines`,
2925 },
2926 {
2927 desc: "basic string with unfinished escape",
2928 data: `a = "hello \`,
2929 msg: `need a character after \`,
2930 },
2931 {
2932 desc: "basic unfinished multiline string",
2933 data: `a = """hello`,
2934 msg: `multiline basic string not terminated by """`,
2935 },
2936 {
2937 desc: "basic unfinished escape in multiline string",
2938 data: `a = """hello \`,
2939 msg: `need a character after \`,
2940 },
2941 {
2942 desc: "malformed local date",
2943 data: `a = 2021-033-0`,
2944 msg: `dates are expected to have the format YYYY-MM-DD`,
2945 },
2946 {
2947 desc: "malformed tz",
2948 data: `a = 2021-03-30 21:31:00+1`,
2949 msg: `invalid date-time timezone`,
2950 },
2951 {
2952 desc: "malformed tz first char",
2953 data: `a = 2021-03-30 21:31:00:1`,
2954 msg: `extra characters at the end of a local date time`,
2955 },
2956 {
2957 desc: "bad char between hours and minutes",
2958 data: `a = 2021-03-30 213:1:00`,
2959 msg: `expecting colon between hours and minutes`,
2960 },
2961 {
2962 desc: "bad char between minutes and seconds",
2963 data: `a = 2021-03-30 21:312:0`,
2964 msg: `expecting colon between minutes and seconds`,
2965 },
2966 {
2967 desc: "invalid hour value",
2968 data: `a=1979-05-27T90:+2:99`,
2969 msg: `hour cannot be greater 23`,
2970 },
2971 {
2972 desc: "invalid minutes value",
2973 data: `a=1979-05-27T23:+2:99`,
2974 msg: `expected digit (0-9)`,
2975 },
2976 {
2977 desc: "invalid seconds value",
2978 data: `a=1979-05-27T12:45:99`,
2979 msg: `seconds cannot be greater 60`,
2980 },
2981 {
2982 desc: `binary with invalid digit`,
2983 data: `a = 0bf`,
2984 },
2985 {
2986 desc: `invalid i in dec`,
2987 data: `a = 0i`,
2988 },
2989 {
2990 desc: `invalid n in dec`,
2991 data: `a = 0n`,
2992 },
2993 {
2994 desc: `invalid unquoted key`,
2995 data: `a`,
2996 },
2997 {
2998 desc: "dt with tz has no time",
2999 data: `a = 2021-03-30TZ`,
3000 },
3001 {
3002 desc: "invalid end of array table",
3003 data: `[[a}`,
3004 },
3005 {
3006 desc: "invalid end of array table two",
3007 data: `[[a]}`,
3008 },
3009 {
3010 desc: "eof after equal",
3011 data: `a =`,
3012 },
3013 {
3014 desc: "invalid true boolean",
3015 data: `a = trois`,
3016 },
3017 {
3018 desc: "invalid false boolean",
3019 data: `a = faux`,
3020 },
3021 {
3022 desc: "inline table with incorrect separator",
3023 data: `a = {b=1;}`,
3024 },
3025 {
3026 desc: "inline table with invalid value",
3027 data: `a = {b=faux}`,
3028 },
3029 {
3030 desc: `incomplete array after whitespace`,
3031 data: `a = [ `,
3032 },
3033 {
3034 desc: `array with comma first`,
3035 data: `a = [ ,]`,
3036 },
3037 {
3038 desc: `array staring with incomplete newline`,
3039 data: "a = [\r]",
3040 },
3041 {
3042 desc: `array with incomplete newline after comma`,
3043 data: "a = [1,\r]",
3044 },
3045 {
3046 desc: `array with incomplete newline after value`,
3047 data: "a = [1\r]",
3048 },
3049 {
3050 desc: `invalid unicode in basic multiline string`,
3051 data: `A = """\u123"""`,
3052 },
3053 {
3054 desc: `invalid long unicode in basic multiline string`,
3055 data: `A = """\U0001D11"""`,
3056 },
3057 {
3058 desc: `invalid unicode in basic string`,
3059 data: `A = "\u123"`,
3060 },
3061 {
3062 desc: `invalid long unicode in basic string`,
3063 data: `A = "\U0001D11"`,
3064 },
3065 {
3066 desc: `invalid escape char basic multiline string`,
3067 data: `A = """\z"""`,
3068 },
3069 {
3070 desc: `invalid inf`,
3071 data: `A = ick`,
3072 },
3073 {
3074 desc: `invalid nan`,
3075 data: `A = non`,
3076 },
3077 {
3078 desc: `invalid character in comment in array`,
3079 data: "A = [#\x00\n]",
3080 },
3081 {
3082 desc: "invalid utf8 character in long string with no escape sequence",
3083 data: "a = \"aaaa\x80aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"",
3084 },
3085 {
3086 desc: "invalid ascii character in long string with no escape sequence",
3087 data: "a = \"aaaa\x00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"",
3088 },
3089 {
3090 desc: "unfinished 2-byte utf8 character in string with no escape sequence",
3091 data: "a = \"aaaa\xC2\"",
3092 },
3093 {
3094 desc: "unfinished 3-byte utf8 character in string with no escape sequence",
3095 data: "a = \"aaaa\xE2\x00\x00\"",
3096 },
3097 {
3098 desc: "invalid 3rd byte of 3-byte utf8 character in string with no escape sequence",
3099 data: "a = \"aaaa\xE2\x80\x00\"",
3100 },
3101 {
3102 desc: "invalid 4th byte of 4-byte utf8 character in string with no escape sequence",
3103 data: "a = \"aaaa\xF2\x81\x81\x00\"",
3104 },
3105 {
3106 desc: "unfinished 2-byte utf8 character in literal string",
3107 data: "a = 'aaa\xC2'",
3108 },
3109 {
3110 desc: "unfinished 3-byte utf8 character in literal string",
3111 data: "a = 'aaaa\xE2\x00\x00'",
3112 },
3113 {
3114 desc: "invalid 3rd byte of 3-byte utf8 character in literal string",
3115 data: "a = 'aaaa\xE2\x80\x00'",
3116 },
3117 {
3118 desc: "invalid 4th byte of 4-byte utf8 character in literal string",
3119 data: "a = 'aaaa\xF2\x81\x81\x00'",
3120 },
3121 {
3122 desc: "invalid start utf8 character in literal string",
3123 data: "a = '\x80'",
3124 },
3125 {
3126 desc: "utf8 character with not enough bytes before end in literal string",
3127 data: "a = '\xEF'",
3128 },
3129 {
3130 desc: "basic string with newline after the first escape code",
3131 data: "a = \"\\t\n\"",
3132 },
3133 {
3134 desc: "basic string with unfinished escape sequence after the first escape code",
3135 data: "a = \"\\t\\",
3136 },
3137 {
3138 desc: "basic string with unfinished after the first escape code",
3139 data: "a = \"\\t",
3140 },
3141 {
3142 desc: "multiline basic string with unfinished escape sequence after the first escape code",
3143 data: "a = \"\"\"\\t\\",
3144 },
3145 {
3146 desc: `impossible date-day`,
3147 data: `A = 2021-03-40T23:59:00`,
3148 msg: `impossible date`,
3149 },
3150 {
3151 desc: `leap day in non-leap year`,
3152 data: `A = 2021-02-29T23:59:00`,
3153 msg: `impossible date`,
3154 },
3155 {
3156 desc: `missing minute digit`,
3157 data: `a=17:4::01`,
3158 },
3159 {
3160 desc: `invalid space in year`,
3161 data: `i=19 7-12-21T10:32:00`,
3162 },
3163 {
3164 desc: `missing nanoseconds digits`,
3165 data: `a=17:45:56.`,
3166 },
3167 {
3168 desc: `minutes over 60`,
3169 data: `a=17:99:00`,
3170 },
3171 {
3172 desc: `invalid second`,
3173 data: `a=17:00::0`,
3174 },
3175 {
3176 desc: `invalid hour`,
3177 data: `a=1::00:00`,
3178 },
3179 {
3180 desc: `invalid month`,
3181 data: `a=2021-0--29`,
3182 },
3183 {
3184 desc: `zero is an invalid day`,
3185 data: `a=2021-11-00`,
3186 },
3187 {
3188 desc: `zero is an invalid month`,
3189 data: `a=2021-00-11`,
3190 },
3191 {
3192 desc: `invalid number of seconds digits with trailing digit`,
3193 data: `a=0000-01-01 00:00:000000Z3`,
3194 },
3195 {
3196 desc: `invalid zone offset hours`,
3197 data: `a=0000-01-01 00:00:00+24:00`,
3198 },
3199 {
3200 desc: `invalid zone offset minutes`,
3201 data: `a=0000-01-01 00:00:00+00:60`,
3202 },
3203 {
3204 desc: `invalid character in zone offset hours`,
3205 data: `a=0000-01-01 00:00:00+0Z:00`,
3206 },
3207 {
3208 desc: `invalid character in zone offset minutes`,
3209 data: `a=0000-01-01 00:00:00+00:0Z`,
3210 },
3211 {
3212 desc: `invalid number of seconds`,
3213 data: `a=0000-01-01 00:00:00+27000`,
3214 },
3215 {
3216 desc: `carriage return inside basic key`,
3217 data: "\"\r\"=42",
3218 },
3219 {
3220 desc: `carriage return inside literal key`,
3221 data: "'\r'=42",
3222 },
3223 {
3224 desc: `carriage return inside basic string`,
3225 data: "A = \"\r\"",
3226 },
3227 {
3228 desc: `carriage return inside basic multiline string`,
3229 data: "a=\"\"\"\r\"\"\"",
3230 },
3231 {
3232 desc: `carriage return at the trail of basic multiline string`,
3233 data: "a=\"\"\"\r",
3234 },
3235 {
3236 desc: `carriage return inside literal string`,
3237 data: "A = '\r'",
3238 },
3239 {
3240 desc: `carriage return inside multiline literal string`,
3241 data: "a='''\r'''",
3242 },
3243 {
3244 desc: `carriage return at trail of multiline literal string`,
3245 data: "a='''\r",
3246 },
3247 {
3248 desc: `carriage return in comment`,
3249 data: "# this is a test\ra=1",
3250 },
3251 {
3252 desc: `backspace in comment`,
3253 data: "# this is a test\ba=1",
3254 },
3255 }
3256
3257 for _, e := range examples {
3258 e := e
3259 t.Run(e.desc, func(t *testing.T) {
3260 m := map[string]interface{}{}
3261 err := toml.Unmarshal([]byte(e.data), &m)
3262 require.Error(t, err)
3263
3264 var de *toml.DecodeError
3265 if !errors.As(err, &de) {
3266 t.Fatalf("err should have been a *toml.DecodeError, but got %s (%T)", err, err)
3267 }
3268
3269 if e.msg != "" {
3270 t.Log("\n" + de.String())
3271 require.Equal(t, "toml: "+e.msg, de.Error())
3272 }
3273 })
3274 }
3275 }
3276
3277 func TestOmitEmpty(t *testing.T) {
3278 type inner struct {
3279 private string
3280 Skip string `toml:"-"`
3281 V string
3282 }
3283
3284 type elem struct {
3285 Foo string `toml:",omitempty"`
3286 Bar string `toml:",omitempty"`
3287 Inner inner `toml:",omitempty"`
3288 }
3289
3290 type doc struct {
3291 X []elem `toml:",inline"`
3292 }
3293
3294 d := doc{X: []elem{elem{
3295 Foo: "test",
3296 Inner: inner{
3297 V: "alue",
3298 },
3299 }}}
3300
3301 b, err := toml.Marshal(d)
3302 require.NoError(t, err)
3303
3304 require.Equal(t, "X = [{Foo = 'test', Inner = {V = 'alue'}}]\n", string(b))
3305 }
3306
3307 func TestUnmarshalTags(t *testing.T) {
3308 type doc struct {
3309 Dash string `toml:"-,"`
3310 Ignore string `toml:"-"`
3311 A string `toml:"hello"`
3312 B string `toml:"comma,omitempty"`
3313 }
3314
3315 data := `
3316 '-' = "dash"
3317 Ignore = 'me'
3318 hello = 'content'
3319 comma = 'ok'
3320 `
3321
3322 d := doc{}
3323 expected := doc{
3324 Dash: "dash",
3325 Ignore: "",
3326 A: "content",
3327 B: "ok",
3328 }
3329
3330 err := toml.Unmarshal([]byte(data), &d)
3331 require.NoError(t, err)
3332 require.Equal(t, expected, d)
3333 }
3334
3335 func TestASCIIControlCharacters(t *testing.T) {
3336 invalidCharacters := []byte{0x7F}
3337 for c := byte(0x0); c <= 0x08; c++ {
3338 invalidCharacters = append(invalidCharacters, c)
3339 }
3340 for c := byte(0x0B); c <= 0x0C; c++ {
3341 invalidCharacters = append(invalidCharacters, c)
3342 }
3343 for c := byte(0x0E); c <= 0x1F; c++ {
3344 invalidCharacters = append(invalidCharacters, c)
3345 }
3346
3347 type stringType struct {
3348 Delimiter string
3349 CanEscape bool
3350 }
3351
3352 stringTypes := map[string]stringType{
3353 "basic": {Delimiter: "\"", CanEscape: true},
3354 "basicMultiline": {Delimiter: "\"\"\"", CanEscape: true},
3355 "literal": {Delimiter: "'", CanEscape: false},
3356 "literalMultiline": {Delimiter: "'''", CanEscape: false},
3357 }
3358
3359 checkError := func(t *testing.T, input []byte) {
3360 t.Helper()
3361 m := map[string]interface{}{}
3362 err := toml.Unmarshal(input, &m)
3363 require.Error(t, err)
3364
3365 var de *toml.DecodeError
3366 if !errors.As(err, &de) {
3367 t.Fatalf("err should have been a *toml.DecodeError, but got %s (%T)", err, err)
3368 }
3369 }
3370
3371 for name, st := range stringTypes {
3372 t.Run(name, func(t *testing.T) {
3373 for _, c := range invalidCharacters {
3374 name := fmt.Sprintf("%2X", c)
3375 t.Run(name, func(t *testing.T) {
3376 data := []byte("A = " + st.Delimiter + string(c) + st.Delimiter)
3377 checkError(t, data)
3378
3379 if st.CanEscape {
3380 t.Run("withEscapeBefore", func(t *testing.T) {
3381 data := []byte("A = " + st.Delimiter + "\\t" + string(c) + st.Delimiter)
3382 checkError(t, data)
3383 })
3384 t.Run("withEscapeAfter", func(t *testing.T) {
3385 data := []byte("A = " + st.Delimiter + string(c) + "\\t" + st.Delimiter)
3386 checkError(t, data)
3387 })
3388 }
3389 })
3390 }
3391 })
3392 }
3393 }
3394
3395
3396 func TestLocalDateTime(t *testing.T) {
3397 examples := []struct {
3398 desc string
3399 input string
3400 prec int
3401 }{
3402 {
3403 desc: "9 digits zero nanoseconds",
3404 input: "2006-01-02T15:04:05.000000000",
3405 prec: 9,
3406 },
3407 {
3408 desc: "9 digits",
3409 input: "2006-01-02T15:04:05.123456789",
3410 prec: 9,
3411 },
3412 {
3413 desc: "8 digits",
3414 input: "2006-01-02T15:04:05.12345678",
3415 prec: 8,
3416 },
3417 {
3418 desc: "7 digits",
3419 input: "2006-01-02T15:04:05.1234567",
3420 prec: 7,
3421 },
3422 {
3423 desc: "6 digits",
3424 input: "2006-01-02T15:04:05.123456",
3425 prec: 6,
3426 },
3427 {
3428 desc: "5 digits",
3429 input: "2006-01-02T15:04:05.12345",
3430 prec: 5,
3431 },
3432 {
3433 desc: "4 digits",
3434 input: "2006-01-02T15:04:05.1234",
3435 prec: 4,
3436 },
3437 {
3438 desc: "3 digits",
3439 input: "2006-01-02T15:04:05.123",
3440 prec: 3,
3441 },
3442 {
3443 desc: "2 digits",
3444 input: "2006-01-02T15:04:05.12",
3445 prec: 2,
3446 },
3447 {
3448 desc: "1 digit",
3449 input: "2006-01-02T15:04:05.1",
3450 prec: 1,
3451 },
3452 {
3453 desc: "0 digit",
3454 input: "2006-01-02T15:04:05",
3455 },
3456 }
3457
3458 for _, e := range examples {
3459 e := e
3460 t.Run(e.desc, func(t *testing.T) {
3461 t.Log("input:", e.input)
3462 doc := `a = ` + e.input
3463 m := map[string]toml.LocalDateTime{}
3464 err := toml.Unmarshal([]byte(doc), &m)
3465 require.NoError(t, err)
3466 actual := m["a"]
3467 golang, err := time.Parse("2006-01-02T15:04:05.999999999", e.input)
3468 require.NoError(t, err)
3469 expected := toml.LocalDateTime{
3470 toml.LocalDate{golang.Year(), int(golang.Month()), golang.Day()},
3471 toml.LocalTime{golang.Hour(), golang.Minute(), golang.Second(), golang.Nanosecond(), e.prec},
3472 }
3473 require.Equal(t, expected, actual)
3474 })
3475 }
3476 }
3477
3478 func TestUnmarshal_RecursiveTable(t *testing.T) {
3479 type Foo struct {
3480 I int
3481 F *Foo
3482 }
3483
3484 examples := []struct {
3485 desc string
3486 input string
3487 expected string
3488 err bool
3489 }{
3490 {
3491 desc: "simplest",
3492 input: `
3493 I=1
3494 `,
3495 expected: `{"I":1,"F":null}`,
3496 },
3497 {
3498 desc: "depth 1",
3499 input: `
3500 I=1
3501 [F]
3502 I=2
3503 `,
3504 expected: `{"I":1,"F":{"I":2,"F":null}}`,
3505 },
3506 {
3507 desc: "depth 3",
3508 input: `
3509 I=1
3510 [F]
3511 I=2
3512 [F.F]
3513 I=3
3514 `,
3515 expected: `{"I":1,"F":{"I":2,"F":{"I":3,"F":null}}}`,
3516 },
3517 {
3518 desc: "depth 4",
3519 input: `
3520 I=1
3521 [F]
3522 I=2
3523 [F.F]
3524 I=3
3525 [F.F.F]
3526 I=4
3527 `,
3528 expected: `{"I":1,"F":{"I":2,"F":{"I":3,"F":{"I":4,"F":null}}}}`,
3529 },
3530 {
3531 desc: "skip mid step",
3532 input: `
3533 I=1
3534 [F.F]
3535 I=7
3536 `,
3537 expected: `{"I":1,"F":{"I":0,"F":{"I":7,"F":null}}}`,
3538 },
3539 }
3540
3541 for _, ex := range examples {
3542 e := ex
3543 t.Run(e.desc, func(t *testing.T) {
3544 foo := Foo{}
3545 err := toml.Unmarshal([]byte(e.input), &foo)
3546 if e.err {
3547 require.Error(t, err)
3548 } else {
3549 require.NoError(t, err)
3550 j, err := json.Marshal(foo)
3551 require.NoError(t, err)
3552 assert.Equal(t, e.expected, string(j))
3553 }
3554 })
3555 }
3556 }
3557
3558 func TestUnmarshal_RecursiveTableArray(t *testing.T) {
3559 type Foo struct {
3560 I int
3561 F []*Foo
3562 }
3563
3564 examples := []struct {
3565 desc string
3566 input string
3567 expected string
3568 err bool
3569 }{
3570 {
3571 desc: "simplest",
3572 input: `
3573 I=1
3574 F=[]
3575 `,
3576 expected: `{"I":1,"F":[]}`,
3577 },
3578 {
3579 desc: "depth 1",
3580 input: `
3581 I=1
3582 [[F]]
3583 I=2
3584 F=[]
3585 `,
3586 expected: `{"I":1,"F":[{"I":2,"F":[]}]}`,
3587 },
3588 {
3589 desc: "depth 2",
3590 input: `
3591 I=1
3592 [[F]]
3593 I=2
3594 [[F.F]]
3595 I=3
3596 F=[]
3597 `,
3598 expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[]}]}]}`,
3599 },
3600 {
3601 desc: "depth 3",
3602 input: `
3603 I=1
3604 [[F]]
3605 I=2
3606 [[F.F]]
3607 I=3
3608 [[F.F.F]]
3609 I=4
3610 F=[]
3611 `,
3612 expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[{"I":4,"F":[]}]}]}]}`,
3613 },
3614 {
3615 desc: "depth 4",
3616 input: `
3617 I=1
3618 [[F]]
3619 I=2
3620 [[F.F]]
3621 I=3
3622 [[F.F.F]]
3623 I=4
3624 [[F.F.F.F]]
3625 I=5
3626 F=[]
3627 `,
3628 expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[{"I":4,"F":[{"I":5,"F":[]}]}]}]}]}`,
3629 },
3630 }
3631
3632 for _, ex := range examples {
3633 e := ex
3634 t.Run(e.desc, func(t *testing.T) {
3635 foo := Foo{}
3636 err := toml.Unmarshal([]byte(e.input), &foo)
3637 if e.err {
3638 require.Error(t, err)
3639 } else {
3640 require.NoError(t, err)
3641 j, err := json.Marshal(foo)
3642 require.NoError(t, err)
3643 assert.Equal(t, e.expected, string(j))
3644 }
3645 })
3646 }
3647 }
3648
3649 func TestUnmarshalEmbedNonString(t *testing.T) {
3650 type Foo []byte
3651 type doc struct {
3652 Foo
3653 }
3654
3655 d := doc{}
3656
3657 err := toml.Unmarshal([]byte(`foo = 'bar'`), &d)
3658 require.NoError(t, err)
3659 require.Nil(t, d.Foo)
3660 }
3661
3662 func TestUnmarshal_Nil(t *testing.T) {
3663 type Foo struct {
3664 Foo *Foo `toml:"foo,omitempty"`
3665 Bar *Foo `toml:"bar,omitempty"`
3666 }
3667
3668 examples := []struct {
3669 desc string
3670 input string
3671 expected string
3672 err bool
3673 }{
3674 {
3675 desc: "empty",
3676 input: ``,
3677 expected: ``,
3678 },
3679 {
3680 desc: "simplest",
3681 input: `
3682 [foo]
3683 [foo.foo]
3684 `,
3685 expected: "[foo]\n[foo.foo]\n",
3686 },
3687 }
3688
3689 for _, ex := range examples {
3690 e := ex
3691 t.Run(e.desc, func(t *testing.T) {
3692 foo := Foo{}
3693 err := toml.Unmarshal([]byte(e.input), &foo)
3694 if e.err {
3695 require.Error(t, err)
3696 } else {
3697 require.NoError(t, err)
3698 j, err := toml.Marshal(foo)
3699 require.NoError(t, err)
3700 assert.Equal(t, e.expected, string(j))
3701 }
3702 })
3703 }
3704 }
3705
View as plain text