1package toml
2
3import (
4	"fmt"
5	"log"
6	"math"
7	"reflect"
8	"strings"
9	"testing"
10	"time"
11)
12
13func TestDecodeSimple(t *testing.T) {
14	var testSimple = `
15age = 250
16andrew = "gallant"
17kait = "brady"
18now = 1987-07-05T05:45:00Z
19nowEast = 2017-06-22T16:15:21+08:00
20nowWest = 2017-06-22T02:14:36-06:00
21yesOrNo = true
22pi = 3.14
23colors = [
24	["red", "green", "blue"],
25	["cyan", "magenta", "yellow", "black"],
26]
27
28[My.Cats]
29plato = "cat 1"
30cauchy = "cat 2"
31`
32
33	type cats struct {
34		Plato  string
35		Cauchy string
36	}
37	type simple struct {
38		Age     int
39		Colors  [][]string
40		Pi      float64
41		YesOrNo bool
42		Now     time.Time
43		NowEast time.Time
44		NowWest time.Time
45		Andrew  string
46		Kait    string
47		My      map[string]cats
48	}
49
50	var val simple
51	_, err := Decode(testSimple, &val)
52	if err != nil {
53		t.Fatal(err)
54	}
55
56	now, err := time.Parse("2006-01-02T15:04:05", "1987-07-05T05:45:00")
57	if err != nil {
58		panic(err)
59	}
60	nowEast, err := time.Parse("2006-01-02T15:04:05-07:00", "2017-06-22T16:15:21+08:00")
61	if err != nil {
62		panic(err)
63	}
64	nowWest, err := time.Parse("2006-01-02T15:04:05-07:00", "2017-06-22T02:14:36-06:00")
65	if err != nil {
66		panic(err)
67	}
68	var answer = simple{
69		Age:     250,
70		Andrew:  "gallant",
71		Kait:    "brady",
72		Now:     now,
73		NowEast: nowEast,
74		NowWest: nowWest,
75		YesOrNo: true,
76		Pi:      3.14,
77		Colors: [][]string{
78			{"red", "green", "blue"},
79			{"cyan", "magenta", "yellow", "black"},
80		},
81		My: map[string]cats{
82			"Cats": {Plato: "cat 1", Cauchy: "cat 2"},
83		},
84	}
85	if !reflect.DeepEqual(val, answer) {
86		t.Fatalf("Expected\n-----\n%#v\n-----\nbut got\n-----\n%#v\n",
87			answer, val)
88	}
89}
90
91func TestDecodeEmbedded(t *testing.T) {
92	type Dog struct{ Name string }
93	type Age int
94	type cat struct{ Name string }
95
96	for _, test := range []struct {
97		label       string
98		input       string
99		decodeInto  interface{}
100		wantDecoded interface{}
101	}{
102		{
103			label:       "embedded struct",
104			input:       `Name = "milton"`,
105			decodeInto:  &struct{ Dog }{},
106			wantDecoded: &struct{ Dog }{Dog{"milton"}},
107		},
108		{
109			label:       "embedded non-nil pointer to struct",
110			input:       `Name = "milton"`,
111			decodeInto:  &struct{ *Dog }{},
112			wantDecoded: &struct{ *Dog }{&Dog{"milton"}},
113		},
114		{
115			label:       "embedded nil pointer to struct",
116			input:       ``,
117			decodeInto:  &struct{ *Dog }{},
118			wantDecoded: &struct{ *Dog }{nil},
119		},
120		{
121			label:       "unexported embedded struct",
122			input:       `Name = "socks"`,
123			decodeInto:  &struct{ cat }{},
124			wantDecoded: &struct{ cat }{cat{"socks"}},
125		},
126		{
127			label:       "embedded int",
128			input:       `Age = -5`,
129			decodeInto:  &struct{ Age }{},
130			wantDecoded: &struct{ Age }{-5},
131		},
132	} {
133		_, err := Decode(test.input, test.decodeInto)
134		if err != nil {
135			t.Fatal(err)
136		}
137		if !reflect.DeepEqual(test.wantDecoded, test.decodeInto) {
138			t.Errorf("%s: want decoded == %+v, got %+v",
139				test.label, test.wantDecoded, test.decodeInto)
140		}
141	}
142}
143
144func TestDecodeIgnoredFields(t *testing.T) {
145	type simple struct {
146		Number int `toml:"-"`
147	}
148	const input = `
149Number = 123
150- = 234
151`
152	var s simple
153	if _, err := Decode(input, &s); err != nil {
154		t.Fatal(err)
155	}
156	if s.Number != 0 {
157		t.Errorf("got: %d; want 0", s.Number)
158	}
159}
160
161func TestTableArrays(t *testing.T) {
162	var tomlTableArrays = `
163[[albums]]
164name = "Born to Run"
165
166  [[albums.songs]]
167  name = "Jungleland"
168
169  [[albums.songs]]
170  name = "Meeting Across the River"
171
172[[albums]]
173name = "Born in the USA"
174
175  [[albums.songs]]
176  name = "Glory Days"
177
178  [[albums.songs]]
179  name = "Dancing in the Dark"
180`
181
182	type Song struct {
183		Name string
184	}
185
186	type Album struct {
187		Name  string
188		Songs []Song
189	}
190
191	type Music struct {
192		Albums []Album
193	}
194
195	expected := Music{[]Album{
196		{"Born to Run", []Song{{"Jungleland"}, {"Meeting Across the River"}}},
197		{"Born in the USA", []Song{{"Glory Days"}, {"Dancing in the Dark"}}},
198	}}
199	var got Music
200	if _, err := Decode(tomlTableArrays, &got); err != nil {
201		t.Fatal(err)
202	}
203	if !reflect.DeepEqual(expected, got) {
204		t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
205	}
206}
207
208func TestTableNesting(t *testing.T) {
209	for _, tt := range []struct {
210		t    string
211		want []string
212	}{
213		{"[a.b.c]", []string{"a", "b", "c"}},
214		{`[a."b.c"]`, []string{"a", "b.c"}},
215		{`[a.'b.c']`, []string{"a", "b.c"}},
216		{`[a.' b ']`, []string{"a", " b "}},
217		{"[ d.e.f ]", []string{"d", "e", "f"}},
218		{"[ g . h . i ]", []string{"g", "h", "i"}},
219		{`[ j . "ʞ" . 'l' ]`, []string{"j", "ʞ", "l"}},
220	} {
221		var m map[string]interface{}
222		if _, err := Decode(tt.t, &m); err != nil {
223			t.Errorf("Decode(%q): got error: %s", tt.t, err)
224			continue
225		}
226		if keys := extractNestedKeys(m); !reflect.DeepEqual(keys, tt.want) {
227			t.Errorf("Decode(%q): got nested keys %#v; want %#v",
228				tt.t, keys, tt.want)
229		}
230	}
231}
232
233func extractNestedKeys(v map[string]interface{}) []string {
234	var result []string
235	for {
236		if len(v) != 1 {
237			return result
238		}
239		for k, m := range v {
240			result = append(result, k)
241			var ok bool
242			v, ok = m.(map[string]interface{})
243			if !ok {
244				return result
245			}
246		}
247
248	}
249}
250
251// Case insensitive matching tests.
252// A bit more comprehensive than needed given the current implementation,
253// but implementations change.
254// Probably still missing demonstrations of some ugly corner cases regarding
255// case insensitive matching and multiple fields.
256func TestCase(t *testing.T) {
257	var caseToml = `
258tOpString = "string"
259tOpInt = 1
260tOpFloat = 1.1
261tOpBool = true
262tOpdate = 2006-01-02T15:04:05Z
263tOparray = [ "array" ]
264Match = "i should be in Match only"
265MatcH = "i should be in MatcH only"
266once = "just once"
267[nEst.eD]
268nEstedString = "another string"
269`
270
271	type InsensitiveEd struct {
272		NestedString string
273	}
274
275	type InsensitiveNest struct {
276		Ed InsensitiveEd
277	}
278
279	type Insensitive struct {
280		TopString string
281		TopInt    int
282		TopFloat  float64
283		TopBool   bool
284		TopDate   time.Time
285		TopArray  []string
286		Match     string
287		MatcH     string
288		Once      string
289		OncE      string
290		Nest      InsensitiveNest
291	}
292
293	tme, err := time.Parse(time.RFC3339, time.RFC3339[:len(time.RFC3339)-5])
294	if err != nil {
295		panic(err)
296	}
297	expected := Insensitive{
298		TopString: "string",
299		TopInt:    1,
300		TopFloat:  1.1,
301		TopBool:   true,
302		TopDate:   tme,
303		TopArray:  []string{"array"},
304		MatcH:     "i should be in MatcH only",
305		Match:     "i should be in Match only",
306		Once:      "just once",
307		OncE:      "",
308		Nest: InsensitiveNest{
309			Ed: InsensitiveEd{NestedString: "another string"},
310		},
311	}
312	var got Insensitive
313	if _, err := Decode(caseToml, &got); err != nil {
314		t.Fatal(err)
315	}
316	if !reflect.DeepEqual(expected, got) {
317		t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
318	}
319}
320
321func TestPointers(t *testing.T) {
322	type Object struct {
323		Type        string
324		Description string
325	}
326
327	type Dict struct {
328		NamedObject map[string]*Object
329		BaseObject  *Object
330		Strptr      *string
331		Strptrs     []*string
332	}
333	s1, s2, s3 := "blah", "abc", "def"
334	expected := &Dict{
335		Strptr:  &s1,
336		Strptrs: []*string{&s2, &s3},
337		NamedObject: map[string]*Object{
338			"foo": {"FOO", "fooooo!!!"},
339			"bar": {"BAR", "ba-ba-ba-ba-barrrr!!!"},
340		},
341		BaseObject: &Object{"BASE", "da base"},
342	}
343
344	ex1 := `
345Strptr = "blah"
346Strptrs = ["abc", "def"]
347
348[NamedObject.foo]
349Type = "FOO"
350Description = "fooooo!!!"
351
352[NamedObject.bar]
353Type = "BAR"
354Description = "ba-ba-ba-ba-barrrr!!!"
355
356[BaseObject]
357Type = "BASE"
358Description = "da base"
359`
360	dict := new(Dict)
361	_, err := Decode(ex1, dict)
362	if err != nil {
363		t.Errorf("Decode error: %v", err)
364	}
365	if !reflect.DeepEqual(expected, dict) {
366		t.Fatalf("\n%#v\n!=\n%#v\n", expected, dict)
367	}
368}
369
370func TestDecodeDatetime(t *testing.T) {
371	const noTimestamp = "2006-01-02T15:04:05"
372	for _, tt := range []struct {
373		s      string
374		t      string
375		format string
376	}{
377		{"1979-05-27T07:32:00Z", "1979-05-27T07:32:00Z", time.RFC3339},
378		{"1979-05-27T00:32:00-07:00", "1979-05-27T00:32:00-07:00", time.RFC3339},
379		{
380			"1979-05-27T00:32:00.999999-07:00",
381			"1979-05-27T00:32:00.999999-07:00",
382			time.RFC3339,
383		},
384		{"1979-05-27T07:32:00", "1979-05-27T07:32:00", noTimestamp},
385		{
386			"1979-05-27T00:32:00.999999",
387			"1979-05-27T00:32:00.999999",
388			noTimestamp,
389		},
390		{"1979-05-27", "1979-05-27T00:00:00", noTimestamp},
391	} {
392		var x struct{ D time.Time }
393		input := "d = " + tt.s
394		if _, err := Decode(input, &x); err != nil {
395			t.Errorf("Decode(%q): got error: %s", input, err)
396			continue
397		}
398		want, err := time.ParseInLocation(tt.format, tt.t, time.Local)
399		if err != nil {
400			panic(err)
401		}
402		if !x.D.Equal(want) {
403			t.Errorf("Decode(%q): got %s; want %s", input, x.D, want)
404		}
405	}
406}
407
408func TestDecodeBadDatetime(t *testing.T) {
409	var x struct{ T time.Time }
410	for _, s := range []string{
411		"123",
412		"2006-01-50T00:00:00Z",
413		"2006-01-30T00:00",
414		"2006-01-30T",
415	} {
416		input := "T = " + s
417		if _, err := Decode(input, &x); err == nil {
418			t.Errorf("Expected invalid DateTime error for %q", s)
419		}
420	}
421}
422
423func TestDecodeMultilineStrings(t *testing.T) {
424	var x struct {
425		S string
426	}
427	const s0 = `s = """
428a b \n c
429d e f
430"""`
431	if _, err := Decode(s0, &x); err != nil {
432		t.Fatal(err)
433	}
434	if want := "a b \n c\nd e f\n"; x.S != want {
435		t.Errorf("got: %q; want: %q", x.S, want)
436	}
437	const s1 = `s = """a b c\
438"""`
439	if _, err := Decode(s1, &x); err != nil {
440		t.Fatal(err)
441	}
442	if want := "a b c"; x.S != want {
443		t.Errorf("got: %q; want: %q", x.S, want)
444	}
445}
446
447type sphere struct {
448	Center [3]float64
449	Radius float64
450}
451
452func TestDecodeSimpleArray(t *testing.T) {
453	var s1 sphere
454	if _, err := Decode(`center = [0.0, 1.5, 0.0]`, &s1); err != nil {
455		t.Fatal(err)
456	}
457}
458
459func TestDecodeArrayWrongSize(t *testing.T) {
460	var s1 sphere
461	if _, err := Decode(`center = [0.1, 2.3]`, &s1); err == nil {
462		t.Fatal("Expected array type mismatch error")
463	}
464}
465
466func TestDecodeLargeIntoSmallInt(t *testing.T) {
467	type table struct {
468		Value int8
469	}
470	var tab table
471	if _, err := Decode(`value = 500`, &tab); err == nil {
472		t.Fatal("Expected integer out-of-bounds error.")
473	}
474}
475
476func TestDecodeSizedInts(t *testing.T) {
477	type table struct {
478		U8  uint8
479		U16 uint16
480		U32 uint32
481		U64 uint64
482		U   uint
483		I8  int8
484		I16 int16
485		I32 int32
486		I64 int64
487		I   int
488	}
489	answer := table{1, 1, 1, 1, 1, -1, -1, -1, -1, -1}
490	toml := `
491	u8 = 1
492	u16 = 1
493	u32 = 1
494	u64 = 1
495	u = 1
496	i8 = -1
497	i16 = -1
498	i32 = -1
499	i64 = -1
500	i = -1
501	`
502	var tab table
503	if _, err := Decode(toml, &tab); err != nil {
504		t.Fatal(err.Error())
505	}
506	if answer != tab {
507		t.Fatalf("Expected %#v but got %#v", answer, tab)
508	}
509}
510
511func TestDecodeInts(t *testing.T) {
512	for _, tt := range []struct {
513		s    string
514		want int64
515	}{
516		{"0", 0},
517		{"+99", 99},
518		{"-10", -10},
519		{"1_234_567", 1234567},
520		{"1_2_3_4", 1234},
521		{"-9_223_372_036_854_775_808", math.MinInt64},
522		{"9_223_372_036_854_775_807", math.MaxInt64},
523	} {
524		var x struct{ N int64 }
525		input := "n = " + tt.s
526		if _, err := Decode(input, &x); err != nil {
527			t.Errorf("Decode(%q): got error: %s", input, err)
528			continue
529		}
530		if x.N != tt.want {
531			t.Errorf("Decode(%q): got %d; want %d", input, x.N, tt.want)
532		}
533	}
534}
535
536func TestDecodeFloats(t *testing.T) {
537	for _, tt := range []struct {
538		s    string
539		want float64
540	}{
541		{"+1.0", 1},
542		{"3.1415", 3.1415},
543		{"-0.01", -0.01},
544		{"5e+22", 5e22},
545		{"1e6", 1e6},
546		{"-2E-2", -2e-2},
547		{"6.626e-34", 6.626e-34},
548		{"9_224_617.445_991_228_313", 9224617.445991228313},
549		{"9_876.54_32e1_0", 9876.5432e10},
550	} {
551		var x struct{ N float64 }
552		input := "n = " + tt.s
553		if _, err := Decode(input, &x); err != nil {
554			t.Errorf("Decode(%q): got error: %s", input, err)
555			continue
556		}
557		if x.N != tt.want {
558			t.Errorf("Decode(%q): got %f; want %f", input, x.N, tt.want)
559		}
560	}
561}
562
563func TestDecodeMalformedNumbers(t *testing.T) {
564	for _, tt := range []struct {
565		s    string
566		want string
567	}{
568		{"++99", "expected a digit"},
569		{"0..1", "must be followed by one or more digits"},
570		{"0.1.2", "Invalid float value"},
571		{"1e2.3", "Invalid float value"},
572		{"1e2e3", "Invalid float value"},
573		{"_123", "expected value"},
574		{"123_", "surrounded by digits"},
575		{"1._23", "surrounded by digits"},
576		{"1e__23", "surrounded by digits"},
577		{"123.", "must be followed by one or more digits"},
578		{"1.e2", "must be followed by one or more digits"},
579	} {
580		var x struct{ N interface{} }
581		input := "n = " + tt.s
582		_, err := Decode(input, &x)
583		if err == nil {
584			t.Errorf("Decode(%q): got nil, want error containing %q",
585				input, tt.want)
586			continue
587		}
588		if !strings.Contains(err.Error(), tt.want) {
589			t.Errorf("Decode(%q): got %q, want error containing %q",
590				input, err, tt.want)
591		}
592	}
593}
594
595func TestDecodeBadValues(t *testing.T) {
596	for _, tt := range []struct {
597		v    interface{}
598		want string
599	}{
600		{3, "non-pointer int"},
601		{(*int)(nil), "nil"},
602	} {
603		_, err := Decode(`x = 3`, tt.v)
604		if err == nil {
605			t.Errorf("Decode(%v): got nil; want error containing %q",
606				tt.v, tt.want)
607			continue
608		}
609		if !strings.Contains(err.Error(), tt.want) {
610			t.Errorf("Decode(%v): got %q; want error containing %q",
611				tt.v, err, tt.want)
612		}
613	}
614}
615
616func TestUnmarshaler(t *testing.T) {
617
618	var tomlBlob = `
619[dishes.hamboogie]
620name = "Hamboogie with fries"
621price = 10.99
622
623[[dishes.hamboogie.ingredients]]
624name = "Bread Bun"
625
626[[dishes.hamboogie.ingredients]]
627name = "Lettuce"
628
629[[dishes.hamboogie.ingredients]]
630name = "Real Beef Patty"
631
632[[dishes.hamboogie.ingredients]]
633name = "Tomato"
634
635[dishes.eggsalad]
636name = "Egg Salad with rice"
637price = 3.99
638
639[[dishes.eggsalad.ingredients]]
640name = "Egg"
641
642[[dishes.eggsalad.ingredients]]
643name = "Mayo"
644
645[[dishes.eggsalad.ingredients]]
646name = "Rice"
647`
648	m := &menu{}
649	if _, err := Decode(tomlBlob, m); err != nil {
650		t.Fatal(err)
651	}
652
653	if len(m.Dishes) != 2 {
654		t.Log("two dishes should be loaded with UnmarshalTOML()")
655		t.Errorf("expected %d but got %d", 2, len(m.Dishes))
656	}
657
658	eggSalad := m.Dishes["eggsalad"]
659	if _, ok := interface{}(eggSalad).(dish); !ok {
660		t.Errorf("expected a dish")
661	}
662
663	if eggSalad.Name != "Egg Salad with rice" {
664		t.Errorf("expected the dish to be named 'Egg Salad with rice'")
665	}
666
667	if len(eggSalad.Ingredients) != 3 {
668		t.Log("dish should be loaded with UnmarshalTOML()")
669		t.Errorf("expected %d but got %d", 3, len(eggSalad.Ingredients))
670	}
671
672	found := false
673	for _, i := range eggSalad.Ingredients {
674		if i.Name == "Rice" {
675			found = true
676			break
677		}
678	}
679	if !found {
680		t.Error("Rice was not loaded in UnmarshalTOML()")
681	}
682
683	// test on a value - must be passed as *
684	o := menu{}
685	if _, err := Decode(tomlBlob, &o); err != nil {
686		t.Fatal(err)
687	}
688
689}
690
691func TestDecodeInlineTable(t *testing.T) {
692	input := `
693[CookieJar]
694Types = {Chocolate = "yummy", Oatmeal = "best ever"}
695
696[Seasons]
697Locations = {NY = {Temp = "not cold", Rating = 4}, MI = {Temp = "freezing", Rating = 9}}
698`
699	type cookieJar struct {
700		Types map[string]string
701	}
702	type properties struct {
703		Temp   string
704		Rating int
705	}
706	type seasons struct {
707		Locations map[string]properties
708	}
709	type wrapper struct {
710		CookieJar cookieJar
711		Seasons   seasons
712	}
713	var got wrapper
714
715	meta, err := Decode(input, &got)
716	if err != nil {
717		t.Fatal(err)
718	}
719	want := wrapper{
720		CookieJar: cookieJar{
721			Types: map[string]string{
722				"Chocolate": "yummy",
723				"Oatmeal":   "best ever",
724			},
725		},
726		Seasons: seasons{
727			Locations: map[string]properties{
728				"NY": {
729					Temp:   "not cold",
730					Rating: 4,
731				},
732				"MI": {
733					Temp:   "freezing",
734					Rating: 9,
735				},
736			},
737		},
738	}
739	if !reflect.DeepEqual(got, want) {
740		t.Fatalf("after decode, got:\n\n%#v\n\nwant:\n\n%#v", got, want)
741	}
742	if len(meta.keys) != 12 {
743		t.Errorf("after decode, got %d meta keys; want 12", len(meta.keys))
744	}
745	if len(meta.types) != 12 {
746		t.Errorf("after decode, got %d meta types; want 12", len(meta.types))
747	}
748}
749
750func TestDecodeInlineTableArray(t *testing.T) {
751	type point struct {
752		X, Y, Z int
753	}
754	var got struct {
755		Points []point
756	}
757	// Example inline table array from the spec.
758	const in = `
759points = [ { x = 1, y = 2, z = 3 },
760           { x = 7, y = 8, z = 9 },
761           { x = 2, y = 4, z = 8 } ]
762
763`
764	if _, err := Decode(in, &got); err != nil {
765		t.Fatal(err)
766	}
767	want := []point{
768		{X: 1, Y: 2, Z: 3},
769		{X: 7, Y: 8, Z: 9},
770		{X: 2, Y: 4, Z: 8},
771	}
772	if !reflect.DeepEqual(got.Points, want) {
773		t.Errorf("got %#v; want %#v", got.Points, want)
774	}
775}
776
777func TestDecodeMalformedInlineTable(t *testing.T) {
778	for _, tt := range []struct {
779		s    string
780		want string
781	}{
782		{"{,}", "unexpected comma"},
783		{"{x = 3 y = 4}", "expected a comma or an inline table terminator"},
784		{"{x=3,,y=4}", "unexpected comma"},
785		{"{x=3,\ny=4}", "newlines not allowed"},
786		{"{x=3\n,y=4}", "newlines not allowed"},
787	} {
788		var x struct{ A map[string]int }
789		input := "a = " + tt.s
790		_, err := Decode(input, &x)
791		if err == nil {
792			t.Errorf("Decode(%q): got nil, want error containing %q",
793				input, tt.want)
794			continue
795		}
796		if !strings.Contains(err.Error(), tt.want) {
797			t.Errorf("Decode(%q): got %q, want error containing %q",
798				input, err, tt.want)
799		}
800	}
801}
802
803type menu struct {
804	Dishes map[string]dish
805}
806
807func (m *menu) UnmarshalTOML(p interface{}) error {
808	m.Dishes = make(map[string]dish)
809	data, _ := p.(map[string]interface{})
810	dishes := data["dishes"].(map[string]interface{})
811	for n, v := range dishes {
812		if d, ok := v.(map[string]interface{}); ok {
813			nd := dish{}
814			nd.UnmarshalTOML(d)
815			m.Dishes[n] = nd
816		} else {
817			return fmt.Errorf("not a dish")
818		}
819	}
820	return nil
821}
822
823type dish struct {
824	Name        string
825	Price       float32
826	Ingredients []ingredient
827}
828
829func (d *dish) UnmarshalTOML(p interface{}) error {
830	data, _ := p.(map[string]interface{})
831	d.Name, _ = data["name"].(string)
832	d.Price, _ = data["price"].(float32)
833	ingredients, _ := data["ingredients"].([]map[string]interface{})
834	for _, e := range ingredients {
835		n, _ := interface{}(e).(map[string]interface{})
836		name, _ := n["name"].(string)
837		i := ingredient{name}
838		d.Ingredients = append(d.Ingredients, i)
839	}
840	return nil
841}
842
843type ingredient struct {
844	Name string
845}
846
847func TestDecodeSlices(t *testing.T) {
848	type T struct {
849		S []string
850	}
851	for i, tt := range []struct {
852		v     T
853		input string
854		want  T
855	}{
856		{T{}, "", T{}},
857		{T{[]string{}}, "", T{[]string{}}},
858		{T{[]string{"a", "b"}}, "", T{[]string{"a", "b"}}},
859		{T{}, "S = []", T{[]string{}}},
860		{T{[]string{}}, "S = []", T{[]string{}}},
861		{T{[]string{"a", "b"}}, "S = []", T{[]string{}}},
862		{T{}, `S = ["x"]`, T{[]string{"x"}}},
863		{T{[]string{}}, `S = ["x"]`, T{[]string{"x"}}},
864		{T{[]string{"a", "b"}}, `S = ["x"]`, T{[]string{"x"}}},
865	} {
866		if _, err := Decode(tt.input, &tt.v); err != nil {
867			t.Errorf("[%d] %s", i, err)
868			continue
869		}
870		if !reflect.DeepEqual(tt.v, tt.want) {
871			t.Errorf("[%d] got %#v; want %#v", i, tt.v, tt.want)
872		}
873	}
874}
875
876func TestDecodePrimitive(t *testing.T) {
877	type S struct {
878		P Primitive
879	}
880	type T struct {
881		S []int
882	}
883	slicep := func(s []int) *[]int { return &s }
884	arrayp := func(a [2]int) *[2]int { return &a }
885	mapp := func(m map[string]int) *map[string]int { return &m }
886	for i, tt := range []struct {
887		v     interface{}
888		input string
889		want  interface{}
890	}{
891		// slices
892		{slicep(nil), "", slicep(nil)},
893		{slicep([]int{}), "", slicep([]int{})},
894		{slicep([]int{1, 2, 3}), "", slicep([]int{1, 2, 3})},
895		{slicep(nil), "P = [1,2]", slicep([]int{1, 2})},
896		{slicep([]int{}), "P = [1,2]", slicep([]int{1, 2})},
897		{slicep([]int{1, 2, 3}), "P = [1,2]", slicep([]int{1, 2})},
898
899		// arrays
900		{arrayp([2]int{2, 3}), "", arrayp([2]int{2, 3})},
901		{arrayp([2]int{2, 3}), "P = [3,4]", arrayp([2]int{3, 4})},
902
903		// maps
904		{mapp(nil), "", mapp(nil)},
905		{mapp(map[string]int{}), "", mapp(map[string]int{})},
906		{mapp(map[string]int{"a": 1}), "", mapp(map[string]int{"a": 1})},
907		{mapp(nil), "[P]\na = 2", mapp(map[string]int{"a": 2})},
908		{mapp(map[string]int{}), "[P]\na = 2", mapp(map[string]int{"a": 2})},
909		{mapp(map[string]int{"a": 1, "b": 3}), "[P]\na = 2", mapp(map[string]int{"a": 2, "b": 3})},
910
911		// structs
912		{&T{nil}, "[P]", &T{nil}},
913		{&T{[]int{}}, "[P]", &T{[]int{}}},
914		{&T{[]int{1, 2, 3}}, "[P]", &T{[]int{1, 2, 3}}},
915		{&T{nil}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
916		{&T{[]int{}}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
917		{&T{[]int{1, 2, 3}}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
918	} {
919		var s S
920		md, err := Decode(tt.input, &s)
921		if err != nil {
922			t.Errorf("[%d] Decode error: %s", i, err)
923			continue
924		}
925		if err := md.PrimitiveDecode(s.P, tt.v); err != nil {
926			t.Errorf("[%d] PrimitiveDecode error: %s", i, err)
927			continue
928		}
929		if !reflect.DeepEqual(tt.v, tt.want) {
930			t.Errorf("[%d] got %#v; want %#v", i, tt.v, tt.want)
931		}
932	}
933}
934
935func TestDecodeErrors(t *testing.T) {
936	for _, s := range []string{
937		`x="`,
938		`x='`,
939		`x='''`,
940
941		// Cases found by fuzzing in
942		// https://github.com/BurntSushi/toml/issues/155.
943		`""�`,   // used to panic with index out of range
944		`e="""`, // used to hang
945	} {
946		var x struct{}
947		_, err := Decode(s, &x)
948		if err == nil {
949			t.Errorf("Decode(%q): got nil error", s)
950		}
951	}
952}
953
954// Test for https://github.com/BurntSushi/toml/pull/166.
955func TestDecodeBoolArray(t *testing.T) {
956	for _, tt := range []struct {
957		s    string
958		got  interface{}
959		want interface{}
960	}{
961		{
962			"a = [true, false]",
963			&struct{ A []bool }{},
964			&struct{ A []bool }{[]bool{true, false}},
965		},
966		{
967			"a = {a = true, b = false}",
968			&struct{ A map[string]bool }{},
969			&struct{ A map[string]bool }{map[string]bool{"a": true, "b": false}},
970		},
971	} {
972		if _, err := Decode(tt.s, tt.got); err != nil {
973			t.Errorf("Decode(%q): %s", tt.s, err)
974			continue
975		}
976		if !reflect.DeepEqual(tt.got, tt.want) {
977			t.Errorf("Decode(%q): got %#v; want %#v", tt.s, tt.got, tt.want)
978		}
979	}
980}
981
982func ExampleMetaData_PrimitiveDecode() {
983	var md MetaData
984	var err error
985
986	var tomlBlob = `
987ranking = ["Springsteen", "J Geils"]
988
989[bands.Springsteen]
990started = 1973
991albums = ["Greetings", "WIESS", "Born to Run", "Darkness"]
992
993[bands."J Geils"]
994started = 1970
995albums = ["The J. Geils Band", "Full House", "Blow Your Face Out"]
996`
997
998	type band struct {
999		Started int
1000		Albums  []string
1001	}
1002	type classics struct {
1003		Ranking []string
1004		Bands   map[string]Primitive
1005	}
1006
1007	// Do the initial decode. Reflection is delayed on Primitive values.
1008	var music classics
1009	if md, err = Decode(tomlBlob, &music); err != nil {
1010		log.Fatal(err)
1011	}
1012
1013	// MetaData still includes information on Primitive values.
1014	fmt.Printf("Is `bands.Springsteen` defined? %v\n",
1015		md.IsDefined("bands", "Springsteen"))
1016
1017	// Decode primitive data into Go values.
1018	for _, artist := range music.Ranking {
1019		// A band is a primitive value, so we need to decode it to get a
1020		// real `band` value.
1021		primValue := music.Bands[artist]
1022
1023		var aBand band
1024		if err = md.PrimitiveDecode(primValue, &aBand); err != nil {
1025			log.Fatal(err)
1026		}
1027		fmt.Printf("%s started in %d.\n", artist, aBand.Started)
1028	}
1029	// Check to see if there were any fields left undecoded.
1030	// Note that this won't be empty before decoding the Primitive value!
1031	fmt.Printf("Undecoded: %q\n", md.Undecoded())
1032
1033	// Output:
1034	// Is `bands.Springsteen` defined? true
1035	// Springsteen started in 1973.
1036	// J Geils started in 1970.
1037	// Undecoded: []
1038}
1039
1040func ExampleDecode() {
1041	var tomlBlob = `
1042# Some comments.
1043[alpha]
1044ip = "10.0.0.1"
1045
1046	[alpha.config]
1047	Ports = [ 8001, 8002 ]
1048	Location = "Toronto"
1049	Created = 1987-07-05T05:45:00Z
1050
1051[beta]
1052ip = "10.0.0.2"
1053
1054	[beta.config]
1055	Ports = [ 9001, 9002 ]
1056	Location = "New Jersey"
1057	Created = 1887-01-05T05:55:00Z
1058`
1059
1060	type serverConfig struct {
1061		Ports    []int
1062		Location string
1063		Created  time.Time
1064	}
1065
1066	type server struct {
1067		IP     string       `toml:"ip,omitempty"`
1068		Config serverConfig `toml:"config"`
1069	}
1070
1071	type servers map[string]server
1072
1073	var config servers
1074	if _, err := Decode(tomlBlob, &config); err != nil {
1075		log.Fatal(err)
1076	}
1077
1078	for _, name := range []string{"alpha", "beta"} {
1079		s := config[name]
1080		fmt.Printf("Server: %s (ip: %s) in %s created on %s\n",
1081			name, s.IP, s.Config.Location,
1082			s.Config.Created.Format("2006-01-02"))
1083		fmt.Printf("Ports: %v\n", s.Config.Ports)
1084	}
1085
1086	// Output:
1087	// Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05
1088	// Ports: [8001 8002]
1089	// Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05
1090	// Ports: [9001 9002]
1091}
1092
1093type duration struct {
1094	time.Duration
1095}
1096
1097func (d *duration) UnmarshalText(text []byte) error {
1098	var err error
1099	d.Duration, err = time.ParseDuration(string(text))
1100	return err
1101}
1102
1103// Example Unmarshaler shows how to decode TOML strings into your own
1104// custom data type.
1105func Example_unmarshaler() {
1106	blob := `
1107[[song]]
1108name = "Thunder Road"
1109duration = "4m49s"
1110
1111[[song]]
1112name = "Stairway to Heaven"
1113duration = "8m03s"
1114`
1115	type song struct {
1116		Name     string
1117		Duration duration
1118	}
1119	type songs struct {
1120		Song []song
1121	}
1122	var favorites songs
1123	if _, err := Decode(blob, &favorites); err != nil {
1124		log.Fatal(err)
1125	}
1126
1127	// Code to implement the TextUnmarshaler interface for `duration`:
1128	//
1129	// type duration struct {
1130	// 	time.Duration
1131	// }
1132	//
1133	// func (d *duration) UnmarshalText(text []byte) error {
1134	// 	var err error
1135	// 	d.Duration, err = time.ParseDuration(string(text))
1136	// 	return err
1137	// }
1138
1139	for _, s := range favorites.Song {
1140		fmt.Printf("%s (%s)\n", s.Name, s.Duration)
1141	}
1142	// Output:
1143	// Thunder Road (4m49s)
1144	// Stairway to Heaven (8m3s)
1145}
1146
1147// Example StrictDecoding shows how to detect whether there are keys in the
1148// TOML document that weren't decoded into the value given. This is useful
1149// for returning an error to the user if they've included extraneous fields
1150// in their configuration.
1151func Example_strictDecoding() {
1152	var blob = `
1153key1 = "value1"
1154key2 = "value2"
1155key3 = "value3"
1156`
1157	type config struct {
1158		Key1 string
1159		Key3 string
1160	}
1161
1162	var conf config
1163	md, err := Decode(blob, &conf)
1164	if err != nil {
1165		log.Fatal(err)
1166	}
1167	fmt.Printf("Undecoded keys: %q\n", md.Undecoded())
1168	// Output:
1169	// Undecoded keys: ["key2"]
1170}
1171
1172// Example UnmarshalTOML shows how to implement a struct type that knows how to
1173// unmarshal itself. The struct must take full responsibility for mapping the
1174// values passed into the struct. The method may be used with interfaces in a
1175// struct in cases where the actual type is not known until the data is
1176// examined.
1177func Example_unmarshalTOML() {
1178
1179	var blob = `
1180[[parts]]
1181type = "valve"
1182id = "valve-1"
1183size = 1.2
1184rating = 4
1185
1186[[parts]]
1187type = "valve"
1188id = "valve-2"
1189size = 2.1
1190rating = 5
1191
1192[[parts]]
1193type = "pipe"
1194id = "pipe-1"
1195length = 2.1
1196diameter = 12
1197
1198[[parts]]
1199type = "cable"
1200id = "cable-1"
1201length = 12
1202rating = 3.1
1203`
1204	o := &order{}
1205	err := Unmarshal([]byte(blob), o)
1206	if err != nil {
1207		log.Fatal(err)
1208	}
1209
1210	fmt.Println(len(o.parts))
1211
1212	for _, part := range o.parts {
1213		fmt.Println(part.Name())
1214	}
1215
1216	// Code to implement UmarshalJSON.
1217
1218	// type order struct {
1219	// 	// NOTE `order.parts` is a private slice of type `part` which is an
1220	// 	// interface and may only be loaded from toml using the
1221	// 	// UnmarshalTOML() method of the Umarshaler interface.
1222	// 	parts parts
1223	// }
1224
1225	// func (o *order) UnmarshalTOML(data interface{}) error {
1226
1227	// 	// NOTE the example below contains detailed type casting to show how
1228	// 	// the 'data' is retrieved. In operational use, a type cast wrapper
1229	// 	// may be preferred e.g.
1230	// 	//
1231	// 	// func AsMap(v interface{}) (map[string]interface{}, error) {
1232	// 	// 		return v.(map[string]interface{})
1233	// 	// }
1234	// 	//
1235	// 	// resulting in:
1236	// 	// d, _ := AsMap(data)
1237	// 	//
1238
1239	// 	d, _ := data.(map[string]interface{})
1240	// 	parts, _ := d["parts"].([]map[string]interface{})
1241
1242	// 	for _, p := range parts {
1243
1244	// 		typ, _ := p["type"].(string)
1245	// 		id, _ := p["id"].(string)
1246
1247	// 		// detect the type of part and handle each case
1248	// 		switch p["type"] {
1249	// 		case "valve":
1250
1251	// 			size := float32(p["size"].(float64))
1252	// 			rating := int(p["rating"].(int64))
1253
1254	// 			valve := &valve{
1255	// 				Type:   typ,
1256	// 				ID:     id,
1257	// 				Size:   size,
1258	// 				Rating: rating,
1259	// 			}
1260
1261	// 			o.parts = append(o.parts, valve)
1262
1263	// 		case "pipe":
1264
1265	// 			length := float32(p["length"].(float64))
1266	// 			diameter := int(p["diameter"].(int64))
1267
1268	// 			pipe := &pipe{
1269	// 				Type:     typ,
1270	// 				ID:       id,
1271	// 				Length:   length,
1272	// 				Diameter: diameter,
1273	// 			}
1274
1275	// 			o.parts = append(o.parts, pipe)
1276
1277	// 		case "cable":
1278
1279	// 			length := int(p["length"].(int64))
1280	// 			rating := float32(p["rating"].(float64))
1281
1282	// 			cable := &cable{
1283	// 				Type:   typ,
1284	// 				ID:     id,
1285	// 				Length: length,
1286	// 				Rating: rating,
1287	// 			}
1288
1289	// 			o.parts = append(o.parts, cable)
1290
1291	// 		}
1292	// 	}
1293
1294	// 	return nil
1295	// }
1296
1297	// type parts []part
1298
1299	// type part interface {
1300	// 	Name() string
1301	// }
1302
1303	// type valve struct {
1304	// 	Type   string
1305	// 	ID     string
1306	// 	Size   float32
1307	// 	Rating int
1308	// }
1309
1310	// func (v *valve) Name() string {
1311	// 	return fmt.Sprintf("VALVE: %s", v.ID)
1312	// }
1313
1314	// type pipe struct {
1315	// 	Type     string
1316	// 	ID       string
1317	// 	Length   float32
1318	// 	Diameter int
1319	// }
1320
1321	// func (p *pipe) Name() string {
1322	// 	return fmt.Sprintf("PIPE: %s", p.ID)
1323	// }
1324
1325	// type cable struct {
1326	// 	Type   string
1327	// 	ID     string
1328	// 	Length int
1329	// 	Rating float32
1330	// }
1331
1332	// func (c *cable) Name() string {
1333	// 	return fmt.Sprintf("CABLE: %s", c.ID)
1334	// }
1335
1336	// Output:
1337	// 4
1338	// VALVE: valve-1
1339	// VALVE: valve-2
1340	// PIPE: pipe-1
1341	// CABLE: cable-1
1342
1343}
1344
1345type order struct {
1346	// NOTE `order.parts` is a private slice of type `part` which is an
1347	// interface and may only be loaded from toml using the UnmarshalTOML()
1348	// method of the Umarshaler interface.
1349	parts parts
1350}
1351
1352func (o *order) UnmarshalTOML(data interface{}) error {
1353
1354	// NOTE the example below contains detailed type casting to show how
1355	// the 'data' is retrieved. In operational use, a type cast wrapper
1356	// may be preferred e.g.
1357	//
1358	// func AsMap(v interface{}) (map[string]interface{}, error) {
1359	// 		return v.(map[string]interface{})
1360	// }
1361	//
1362	// resulting in:
1363	// d, _ := AsMap(data)
1364	//
1365
1366	d, _ := data.(map[string]interface{})
1367	parts, _ := d["parts"].([]map[string]interface{})
1368
1369	for _, p := range parts {
1370
1371		typ, _ := p["type"].(string)
1372		id, _ := p["id"].(string)
1373
1374		// detect the type of part and handle each case
1375		switch p["type"] {
1376		case "valve":
1377
1378			size := float32(p["size"].(float64))
1379			rating := int(p["rating"].(int64))
1380
1381			valve := &valve{
1382				Type:   typ,
1383				ID:     id,
1384				Size:   size,
1385				Rating: rating,
1386			}
1387
1388			o.parts = append(o.parts, valve)
1389
1390		case "pipe":
1391
1392			length := float32(p["length"].(float64))
1393			diameter := int(p["diameter"].(int64))
1394
1395			pipe := &pipe{
1396				Type:     typ,
1397				ID:       id,
1398				Length:   length,
1399				Diameter: diameter,
1400			}
1401
1402			o.parts = append(o.parts, pipe)
1403
1404		case "cable":
1405
1406			length := int(p["length"].(int64))
1407			rating := float32(p["rating"].(float64))
1408
1409			cable := &cable{
1410				Type:   typ,
1411				ID:     id,
1412				Length: length,
1413				Rating: rating,
1414			}
1415
1416			o.parts = append(o.parts, cable)
1417
1418		}
1419	}
1420
1421	return nil
1422}
1423
1424type parts []part
1425
1426type part interface {
1427	Name() string
1428}
1429
1430type valve struct {
1431	Type   string
1432	ID     string
1433	Size   float32
1434	Rating int
1435}
1436
1437func (v *valve) Name() string {
1438	return fmt.Sprintf("VALVE: %s", v.ID)
1439}
1440
1441type pipe struct {
1442	Type     string
1443	ID       string
1444	Length   float32
1445	Diameter int
1446}
1447
1448func (p *pipe) Name() string {
1449	return fmt.Sprintf("PIPE: %s", p.ID)
1450}
1451
1452type cable struct {
1453	Type   string
1454	ID     string
1455	Length int
1456	Rating float32
1457}
1458
1459func (c *cable) Name() string {
1460	return fmt.Sprintf("CABLE: %s", c.ID)
1461}
1462