Skip to content

Commit e2f6fa2

Browse files
committed
Allow using interface{} as map keys when decoding
Ref: #181
1 parent f0ccf71 commit e2f6fa2

File tree

2 files changed

+50
-25
lines changed

2 files changed

+50
-25
lines changed

decode.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -305,10 +305,10 @@ func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
305305
}
306306

307307
func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
308-
if k := rv.Type().Key().Kind(); k != reflect.String {
309-
return fmt.Errorf(
310-
"toml: cannot decode to a map with non-string key type (%s in %q)",
311-
k, rv.Type())
308+
keyType := rv.Type().Key().Kind()
309+
if keyType != reflect.String && keyType != reflect.Interface {
310+
return fmt.Errorf("toml: cannot decode to a map with non-string key type (%s in %q)",
311+
keyType, rv.Type())
312312
}
313313

314314
tmap, ok := mapping.(map[string]interface{})
@@ -334,7 +334,14 @@ func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
334334
md.context = md.context[0 : len(md.context)-1]
335335

336336
rvkey := indirect(reflect.New(rv.Type().Key()))
337-
rvkey.SetString(k)
337+
338+
switch keyType {
339+
case reflect.Interface:
340+
rvkey.Set(reflect.ValueOf(k))
341+
case reflect.String:
342+
rvkey.SetString(k)
343+
}
344+
338345
rv.SetMapIndex(rvkey, rvval)
339346
}
340347
return nil

decode_test.go

+38-20
Original file line numberDiff line numberDiff line change
@@ -430,38 +430,56 @@ func TestDecodeSizedInts(t *testing.T) {
430430

431431
type NopUnmarshalTOML int
432432

433-
func (NopUnmarshalTOML) UnmarshalTOML(p interface{}) error { return nil }
433+
func (n *NopUnmarshalTOML) UnmarshalTOML(p interface{}) error {
434+
*n = 42
435+
return nil
436+
}
434437

435438
func TestDecodeTypes(t *testing.T) {
436-
type mystr string
439+
type (
440+
mystr string
441+
myiface interface{}
442+
)
437443

438444
for _, tt := range []struct {
439-
v interface{}
440-
want string
445+
v interface{}
446+
want string
447+
wantErr string
441448
}{
442-
{new(map[string]bool), ""},
443-
{new(map[mystr]bool), ""},
444-
{new(NopUnmarshalTOML), ""},
449+
{new(map[string]bool), "&map[F:true]", ""},
450+
{new(map[mystr]bool), "&map[F:true]", ""},
451+
{new(NopUnmarshalTOML), "42", ""},
452+
{new(map[interface{}]bool), "&map[F:true]", ""},
453+
{new(map[myiface]bool), "&map[F:true]", ""},
445454

446-
{3, `toml: cannot decode to non-pointer "int"`},
447-
{map[string]interface{}{}, `toml: cannot decode to non-pointer "map[string]interface {}"`},
455+
{3, "", `toml: cannot decode to non-pointer "int"`},
456+
{map[string]interface{}{}, "", `toml: cannot decode to non-pointer "map[string]interface {}"`},
448457

449-
{(*int)(nil), `toml: cannot decode to nil value of "*int"`},
450-
{(*Unmarshaler)(nil), `toml: cannot decode to nil value of "*toml.Unmarshaler"`},
451-
{nil, `toml: cannot decode to non-pointer <nil>`},
458+
{(*int)(nil), "", `toml: cannot decode to nil value of "*int"`},
459+
{(*Unmarshaler)(nil), "", `toml: cannot decode to nil value of "*toml.Unmarshaler"`},
460+
{nil, "", `toml: cannot decode to non-pointer <nil>`},
452461

453-
{new(map[int]string), "toml: cannot decode to a map with non-string key type"},
454-
{new(map[interface{}]string), "toml: cannot decode to a map with non-string key type"},
462+
{new(map[int]string), "", "toml: cannot decode to a map with non-string key type"},
455463

456-
{new(struct{ F int }), `toml: line 1 (last key "F"): incompatible types: TOML value has type bool; destination has type integer`},
457-
{new(map[string]int), `toml: line 1 (last key "F"): incompatible types: TOML value has type bool; destination has type integer`},
458-
{new(int), `toml: cannot decode to type int`},
459-
{new([]int), "toml: cannot decode to type []int"},
464+
{new(struct{ F int }), "", `toml: line 1 (last key "F"): incompatible types: TOML value has type bool; destination has type integer`},
465+
{new(map[string]int), "", `toml: line 1 (last key "F"): incompatible types: TOML value has type bool; destination has type integer`},
466+
{new(int), "", `toml: cannot decode to type int`},
467+
{new([]int), "", "toml: cannot decode to type []int"},
460468
} {
461469
t.Run(fmt.Sprintf("%T", tt.v), func(t *testing.T) {
462470
_, err := Decode(`F = true`, tt.v)
463-
if !errorContains(err, tt.want) {
464-
t.Errorf("wrong error\nhave: %q\nwant: %q", err, tt.want)
471+
if !errorContains(err, tt.wantErr) {
472+
t.Fatalf("wrong error\nhave: %q\nwant: %q", err, tt.wantErr)
473+
}
474+
475+
if err == nil {
476+
have := fmt.Sprintf("%v", tt.v)
477+
if n, ok := tt.v.(*NopUnmarshalTOML); ok {
478+
have = fmt.Sprintf("%v", *n)
479+
}
480+
if have != tt.want {
481+
t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
482+
}
465483
}
466484
})
467485
}

0 commit comments

Comments
 (0)