diff --git a/common/structure/structure.go b/common/structure/structure.go index 88d7df2e..78f344a4 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -86,11 +86,16 @@ func (d *Decoder) Decode(src map[string]any, dst any) error { } func (d *Decoder) decode(name string, data any, val reflect.Value) error { - switch val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + kind := val.Kind() + switch { + case isInt(kind): return d.decodeInt(name, data, val) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + case isUint(kind): return d.decodeUint(name, data, val) + case isFloat(kind): + return d.decodeFloat(name, data, val) + } + switch kind { case reflect.String: return d.decodeString(name, data, val) case reflect.Bool: @@ -108,15 +113,42 @@ func (d *Decoder) decode(name string, data any, val reflect.Value) error { } } +func isInt(kind reflect.Kind) bool { + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + default: + return false + } +} + +func isUint(kind reflect.Kind) bool { + switch kind { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + default: + return false + } +} + +func isFloat(kind reflect.Kind) bool { + switch kind { + case reflect.Float32, reflect.Float64: + return true + default: + return false + } +} + func (d *Decoder) decodeInt(name string, data any, val reflect.Value) (err error) { dataVal := reflect.ValueOf(data) kind := dataVal.Kind() switch { - case kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64: + case isInt(kind): val.SetInt(dataVal.Int()) - case (kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64) && d.option.WeaklyTypedInput: + case isUint(kind) && d.option.WeaklyTypedInput: val.SetInt(int64(dataVal.Uint())) - case kind == reflect.Float64 && d.option.WeaklyTypedInput: + case isFloat(kind) && d.option.WeaklyTypedInput: val.SetInt(int64(dataVal.Float())) case kind == reflect.String && d.option.WeaklyTypedInput: var i int64 @@ -139,12 +171,12 @@ func (d *Decoder) decodeUint(name string, data any, val reflect.Value) (err erro dataVal := reflect.ValueOf(data) kind := dataVal.Kind() switch { - case kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64: + case isUint(kind): val.SetUint(dataVal.Uint()) - case (kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64) && d.option.WeaklyTypedInput: + case isInt(kind) && d.option.WeaklyTypedInput: val.SetUint(uint64(dataVal.Int())) - case kind == reflect.Float64 && d.option.WeaklyTypedInput: - val.SetUint(uint64(int64(dataVal.Float()))) + case isFloat(kind) && d.option.WeaklyTypedInput: + val.SetUint(uint64(dataVal.Float())) case kind == reflect.String && d.option.WeaklyTypedInput: var i uint64 i, err = strconv.ParseUint(dataVal.String(), 0, val.Type().Bits()) @@ -162,14 +194,45 @@ func (d *Decoder) decodeUint(name string, data any, val reflect.Value) (err erro return err } +func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) (err error) { + dataVal := reflect.ValueOf(data) + kind := dataVal.Kind() + switch { + case isFloat(kind): + val.SetFloat(dataVal.Float()) + case isUint(kind): + val.SetFloat(float64(dataVal.Uint())) + case isInt(kind) && d.option.WeaklyTypedInput: + val.SetFloat(float64(dataVal.Int())) + case kind == reflect.String && d.option.WeaklyTypedInput: + var i float64 + i, err = strconv.ParseFloat(dataVal.String(), val.Type().Bits()) + if err == nil { + val.SetFloat(i) + } else { + err = fmt.Errorf("cannot parse '%s' as int: %s", name, err) + } + default: + err = fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type(), + ) + } + return err +} + func (d *Decoder) decodeString(name string, data any, val reflect.Value) (err error) { dataVal := reflect.ValueOf(data) kind := dataVal.Kind() switch { case kind == reflect.String: val.SetString(dataVal.String()) - case kind == reflect.Int && d.option.WeaklyTypedInput: + case isInt(kind) && d.option.WeaklyTypedInput: val.SetString(strconv.FormatInt(dataVal.Int(), 10)) + case isUint(kind) && d.option.WeaklyTypedInput: + val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) + case isFloat(kind) && d.option.WeaklyTypedInput: + val.SetString(strconv.FormatFloat(dataVal.Float(), 'E', -1, dataVal.Type().Bits())) default: err = fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s'", @@ -185,8 +248,10 @@ func (d *Decoder) decodeBool(name string, data any, val reflect.Value) (err erro switch { case kind == reflect.Bool: val.SetBool(dataVal.Bool()) - case kind == reflect.Int && d.option.WeaklyTypedInput: + case isInt(kind) && d.option.WeaklyTypedInput: val.SetBool(dataVal.Int() != 0) + case isUint(kind) && d.option.WeaklyTypedInput: + val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) default: err = fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s'", @@ -201,7 +266,7 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { valType := val.Type() valElemType := valType.Elem() - if dataVal.Kind() == reflect.String && valElemType.Kind() == reflect.Uint8 { + if dataVal.Kind() == reflect.String && valElemType.Kind() == reflect.Uint8 { // from encoding/json s := []byte(dataVal.String()) b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) n, err := base64.StdEncoding.Decode(b, s)