git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/toml/decode_test.go (about)

     1  package toml
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"math"
    10  	"os"
    11  	"reflect"
    12  	"strconv"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"git.sr.ht/~pingoo/stdx/toml/internal"
    18  )
    19  
    20  func TestDecodeReader(t *testing.T) {
    21  	var i struct{ A int }
    22  	meta, err := DecodeReader(strings.NewReader("a = 42"), &i)
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  	have := fmt.Sprintf("%v %v %v", i, meta.Keys(), meta.Type("a"))
    27  	want := "{42} [a] Integer"
    28  	if have != want {
    29  		t.Errorf("\nhave: %s\nwant: %s", have, want)
    30  	}
    31  }
    32  
    33  func TestDecodeFile(t *testing.T) {
    34  	tmp, err := ioutil.TempFile("", "toml-")
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	defer os.Remove(tmp.Name())
    39  	if _, err := tmp.WriteString("a = 42"); err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	if err := tmp.Close(); err != nil {
    43  		t.Fatal(err)
    44  	}
    45  
    46  	var i struct{ A int }
    47  	meta, err := DecodeFile(tmp.Name(), &i)
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  
    52  	have := fmt.Sprintf("%v %v %v", i, meta.Keys(), meta.Type("a"))
    53  	want := "{42} [a] Integer"
    54  	if have != want {
    55  		t.Errorf("\nhave: %s\nwant: %s", have, want)
    56  	}
    57  }
    58  
    59  func TestDecodeBOM(t *testing.T) {
    60  	for _, tt := range [][]byte{
    61  		[]byte("\xff\xfea = \"b\""),
    62  		[]byte("\xfe\xffa = \"b\""),
    63  	} {
    64  		t.Run("", func(t *testing.T) {
    65  			var s struct{ A string }
    66  			_, err := Decode(string(tt), &s)
    67  			if err != nil {
    68  				t.Fatal(err)
    69  			}
    70  			if s.A != "b" {
    71  				t.Errorf(`s.A is not "b" but %q`, s.A)
    72  			}
    73  		})
    74  	}
    75  }
    76  
    77  func TestDecodeEmbedded(t *testing.T) {
    78  	type Dog struct{ Name string }
    79  	type Age int
    80  	type cat struct{ Name string }
    81  
    82  	for _, test := range []struct {
    83  		label       string
    84  		input       string
    85  		decodeInto  interface{}
    86  		wantDecoded interface{}
    87  	}{
    88  		{
    89  			label:       "embedded struct",
    90  			input:       `Name = "milton"`,
    91  			decodeInto:  &struct{ Dog }{},
    92  			wantDecoded: &struct{ Dog }{Dog{"milton"}},
    93  		},
    94  		{
    95  			label:       "embedded non-nil pointer to struct",
    96  			input:       `Name = "milton"`,
    97  			decodeInto:  &struct{ *Dog }{},
    98  			wantDecoded: &struct{ *Dog }{&Dog{"milton"}},
    99  		},
   100  		{
   101  			label:       "embedded nil pointer to struct",
   102  			input:       ``,
   103  			decodeInto:  &struct{ *Dog }{},
   104  			wantDecoded: &struct{ *Dog }{nil},
   105  		},
   106  		{
   107  			label:       "unexported embedded struct",
   108  			input:       `Name = "socks"`,
   109  			decodeInto:  &struct{ cat }{},
   110  			wantDecoded: &struct{ cat }{cat{"socks"}},
   111  		},
   112  		{
   113  			label:       "embedded int",
   114  			input:       `Age = -5`,
   115  			decodeInto:  &struct{ Age }{},
   116  			wantDecoded: &struct{ Age }{-5},
   117  		},
   118  	} {
   119  		_, err := Decode(test.input, test.decodeInto)
   120  		if err != nil {
   121  			t.Fatal(err)
   122  		}
   123  		if !reflect.DeepEqual(test.wantDecoded, test.decodeInto) {
   124  			t.Errorf("%s: want decoded == %+v, got %+v",
   125  				test.label, test.wantDecoded, test.decodeInto)
   126  		}
   127  	}
   128  }
   129  
   130  func TestDecodeErrors(t *testing.T) {
   131  	tests := []struct {
   132  		s       interface{}
   133  		toml    string
   134  		wantErr string
   135  	}{
   136  		{
   137  			&struct{ V int8 }{},
   138  			`V = 999`,
   139  			`toml: line 1 (last key "V"): 999 is out of range for int8`,
   140  		},
   141  		{
   142  			&struct{ V float32 }{},
   143  			`V = 999999999999999`,
   144  			`toml: line 1 (last key "V"): 999999999999999 is out of range for float32`,
   145  		},
   146  		{
   147  			&struct{ V string }{},
   148  			`V = 5`,
   149  			`toml: line 1 (last key "V"): incompatible types: TOML value has type int64; destination has type string`,
   150  		},
   151  		{
   152  			&struct{ V interface{ ASD() } }{},
   153  			`V = 999`,
   154  			`toml: line 1 (last key "V"): unsupported type interface { ASD() }`,
   155  		},
   156  		{
   157  			&struct{ V struct{ V int } }{},
   158  			`V = 999`,
   159  			`toml: line 1 (last key "V"): type mismatch for struct { V int }: expected table but found int64`,
   160  		},
   161  		{
   162  			&struct{ V [1]int }{},
   163  			`V = [1,2,3]`,
   164  			`toml: line 1 (last key "V"): expected array length 1; got TOML array of length 3`,
   165  		},
   166  		{
   167  			&struct{ V struct{ N int8 } }{},
   168  			`V.N = 999`,
   169  			`toml: line 1 (last key "V.N"): 999 is out of range for int8`,
   170  		},
   171  		{
   172  			&struct{ V struct{ N float32 } }{},
   173  			`V.N = 999999999999999`,
   174  			`toml: line 1 (last key "V.N"): 999999999999999 is out of range for float32`,
   175  		},
   176  		{
   177  			&struct{ V struct{ N string } }{},
   178  			`V.N = 5`,
   179  			`toml: line 1 (last key "V.N"): incompatible types: TOML value has type int64; destination has type string`,
   180  		},
   181  		{
   182  			&struct {
   183  				V struct{ N interface{ ASD() } }
   184  			}{},
   185  			`V.N = 999`,
   186  			`toml: line 1 (last key "V.N"): unsupported type interface { ASD() }`,
   187  		},
   188  		{
   189  			&struct{ V struct{ N struct{ V int } } }{},
   190  			`V.N = 999`,
   191  			`toml: line 1 (last key "V.N"): type mismatch for struct { V int }: expected table but found int64`,
   192  		},
   193  		{
   194  			&struct{ V struct{ N [1]int } }{},
   195  			`V.N = [1,2,3]`,
   196  			`toml: line 1 (last key "V.N"): expected array length 1; got TOML array of length 3`,
   197  		},
   198  	}
   199  
   200  	for _, tt := range tests {
   201  		_, err := Decode(tt.toml, tt.s)
   202  		if err == nil {
   203  			t.Fatal("err is nil")
   204  		}
   205  		if err.Error() != tt.wantErr {
   206  			t.Errorf("\nhave: %q\nwant: %q", err, tt.wantErr)
   207  		}
   208  	}
   209  }
   210  
   211  func TestDecodeIgnoreFields(t *testing.T) {
   212  	const input = `
   213  Number = 123
   214  - = 234
   215  `
   216  	var s struct {
   217  		Number int `toml:"-"`
   218  	}
   219  	if _, err := Decode(input, &s); err != nil {
   220  		t.Fatal(err)
   221  	}
   222  	if s.Number != 0 {
   223  		t.Errorf("got: %d; want 0", s.Number)
   224  	}
   225  }
   226  
   227  func TestDecodeTableArrays(t *testing.T) {
   228  	var tomlTableArrays = `
   229  [[albums]]
   230  name = "Born to Run"
   231  
   232    [[albums.songs]]
   233    name = "Jungleland"
   234  
   235    [[albums.songs]]
   236    name = "Meeting Across the River"
   237  
   238  [[albums]]
   239  name = "Born in the USA"
   240  
   241    [[albums.songs]]
   242    name = "Glory Days"
   243  
   244    [[albums.songs]]
   245    name = "Dancing in the Dark"
   246  `
   247  
   248  	type Song struct {
   249  		Name string
   250  	}
   251  
   252  	type Album struct {
   253  		Name  string
   254  		Songs []Song
   255  	}
   256  
   257  	type Music struct {
   258  		Albums []Album
   259  	}
   260  
   261  	expected := Music{[]Album{
   262  		{"Born to Run", []Song{{"Jungleland"}, {"Meeting Across the River"}}},
   263  		{"Born in the USA", []Song{{"Glory Days"}, {"Dancing in the Dark"}}},
   264  	}}
   265  	var got Music
   266  	if _, err := Decode(tomlTableArrays, &got); err != nil {
   267  		t.Fatal(err)
   268  	}
   269  	if !reflect.DeepEqual(expected, got) {
   270  		t.Fatalf("\n%#v\n!=\n%#v\n", expected, got)
   271  	}
   272  }
   273  
   274  func TestDecodePointers(t *testing.T) {
   275  	type Object struct {
   276  		Type        string
   277  		Description string
   278  	}
   279  
   280  	type Dict struct {
   281  		NamedObject map[string]*Object
   282  		BaseObject  *Object
   283  		Strptr      *string
   284  		Strptrs     []*string
   285  	}
   286  	s1, s2, s3 := "blah", "abc", "def"
   287  	expected := &Dict{
   288  		Strptr:  &s1,
   289  		Strptrs: []*string{&s2, &s3},
   290  		NamedObject: map[string]*Object{
   291  			"foo": {"FOO", "fooooo!!!"},
   292  			"bar": {"BAR", "ba-ba-ba-ba-barrrr!!!"},
   293  		},
   294  		BaseObject: &Object{"BASE", "da base"},
   295  	}
   296  
   297  	ex1 := `
   298  Strptr = "blah"
   299  Strptrs = ["abc", "def"]
   300  
   301  [NamedObject.foo]
   302  Type = "FOO"
   303  Description = "fooooo!!!"
   304  
   305  [NamedObject.bar]
   306  Type = "BAR"
   307  Description = "ba-ba-ba-ba-barrrr!!!"
   308  
   309  [BaseObject]
   310  Type = "BASE"
   311  Description = "da base"
   312  `
   313  	dict := new(Dict)
   314  	_, err := Decode(ex1, dict)
   315  	if err != nil {
   316  		t.Errorf("Decode error: %v", err)
   317  	}
   318  	if !reflect.DeepEqual(expected, dict) {
   319  		t.Fatalf("\n%#v\n!=\n%#v\n", expected, dict)
   320  	}
   321  }
   322  
   323  func TestDecodeBadDatetime(t *testing.T) {
   324  	var x struct{ T time.Time }
   325  	for _, s := range []string{"123", "1230"} {
   326  		input := "T = " + s
   327  		if _, err := Decode(input, &x); err == nil {
   328  			t.Errorf("Expected invalid DateTime error for %q", s)
   329  		}
   330  	}
   331  }
   332  
   333  type sphere struct {
   334  	Center [3]float64
   335  	Radius float64
   336  }
   337  
   338  func TestDecodeArrayWrongSize(t *testing.T) {
   339  	var s1 sphere
   340  	if _, err := Decode(`center = [0.1, 2.3]`, &s1); err == nil {
   341  		t.Fatal("Expected array type mismatch error")
   342  	}
   343  }
   344  
   345  func TestDecodeIntOverflow(t *testing.T) {
   346  	type table struct {
   347  		Value int8
   348  	}
   349  	var tab table
   350  	if _, err := Decode(`value = 500`, &tab); err == nil {
   351  		t.Fatal("Expected integer out-of-bounds error.")
   352  	}
   353  }
   354  
   355  func TestDecodeFloatOverflow(t *testing.T) {
   356  	tests := []struct {
   357  		value    string
   358  		overflow bool
   359  	}{
   360  		{fmt.Sprintf(`F32 = %f`, math.MaxFloat64), true},
   361  		{fmt.Sprintf(`F32 = %f`, -math.MaxFloat64), true},
   362  		{fmt.Sprintf(`F32 = %f`, math.MaxFloat32*1.1), true},
   363  		{fmt.Sprintf(`F32 = %f`, -math.MaxFloat32*1.1), true},
   364  		{fmt.Sprintf(`F32 = %d`, maxSafeFloat32Int+1), true},
   365  		{fmt.Sprintf(`F32 = %d`, -maxSafeFloat32Int-1), true},
   366  		{fmt.Sprintf(`F64 = %d`, maxSafeFloat64Int+1), true},
   367  		{fmt.Sprintf(`F64 = %d`, -maxSafeFloat64Int-1), true},
   368  
   369  		{fmt.Sprintf(`F32 = %f`, math.MaxFloat32), false},
   370  		{fmt.Sprintf(`F32 = %f`, -math.MaxFloat32), false},
   371  		{fmt.Sprintf(`F32 = %d`, maxSafeFloat32Int), false},
   372  		{fmt.Sprintf(`F32 = %d`, -maxSafeFloat32Int), false},
   373  		{fmt.Sprintf(`F64 = %f`, math.MaxFloat64), false},
   374  		{fmt.Sprintf(`F64 = %f`, -math.MaxFloat64), false},
   375  		{fmt.Sprintf(`F64 = %f`, math.MaxFloat32), false},
   376  		{fmt.Sprintf(`F64 = %f`, -math.MaxFloat32), false},
   377  		{fmt.Sprintf(`F64 = %d`, maxSafeFloat64Int), false},
   378  		{fmt.Sprintf(`F64 = %d`, -maxSafeFloat64Int), false},
   379  	}
   380  
   381  	for _, tt := range tests {
   382  		t.Run("", func(t *testing.T) {
   383  			var tab struct {
   384  				F32 float32
   385  				F64 float64
   386  			}
   387  			_, err := Decode(tt.value, &tab)
   388  
   389  			if tt.overflow && err == nil {
   390  				t.Fatal("expected error, but err is nil")
   391  			}
   392  			if (tt.overflow && !errorContains(err, "out of range")) || (!tt.overflow && err != nil) {
   393  				t.Fatalf("unexpected error:\n%v", err)
   394  			}
   395  		})
   396  	}
   397  }
   398  
   399  func TestDecodeSizedInts(t *testing.T) {
   400  	type table struct {
   401  		U8  uint8
   402  		U16 uint16
   403  		U32 uint32
   404  		U64 uint64
   405  		U   uint
   406  		I8  int8
   407  		I16 int16
   408  		I32 int32
   409  		I64 int64
   410  		I   int
   411  	}
   412  	answer := table{1, 1, 1, 1, 1, -1, -1, -1, -1, -1}
   413  	toml := `
   414  	u8 = 1
   415  	u16 = 1
   416  	u32 = 1
   417  	u64 = 1
   418  	u = 1
   419  	i8 = -1
   420  	i16 = -1
   421  	i32 = -1
   422  	i64 = -1
   423  	i = -1
   424  	`
   425  	var tab table
   426  	if _, err := Decode(toml, &tab); err != nil {
   427  		t.Fatal(err.Error())
   428  	}
   429  	if answer != tab {
   430  		t.Fatalf("Expected %#v but got %#v", answer, tab)
   431  	}
   432  }
   433  
   434  type NopUnmarshalTOML int
   435  
   436  func (n *NopUnmarshalTOML) UnmarshalTOML(p interface{}) error {
   437  	*n = 42
   438  	return nil
   439  }
   440  
   441  func TestDecodeTypes(t *testing.T) {
   442  	type (
   443  		mystr   string
   444  		myiface interface{}
   445  	)
   446  
   447  	for _, tt := range []struct {
   448  		v       interface{}
   449  		want    string
   450  		wantErr string
   451  	}{
   452  		{new(map[string]bool), "&map[F:true]", ""},
   453  		{new(map[mystr]bool), "&map[F:true]", ""},
   454  		{new(NopUnmarshalTOML), "42", ""},
   455  		{new(map[interface{}]bool), "&map[F:true]", ""},
   456  		{new(map[myiface]bool), "&map[F:true]", ""},
   457  
   458  		{3, "", `toml: cannot decode to non-pointer "int"`},
   459  		{map[string]interface{}{}, "", `toml: cannot decode to non-pointer "map[string]interface {}"`},
   460  
   461  		{(*int)(nil), "", `toml: cannot decode to nil value of "*int"`},
   462  		{(*Unmarshaler)(nil), "", `toml: cannot decode to nil value of "*toml.Unmarshaler"`},
   463  		{nil, "", `toml: cannot decode to non-pointer <nil>`},
   464  
   465  		{new(map[int]string), "", "toml: cannot decode to a map with non-string key type"},
   466  
   467  		{new(struct{ F int }), "", `toml: line 1 (last key "F"): incompatible types: TOML value has type bool; destination has type integer`},
   468  		{new(map[string]int), "", `toml: line 1 (last key "F"): incompatible types: TOML value has type bool; destination has type integer`},
   469  		{new(int), "", `toml: cannot decode to type int`},
   470  		{new([]int), "", "toml: cannot decode to type []int"},
   471  	} {
   472  		t.Run(fmt.Sprintf("%T", tt.v), func(t *testing.T) {
   473  			_, err := Decode(`F = true`, tt.v)
   474  			if !errorContains(err, tt.wantErr) {
   475  				t.Fatalf("wrong error\nhave: %q\nwant: %q", err, tt.wantErr)
   476  			}
   477  
   478  			if err == nil {
   479  				have := fmt.Sprintf("%v", tt.v)
   480  				if n, ok := tt.v.(*NopUnmarshalTOML); ok {
   481  					have = fmt.Sprintf("%v", *n)
   482  				}
   483  				if have != tt.want {
   484  					t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
   485  				}
   486  			}
   487  		})
   488  	}
   489  }
   490  
   491  func TestUnmarshaler(t *testing.T) {
   492  	var tomlBlob = `
   493  [dishes.hamboogie]
   494  name = "Hamboogie with fries"
   495  price = 10.99
   496  
   497  [[dishes.hamboogie.ingredients]]
   498  name = "Bread Bun"
   499  
   500  [[dishes.hamboogie.ingredients]]
   501  name = "Lettuce"
   502  
   503  [[dishes.hamboogie.ingredients]]
   504  name = "Real Beef Patty"
   505  
   506  [[dishes.hamboogie.ingredients]]
   507  name = "Tomato"
   508  
   509  [dishes.eggsalad]
   510  name = "Egg Salad with rice"
   511  price = 3.99
   512  
   513  [[dishes.eggsalad.ingredients]]
   514  name = "Egg"
   515  
   516  [[dishes.eggsalad.ingredients]]
   517  name = "Mayo"
   518  
   519  [[dishes.eggsalad.ingredients]]
   520  name = "Rice"
   521  `
   522  	m := &menu{}
   523  	if _, err := Decode(tomlBlob, m); err != nil {
   524  		t.Fatal(err)
   525  	}
   526  
   527  	if len(m.Dishes) != 2 {
   528  		t.Log("two dishes should be loaded with UnmarshalTOML()")
   529  		t.Errorf("expected %d but got %d", 2, len(m.Dishes))
   530  	}
   531  
   532  	eggSalad := m.Dishes["eggsalad"]
   533  	if _, ok := interface{}(eggSalad).(dish); !ok {
   534  		t.Errorf("expected a dish")
   535  	}
   536  
   537  	if eggSalad.Name != "Egg Salad with rice" {
   538  		t.Errorf("expected the dish to be named 'Egg Salad with rice'")
   539  	}
   540  
   541  	if len(eggSalad.Ingredients) != 3 {
   542  		t.Log("dish should be loaded with UnmarshalTOML()")
   543  		t.Errorf("expected %d but got %d", 3, len(eggSalad.Ingredients))
   544  	}
   545  
   546  	found := false
   547  	for _, i := range eggSalad.Ingredients {
   548  		if i.Name == "Rice" {
   549  			found = true
   550  			break
   551  		}
   552  	}
   553  	if !found {
   554  		t.Error("Rice was not loaded in UnmarshalTOML()")
   555  	}
   556  
   557  	// test on a value - must be passed as *
   558  	o := menu{}
   559  	if _, err := Decode(tomlBlob, &o); err != nil {
   560  		t.Fatal(err)
   561  	}
   562  
   563  }
   564  
   565  func TestDecodeInlineTable(t *testing.T) {
   566  	input := `
   567  [CookieJar]
   568  Types = {Chocolate = "yummy", Oatmeal = "best ever"}
   569  
   570  [Seasons]
   571  Locations = {NY = {Temp = "not cold", Rating = 4}, MI = {Temp = "freezing", Rating = 9}}
   572  `
   573  	type cookieJar struct {
   574  		Types map[string]string
   575  	}
   576  	type properties struct {
   577  		Temp   string
   578  		Rating int
   579  	}
   580  	type seasons struct {
   581  		Locations map[string]properties
   582  	}
   583  	type wrapper struct {
   584  		CookieJar cookieJar
   585  		Seasons   seasons
   586  	}
   587  	var got wrapper
   588  
   589  	meta, err := Decode(input, &got)
   590  	if err != nil {
   591  		t.Fatal(err)
   592  	}
   593  	want := wrapper{
   594  		CookieJar: cookieJar{
   595  			Types: map[string]string{
   596  				"Chocolate": "yummy",
   597  				"Oatmeal":   "best ever",
   598  			},
   599  		},
   600  		Seasons: seasons{
   601  			Locations: map[string]properties{
   602  				"NY": {
   603  					Temp:   "not cold",
   604  					Rating: 4,
   605  				},
   606  				"MI": {
   607  					Temp:   "freezing",
   608  					Rating: 9,
   609  				},
   610  			},
   611  		},
   612  	}
   613  	if !reflect.DeepEqual(got, want) {
   614  		t.Fatalf("after decode, got:\n\n%#v\n\nwant:\n\n%#v", got, want)
   615  	}
   616  	if len(meta.keys) != 12 {
   617  		t.Errorf("after decode, got %d meta keys; want 12", len(meta.keys))
   618  	}
   619  	if len(meta.keyInfo) != 12 {
   620  		t.Errorf("after decode, got %d meta keyInfo; want 12", len(meta.keyInfo))
   621  	}
   622  }
   623  
   624  func TestDecodeInlineTableArray(t *testing.T) {
   625  	type point struct {
   626  		X, Y, Z int
   627  	}
   628  	var got struct {
   629  		Points []point
   630  	}
   631  	// Example inline table array from the spec.
   632  	const in = `
   633  points = [ { x = 1, y = 2, z = 3 },
   634             { x = 7, y = 8, z = 9 },
   635             { x = 2, y = 4, z = 8 } ]
   636  
   637  `
   638  	if _, err := Decode(in, &got); err != nil {
   639  		t.Fatal(err)
   640  	}
   641  	want := []point{
   642  		{X: 1, Y: 2, Z: 3},
   643  		{X: 7, Y: 8, Z: 9},
   644  		{X: 2, Y: 4, Z: 8},
   645  	}
   646  	if !reflect.DeepEqual(got.Points, want) {
   647  		t.Errorf("got %#v; want %#v", got.Points, want)
   648  	}
   649  }
   650  
   651  type menu struct {
   652  	Dishes map[string]dish
   653  }
   654  
   655  func (m *menu) UnmarshalTOML(p interface{}) error {
   656  	m.Dishes = make(map[string]dish)
   657  	data, _ := p.(map[string]interface{})
   658  	dishes := data["dishes"].(map[string]interface{})
   659  	for n, v := range dishes {
   660  		if d, ok := v.(map[string]interface{}); ok {
   661  			nd := dish{}
   662  			nd.UnmarshalTOML(d)
   663  			m.Dishes[n] = nd
   664  		} else {
   665  			return fmt.Errorf("not a dish")
   666  		}
   667  	}
   668  	return nil
   669  }
   670  
   671  type dish struct {
   672  	Name        string
   673  	Price       float32
   674  	Ingredients []ingredient
   675  }
   676  
   677  func (d *dish) UnmarshalTOML(p interface{}) error {
   678  	data, _ := p.(map[string]interface{})
   679  	d.Name, _ = data["name"].(string)
   680  	d.Price, _ = data["price"].(float32)
   681  	ingredients, _ := data["ingredients"].([]map[string]interface{})
   682  	for _, e := range ingredients {
   683  		n, _ := interface{}(e).(map[string]interface{})
   684  		name, _ := n["name"].(string)
   685  		i := ingredient{name}
   686  		d.Ingredients = append(d.Ingredients, i)
   687  	}
   688  	return nil
   689  }
   690  
   691  type ingredient struct {
   692  	Name string
   693  }
   694  
   695  func TestDecodeSlices(t *testing.T) {
   696  	type (
   697  		T struct {
   698  			Arr []string
   699  			Tbl map[string]interface{}
   700  		}
   701  		M map[string]interface{}
   702  	)
   703  	tests := []struct {
   704  		input    string
   705  		in, want T
   706  	}{
   707  		{"",
   708  			T{}, T{}},
   709  
   710  		// Leave existing values alone.
   711  		{"",
   712  			T{[]string{}, M{"arr": []string{}}},
   713  			T{[]string{}, M{"arr": []string{}}}},
   714  		{"",
   715  			T{[]string{"a"}, M{"arr": []string{"b"}}},
   716  			T{[]string{"a"}, M{"arr": []string{"b"}}}},
   717  
   718  		// Empty array always allocates (see #339)
   719  		{`arr = []
   720  		tbl = {arr = []}`,
   721  			T{},
   722  			T{[]string{}, M{"arr": []interface{}{}}}},
   723  		{`arr = []
   724  		tbl = {}`,
   725  			T{[]string{}, M{}},
   726  			T{[]string{}, M{}}},
   727  
   728  		{`arr = []`,
   729  			T{[]string{"a"}, M{}},
   730  			T{[]string{}, M{}}},
   731  
   732  		{`arr = ["x"]
   733  		 tbl = {arr=["y"]}`,
   734  			T{},
   735  			T{[]string{"x"}, M{"arr": []interface{}{"y"}}}},
   736  		{`arr = ["x"]
   737  		 tbl = {arr=["y"]}`,
   738  			T{[]string{}, M{}},
   739  			T{[]string{"x"}, M{"arr": []interface{}{"y"}}}},
   740  		{`arr = ["x"]
   741  		tbl = {arr=["y"]}`,
   742  			T{[]string{"a", "b"}, M{"arr": []interface{}{"c", "d"}}},
   743  			T{[]string{"x"}, M{"arr": []interface{}{"y"}}}},
   744  	}
   745  
   746  	for _, tt := range tests {
   747  		t.Run("", func(t *testing.T) {
   748  			_, err := Decode(tt.input, &tt.in)
   749  			if err != nil {
   750  				t.Error(err)
   751  			}
   752  			if !reflect.DeepEqual(tt.in, tt.want) {
   753  				t.Errorf("\nhave: %#v\nwant: %#v", tt.in, tt.want)
   754  			}
   755  		})
   756  	}
   757  }
   758  
   759  func TestDecodePrimitive(t *testing.T) {
   760  	type S struct {
   761  		P Primitive
   762  	}
   763  	type T struct {
   764  		S []int
   765  	}
   766  	slicep := func(s []int) *[]int { return &s }
   767  	arrayp := func(a [2]int) *[2]int { return &a }
   768  	mapp := func(m map[string]int) *map[string]int { return &m }
   769  	for i, tt := range []struct {
   770  		v     interface{}
   771  		input string
   772  		want  interface{}
   773  	}{
   774  		// slices
   775  		{slicep(nil), "", slicep(nil)},
   776  		{slicep([]int{}), "", slicep([]int{})},
   777  		{slicep([]int{1, 2, 3}), "", slicep([]int{1, 2, 3})},
   778  		{slicep(nil), "P = [1,2]", slicep([]int{1, 2})},
   779  		{slicep([]int{}), "P = [1,2]", slicep([]int{1, 2})},
   780  		{slicep([]int{1, 2, 3}), "P = [1,2]", slicep([]int{1, 2})},
   781  
   782  		// arrays
   783  		{arrayp([2]int{2, 3}), "", arrayp([2]int{2, 3})},
   784  		{arrayp([2]int{2, 3}), "P = [3,4]", arrayp([2]int{3, 4})},
   785  
   786  		// maps
   787  		{mapp(nil), "", mapp(nil)},
   788  		{mapp(map[string]int{}), "", mapp(map[string]int{})},
   789  		{mapp(map[string]int{"a": 1}), "", mapp(map[string]int{"a": 1})},
   790  		{mapp(nil), "[P]\na = 2", mapp(map[string]int{"a": 2})},
   791  		{mapp(map[string]int{}), "[P]\na = 2", mapp(map[string]int{"a": 2})},
   792  		{mapp(map[string]int{"a": 1, "b": 3}), "[P]\na = 2", mapp(map[string]int{"a": 2, "b": 3})},
   793  
   794  		// structs
   795  		{&T{nil}, "[P]", &T{nil}},
   796  		{&T{[]int{}}, "[P]", &T{[]int{}}},
   797  		{&T{[]int{1, 2, 3}}, "[P]", &T{[]int{1, 2, 3}}},
   798  		{&T{nil}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
   799  		{&T{[]int{}}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
   800  		{&T{[]int{1, 2, 3}}, "[P]\nS = [1,2]", &T{[]int{1, 2}}},
   801  	} {
   802  		var s S
   803  		md, err := Decode(tt.input, &s)
   804  		if err != nil {
   805  			t.Errorf("[%d] Decode error: %s", i, err)
   806  			continue
   807  		}
   808  		if err := md.PrimitiveDecode(s.P, tt.v); err != nil {
   809  			t.Errorf("[%d] PrimitiveDecode error: %s", i, err)
   810  			continue
   811  		}
   812  		if !reflect.DeepEqual(tt.v, tt.want) {
   813  			t.Errorf("[%d] got %#v; want %#v", i, tt.v, tt.want)
   814  		}
   815  	}
   816  }
   817  
   818  func TestDecodeDatetime(t *testing.T) {
   819  	// Test here in addition to toml-test to ensure the TZs are correct.
   820  	tz7 := time.FixedZone("", -3600*7)
   821  
   822  	for _, tt := range []struct {
   823  		in   string
   824  		want time.Time
   825  	}{
   826  		// Offset datetime
   827  		{"1979-05-27T07:32:00Z", time.Date(1979, 05, 27, 07, 32, 0, 0, time.UTC)},
   828  		{"1979-05-27T07:32:00.999999Z", time.Date(1979, 05, 27, 07, 32, 0, 999999000, time.UTC)},
   829  		{"1979-05-27T00:32:00-07:00", time.Date(1979, 05, 27, 00, 32, 0, 0, tz7)},
   830  		{"1979-05-27T00:32:00.999999-07:00", time.Date(1979, 05, 27, 00, 32, 0, 999999000, tz7)},
   831  		{"1979-05-27T00:32:00.24-07:00", time.Date(1979, 05, 27, 00, 32, 0, 240000000, tz7)},
   832  		{"1979-05-27 07:32:00Z", time.Date(1979, 05, 27, 07, 32, 0, 0, time.UTC)},
   833  		{"1979-05-27t07:32:00z", time.Date(1979, 05, 27, 07, 32, 0, 0, time.UTC)},
   834  
   835  		// Make sure the space between the datetime and "#" isn't lexed.
   836  		{"1979-05-27T07:32:12-07:00  # c", time.Date(1979, 05, 27, 07, 32, 12, 0, tz7)},
   837  
   838  		// Local times.
   839  		{"1979-05-27T07:32:00", time.Date(1979, 05, 27, 07, 32, 0, 0, internal.LocalDatetime)},
   840  		{"1979-05-27T07:32:00.999999", time.Date(1979, 05, 27, 07, 32, 0, 999999000, internal.LocalDatetime)},
   841  		{"1979-05-27T07:32:00.25", time.Date(1979, 05, 27, 07, 32, 0, 250000000, internal.LocalDatetime)},
   842  		{"1979-05-27", time.Date(1979, 05, 27, 0, 0, 0, 0, internal.LocalDate)},
   843  		{"07:32:00", time.Date(0, 1, 1, 07, 32, 0, 0, internal.LocalTime)},
   844  		{"07:32:00.999999", time.Date(0, 1, 1, 07, 32, 0, 999999000, internal.LocalTime)},
   845  	} {
   846  		t.Run(tt.in, func(t *testing.T) {
   847  			var x struct{ D time.Time }
   848  			input := "d = " + tt.in
   849  			if _, err := Decode(input, &x); err != nil {
   850  				t.Fatalf("got error: %s", err)
   851  			}
   852  
   853  			if h, w := x.D.Format(time.RFC3339Nano), tt.want.Format(time.RFC3339Nano); h != w {
   854  				t.Errorf("\nhave: %s\nwant: %s", h, w)
   855  			}
   856  		})
   857  	}
   858  }
   859  
   860  func TestDecodeTextUnmarshaler(t *testing.T) {
   861  	tests := []struct {
   862  		name string
   863  		t    interface{}
   864  		toml string
   865  		want string
   866  	}{
   867  		{
   868  			"time.Time",
   869  			struct{ Time time.Time }{},
   870  			"Time = 1987-07-05T05:45:00Z",
   871  			"map[Time:1987-07-05 05:45:00 +0000 UTC]",
   872  		},
   873  		{
   874  			"*time.Time",
   875  			struct{ Time *time.Time }{},
   876  			"Time = 1988-07-05T05:45:00Z",
   877  			"map[Time:1988-07-05 05:45:00 +0000 UTC]",
   878  		},
   879  		{
   880  			"map[string]time.Time",
   881  			struct{ Times map[string]time.Time }{},
   882  			"Times.one = 1989-07-05T05:45:00Z\nTimes.two = 1990-07-05T05:45:00Z",
   883  			"map[Times:map[one:1989-07-05 05:45:00 +0000 UTC two:1990-07-05 05:45:00 +0000 UTC]]",
   884  		},
   885  		{
   886  			"map[string]*time.Time",
   887  			struct{ Times map[string]*time.Time }{},
   888  			"Times.one = 1989-07-05T05:45:00Z\nTimes.two = 1990-07-05T05:45:00Z",
   889  			"map[Times:map[one:1989-07-05 05:45:00 +0000 UTC two:1990-07-05 05:45:00 +0000 UTC]]",
   890  		},
   891  	}
   892  
   893  	for _, tt := range tests {
   894  		t.Run(tt.name, func(t *testing.T) {
   895  			_, err := Decode(tt.toml, &tt.t)
   896  			if err != nil {
   897  				t.Fatal(err)
   898  			}
   899  
   900  			have := fmt.Sprintf("%v", tt.t)
   901  			if have != tt.want {
   902  				t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
   903  			}
   904  		})
   905  	}
   906  }
   907  
   908  func TestDecodeDuration(t *testing.T) {
   909  	tests := []struct {
   910  		in                  interface{}
   911  		toml, want, wantErr string
   912  	}{
   913  		{&struct{ T time.Duration }{}, `t = "0s"`,
   914  			"&{0s}", ""},
   915  		{&struct{ T time.Duration }{}, `t = "5m4s"`,
   916  			"&{5m4s}", ""},
   917  		{&struct{ T time.Duration }{}, `t = "4.000000002s"`,
   918  			"&{4.000000002s}", ""},
   919  
   920  		{&struct{ T time.Duration }{}, `t = 0`,
   921  			"&{0s}", ""},
   922  		{&struct{ T time.Duration }{}, `t = 12345678`,
   923  			"&{12.345678ms}", ""},
   924  
   925  		{&struct{ T *time.Duration }{}, `T = "5s"`,
   926  			"&{5s}", ""},
   927  		{&struct{ T *time.Duration }{}, `T = 5`,
   928  			"&{5ns}", ""},
   929  
   930  		{&struct{ T map[string]time.Duration }{}, `T.dur = "5s"`,
   931  			"&{map[dur:5s]}", ""},
   932  		{&struct{ T map[string]*time.Duration }{}, `T.dur = "5s"`,
   933  			"&{map[dur:5s]}", ""},
   934  
   935  		{&struct{ T []time.Duration }{}, `T = ["5s"]`,
   936  			"&{[5s]}", ""},
   937  		{&struct{ T []*time.Duration }{}, `T = ["5s"]`,
   938  			"&{[5s]}", ""},
   939  
   940  		{&struct{ T time.Duration }{}, `t = "99 bottles of beer"`, "&{0s}", `invalid duration: "99 bottles of beer"`},
   941  		{&struct{ T time.Duration }{}, `t = "one bottle of beer"`, "&{0s}", `invalid duration: "one bottle of beer"`},
   942  		{&struct{ T time.Duration }{}, `t = 1.2`, "&{0s}", "incompatible types:"},
   943  		{&struct{ T time.Duration }{}, `t = {}`, "&{0s}", "incompatible types:"},
   944  		{&struct{ T time.Duration }{}, `t = []`, "&{0s}", "incompatible types:"},
   945  	}
   946  
   947  	for _, tt := range tests {
   948  		t.Run("", func(t *testing.T) {
   949  			_, err := Decode(tt.toml, tt.in)
   950  			if !errorContains(err, tt.wantErr) {
   951  				t.Fatal(err)
   952  			}
   953  
   954  			have := fmt.Sprintf("%s", tt.in)
   955  			if have != tt.want {
   956  				t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
   957  			}
   958  		})
   959  	}
   960  }
   961  
   962  func TestDecodeJSONNumber(t *testing.T) {
   963  	tests := []struct {
   964  		in                  interface{}
   965  		toml, want, wantErr string
   966  	}{
   967  		{&struct{ D json.Number }{}, `D = 2`, "&{2}", ""},
   968  		{&struct{ D json.Number }{}, `D = 2.002`, "&{2.002}", ""},
   969  		{&struct{ D *json.Number }{}, `D = 2`, "&{2}", ""},
   970  		{&struct{ D *json.Number }{}, `D = 2.002`, "&{2.002}", ""},
   971  		{&struct{ D []json.Number }{}, `D = [2, 3.03]`, "&{[2 3.03]}", ""},
   972  		{&struct{ D []*json.Number }{}, `D = [2, 3.03]`, "&{[2 3.03]}", ""},
   973  		{&struct{ D map[string]json.Number }{}, `D = {a=2, b=3.03}`, "&{map[a:2 b:3.03]}", ""},
   974  		{&struct{ D map[string]*json.Number }{}, `D = {a=2, b=3.03}`, "&{map[a:2 b:3.03]}", ""},
   975  
   976  		{&struct{ D json.Number }{}, `D = {}`, "&{}", "incompatible types"},
   977  		{&struct{ D json.Number }{}, `D = []`, "&{}", "incompatible types"},
   978  		{&struct{ D json.Number }{}, `D = "2"`, "&{}", "incompatible types"},
   979  	}
   980  
   981  	for _, tt := range tests {
   982  		t.Run("", func(t *testing.T) {
   983  			_, err := Decode(tt.toml, tt.in)
   984  			if !errorContains(err, tt.wantErr) {
   985  				t.Fatal(err)
   986  			}
   987  
   988  			have := fmt.Sprintf("%s", tt.in)
   989  			if have != tt.want {
   990  				t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
   991  			}
   992  		})
   993  	}
   994  }
   995  
   996  func TestMetaDotConflict(t *testing.T) {
   997  	var m map[string]interface{}
   998  	meta, err := Decode(`
   999  		"a.b" = "str"
  1000  		a.b   = 1
  1001  		""    = 2
  1002  	`, &m)
  1003  	if err != nil {
  1004  		t.Fatal(err)
  1005  	}
  1006  
  1007  	want := `"a.b"=String; a.b=Integer; ""=Integer`
  1008  	have := ""
  1009  	for i, k := range meta.Keys() {
  1010  		if i > 0 {
  1011  			have += "; "
  1012  		}
  1013  		have += k.String() + "=" + meta.Type(k...)
  1014  	}
  1015  	if have != want {
  1016  		t.Errorf("\nhave: %s\nwant: %s", have, want)
  1017  	}
  1018  }
  1019  
  1020  type (
  1021  	Outer struct {
  1022  		Int   *InnerInt
  1023  		Enum  *Enum
  1024  		Slice *InnerArrayString
  1025  	}
  1026  	Enum             int
  1027  	InnerString      struct{ value string }
  1028  	InnerInt         struct{ value int }
  1029  	InnerBool        struct{ value bool }
  1030  	InnerArrayString struct{ value []string }
  1031  )
  1032  
  1033  const (
  1034  	NoValue Enum = iota
  1035  	OtherValue
  1036  )
  1037  
  1038  func (e *Enum) Value() string {
  1039  	switch *e {
  1040  	case OtherValue:
  1041  		return "OTHER_VALUE"
  1042  	}
  1043  	return ""
  1044  }
  1045  
  1046  func (e *Enum) MarshalTOML() ([]byte, error) {
  1047  	return []byte(`"` + e.Value() + `"`), nil
  1048  }
  1049  
  1050  func (e *Enum) UnmarshalTOML(value interface{}) error {
  1051  	sValue, ok := value.(string)
  1052  	if !ok {
  1053  		return fmt.Errorf("value %v is not a string type", value)
  1054  	}
  1055  	for _, enum := range []Enum{NoValue, OtherValue} {
  1056  		if enum.Value() == sValue {
  1057  			*e = enum
  1058  			return nil
  1059  		}
  1060  	}
  1061  	return errors.New("invalid enum value")
  1062  }
  1063  
  1064  func (i *InnerInt) MarshalTOML() ([]byte, error) {
  1065  	return []byte(strconv.Itoa(i.value)), nil
  1066  }
  1067  func (i *InnerInt) UnmarshalTOML(value interface{}) error {
  1068  	iValue, ok := value.(int64)
  1069  	if !ok {
  1070  		return fmt.Errorf("value %v is not a int type", value)
  1071  	}
  1072  	i.value = int(iValue)
  1073  	return nil
  1074  }
  1075  
  1076  func (as *InnerArrayString) MarshalTOML() ([]byte, error) {
  1077  	return []byte("[\"" + strings.Join(as.value, "\", \"") + "\"]"), nil
  1078  }
  1079  
  1080  func (as *InnerArrayString) UnmarshalTOML(value interface{}) error {
  1081  	if value != nil {
  1082  		asValue, ok := value.([]interface{})
  1083  		if !ok {
  1084  			return fmt.Errorf("value %v is not a [] type", value)
  1085  		}
  1086  		as.value = []string{}
  1087  		for _, value := range asValue {
  1088  			as.value = append(as.value, value.(string))
  1089  		}
  1090  	}
  1091  	return nil
  1092  }
  1093  
  1094  // Test for #341
  1095  func TestCustomEncode(t *testing.T) {
  1096  	enum := OtherValue
  1097  	outer := Outer{
  1098  		Int:   &InnerInt{value: 10},
  1099  		Enum:  &enum,
  1100  		Slice: &InnerArrayString{value: []string{"text1", "text2"}},
  1101  	}
  1102  
  1103  	var buf bytes.Buffer
  1104  	err := NewEncoder(&buf).Encode(outer)
  1105  	if err != nil {
  1106  		t.Errorf("Encode failed: %s", err)
  1107  	}
  1108  
  1109  	have := strings.TrimSpace(buf.String())
  1110  	want := strings.ReplaceAll(strings.TrimSpace(`
  1111  		Int = 10
  1112  		Enum = "OTHER_VALUE"
  1113  		Slice = ["text1", "text2"]
  1114  	`), "\t", "")
  1115  	if want != have {
  1116  		t.Errorf("\nhave: %s\nwant: %s\n", have, want)
  1117  	}
  1118  }
  1119  
  1120  // Test for #341
  1121  func TestCustomDecode(t *testing.T) {
  1122  	var outer Outer
  1123  	_, err := Decode(`
  1124  		Int = 10
  1125  		Enum = "OTHER_VALUE"
  1126  		Slice = ["text1", "text2"]
  1127  	`, &outer)
  1128  	if err != nil {
  1129  		t.Fatal(fmt.Sprintf("Decode failed: %s", err))
  1130  	}
  1131  
  1132  	if outer.Int.value != 10 {
  1133  		t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Int.value, 10)
  1134  	}
  1135  	if *outer.Enum != OtherValue {
  1136  		t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Enum, OtherValue)
  1137  	}
  1138  	if fmt.Sprint(outer.Slice.value) != fmt.Sprint([]string{"text1", "text2"}) {
  1139  		t.Errorf("\nhave:\n%v\nwant:\n%v\n", outer.Slice.value, []string{"text1", "text2"})
  1140  	}
  1141  }
  1142  
  1143  // errorContains checks if the error message in have contains the text in
  1144  // want.
  1145  //
  1146  // This is safe when have is nil. Use an empty string for want if you want to
  1147  // test that err is nil.
  1148  func errorContains(have error, want string) bool {
  1149  	if have == nil {
  1150  		return want == ""
  1151  	}
  1152  	if want == "" {
  1153  		return false
  1154  	}
  1155  	return strings.Contains(have.Error(), want)
  1156  }