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