properly handle integer conversions for int/uint fields in structs
diff --git a/mapstructure.go b/mapstructure.go index b7b3edc..9da5586 100644 --- a/mapstructure.go +++ b/mapstructure.go
@@ -65,12 +65,12 @@ switch k { case reflect.Bool: fallthrough + case reflect.String: + return decodeBasic(name, data, val) case reflect.Int: fallthrough - case reflect.String: - fallthrough case reflect.Uint: - return decodeBasic(name, data, val) + return decodeInt(name, data, val) case reflect.Struct: return decodeStruct(name, data, val) case reflect.Map: @@ -103,6 +103,59 @@ return nil } +func decodeInt(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.ValueOf(data) + if !dataVal.IsValid() { + // This should never happen + panic("data is invalid") + } + + dataKind := dataVal.Kind() + if dataKind >= reflect.Int && dataKind <= reflect.Int64 { + dataKind = reflect.Int + } else if dataKind >= reflect.Uint && dataKind <= reflect.Uint64 { + dataKind = reflect.Uint + } else if dataKind >= reflect.Float32 && dataKind <= reflect.Float64 { + dataKind = reflect.Float32 + } else { + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + valKind := val.Kind() + if valKind >= reflect.Int && valKind <= reflect.Int64 { + valKind = reflect.Int + } else if valKind >= reflect.Uint && valKind <= reflect.Uint64 { + valKind = reflect.Uint + } + + switch dataKind { + case reflect.Int: + if valKind == reflect.Int { + val.SetInt(dataVal.Int()) + } else { + val.SetUint(uint64(dataVal.Uint())) + } + case reflect.Uint: + if valKind == reflect.Int { + val.SetInt(int64(dataVal.Uint())) + } else { + val.SetUint(dataVal.Uint()) + } + case reflect.Float32: + if valKind == reflect.Int { + val.SetInt(int64(dataVal.Float())) + } else { + val.SetUint(uint64(dataVal.Float())) + } + default: + panic("should never reach") + } + + return nil +} + func decodeMap(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) if dataVal.Kind() != reflect.Map {
diff --git a/mapstructure_examples_test.go b/mapstructure_examples_test.go index e7449c5..0e1af6b 100644 --- a/mapstructure_examples_test.go +++ b/mapstructure_examples_test.go
@@ -4,14 +4,14 @@ "fmt" ) -type Person struct { - Name string - Age int - Emails []string - Extra map[string]string -} - func ExampleDecode() { + type Person struct { + Name string + Age int + Emails []string + Extra map[string]string + } + // This input can come from anywhere, but typically comes from // something like decoding JSON where we're not quite sure of the // struct initially. @@ -36,6 +36,13 @@ } func ExampleDecode_errors() { + type Person struct { + Name string + Age int + Emails []string + Extra map[string]string + } + // This input can come from anywhere, but typically comes from // something like decoding JSON where we're not quite sure of the // struct initially. @@ -56,7 +63,7 @@ // 5 error(s) decoding: // // * 'Name' expected type 'string', got 'int' - // * 'Age' expected type 'int', got 'string' + // * 'Age' expected type 'int', got unconvertible type 'string' // * 'Emails[0]' expected type 'string', got 'int' // * 'Emails[1]' expected type 'string', got 'int' // * 'Emails[2]' expected type 'string', got 'int'
diff --git a/mapstructure_test.go b/mapstructure_test.go index 663f520..cda1449 100644 --- a/mapstructure_test.go +++ b/mapstructure_test.go
@@ -75,6 +75,20 @@ } } +func TestBasic_IntWithFloat(t *testing.T) { + t.Parallel() + + input := map[string]interface{}{ + "vint": float64(42), + } + + var result Basic + err := Decode(input, &result) + if err != nil { + t.Fatalf("got an err: %s", err) + } +} + func TestMap(t *testing.T) { t.Parallel()