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()