From bec4b0532e3083001f27eda3e7e78f124822634f Mon Sep 17 00:00:00 2001 From: tlipoca9 Date: Sun, 25 Feb 2024 00:54:21 +0800 Subject: [PATCH 1/2] feat: add StringToBasicTypesHookFunc --- decode_hooks.go | 243 +++++++++++++++++++ decode_hooks_test.go | 552 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 795 insertions(+) diff --git a/decode_hooks.go b/decode_hooks.go index 840d6ad..24d82f0 100644 --- a/decode_hooks.go +++ b/decode_hooks.go @@ -332,3 +332,246 @@ func StringToNetIPAddrPortHookFunc() DecodeHookFunc { return netip.ParseAddrPort(data.(string)) } } + +// StringToBasicTypeHookFunc returns a DecodeHookFunc that converts +// strings to basic types. +// int8, uint8, int16, uint16, int32, uint32, int64, uint64, int, uint, float32, float64, bool, byte, rune, complex64, complex128 +func StringToBasicTypeHookFunc() DecodeHookFunc { + return ComposeDecodeHookFunc( + StringToInt8HookFunc(), + StringToUint8HookFunc(), + StringToInt16HookFunc(), + StringToUint16HookFunc(), + StringToInt32HookFunc(), + StringToUint32HookFunc(), + StringToInt64HookFunc(), + StringToUint64HookFunc(), + StringToIntHookFunc(), + StringToUintHookFunc(), + StringToFloat32HookFunc(), + StringToFloat64HookFunc(), + StringToBoolHookFunc(), + // byte and rune are aliases for uint8 and int32 respectively + // StringToByteHookFunc(), + // StringToRuneHookFunc(), + StringToComplex64HookFunc(), + StringToComplex128HookFunc(), + ) +} + +// StringToInt8HookFunc returns a DecodeHookFunc that converts +// strings to int8. +func StringToInt8HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int8 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 8) + return int8(i64), err + } +} + +// StringToUint8HookFunc returns a DecodeHookFunc that converts +// strings to uint8. +func StringToUint8HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint8 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 8) + return uint8(u64), err + } +} + +// StringToInt16HookFunc returns a DecodeHookFunc that converts +// strings to int16. +func StringToInt16HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int16 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 16) + return int16(i64), err + } +} + +// StringToUint16HookFunc returns a DecodeHookFunc that converts +// strings to uint16. +func StringToUint16HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint16 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 16) + return uint16(u64), err + } +} + +// StringToInt32HookFunc returns a DecodeHookFunc that converts +// strings to int32. +func StringToInt32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int32 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 32) + return int32(i64), err + } +} + +// StringToUint32HookFunc returns a DecodeHookFunc that converts +// strings to uint32. +func StringToUint32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint32 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 32) + return uint32(u64), err + } +} + +// StringToInt64HookFunc returns a DecodeHookFunc that converts +// strings to int64. +func StringToInt64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int64 { + return data, nil + } + + // Convert it by parsing + return strconv.ParseInt(data.(string), 0, 64) + } +} + +// StringToUint64HookFunc returns a DecodeHookFunc that converts +// strings to uint64. +func StringToUint64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint64 { + return data, nil + } + + // Convert it by parsing + return strconv.ParseUint(data.(string), 0, 64) + } +} + +// StringToIntHookFunc returns a DecodeHookFunc that converts +// strings to int. +func StringToIntHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 0) + return int(i64), err + } +} + +// StringToUintHookFunc returns a DecodeHookFunc that converts +// strings to uint. +func StringToUintHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 0) + return uint(u64), err + } +} + +// StringToFloat32HookFunc returns a DecodeHookFunc that converts +// strings to float32. +func StringToFloat32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Float32 { + return data, nil + } + + // Convert it by parsing + f64, err := strconv.ParseFloat(data.(string), 32) + return float32(f64), err + } +} + +// StringToFloat64HookFunc returns a DecodeHookFunc that converts +// strings to float64. +func StringToFloat64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Float64 { + return data, nil + } + + // Convert it by parsing + return strconv.ParseFloat(data.(string), 64) + } +} + +// StringToBoolHookFunc returns a DecodeHookFunc that converts +// strings to bool. +func StringToBoolHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Bool { + return data, nil + } + + // Convert it by parsing + return strconv.ParseBool(data.(string)) + } +} + +// StringToByteHookFunc returns a DecodeHookFunc that converts +// strings to byte. +func StringToByteHookFunc() DecodeHookFunc { + return StringToUint8HookFunc() +} + +// StringToRuneHookFunc returns a DecodeHookFunc that converts +// strings to rune. +func StringToRuneHookFunc() DecodeHookFunc { + return StringToInt32HookFunc() +} + +// StringToComplex64HookFunc returns a DecodeHookFunc that converts +// strings to complex64. +func StringToComplex64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Complex64 { + return data, nil + } + + // Convert it by parsing + c128, err := strconv.ParseComplex(data.(string), 64) + return complex64(c128), err + } +} + +// StringToComplex128HookFunc returns a DecodeHookFunc that converts +// strings to complex128. +func StringToComplex128HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Complex128 { + return data, nil + } + + // Convert it by parsing + return strconv.ParseComplex(data.(string), 128) + } +} diff --git a/decode_hooks_test.go b/decode_hooks_test.go index c53d73f..0604ddd 100644 --- a/decode_hooks_test.go +++ b/decode_hooks_test.go @@ -7,6 +7,7 @@ import ( "net" "net/netip" "reflect" + "strings" "testing" "time" ) @@ -647,3 +648,554 @@ func TestStringToNetIPAddrPortHookFunc(t *testing.T) { } } } + +func TestStringToBasicTypeHookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, strValue, "42", false}, + {strValue, reflect.ValueOf(int8(0)), int8(42), false}, + {strValue, reflect.ValueOf(uint8(0)), uint8(42), false}, + {strValue, reflect.ValueOf(int16(0)), int16(42), false}, + {strValue, reflect.ValueOf(uint16(0)), uint16(42), false}, + {strValue, reflect.ValueOf(int32(0)), int32(42), false}, + {strValue, reflect.ValueOf(uint32(0)), uint32(42), false}, + {strValue, reflect.ValueOf(int64(0)), int64(42), false}, + {strValue, reflect.ValueOf(uint64(0)), uint64(42), false}, + {strValue, reflect.ValueOf(int(0)), int(42), false}, + {strValue, reflect.ValueOf(uint(0)), uint(42), false}, + {strValue, reflect.ValueOf(float32(0)), float32(42), false}, + {strValue, reflect.ValueOf(float64(0)), float64(42), false}, + {reflect.ValueOf("true"), reflect.ValueOf(bool(false)), true, false}, + {strValue, reflect.ValueOf(byte(0)), byte(42), false}, + {strValue, reflect.ValueOf(rune(0)), rune(42), false}, + {strValue, reflect.ValueOf(complex64(0)), complex64(42), false}, + {strValue, reflect.ValueOf(complex128(0)), complex128(42), false}, + } + + for i, tc := range cases { + f := StringToBasicTypeHookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToInt8HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + int8Value := reflect.ValueOf(int8(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, int8Value, int8(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), int8Value, int8(0), true}, + {reflect.ValueOf("42.42"), int8Value, int8(0), true}, + {reflect.ValueOf("-42"), int8Value, int8(-42), false}, + {reflect.ValueOf("0b101010"), int8Value, int8(42), false}, + {reflect.ValueOf("052"), int8Value, int8(42), false}, + {reflect.ValueOf("0o52"), int8Value, int8(42), false}, + {reflect.ValueOf("0x2a"), int8Value, int8(42), false}, + {reflect.ValueOf("0X2A"), int8Value, int8(42), false}, + {reflect.ValueOf("0"), int8Value, int8(0), false}, + {reflect.ValueOf("0.0"), int8Value, int8(0), true}, + } + + for i, tc := range cases { + f := StringToInt8HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToUint8HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + uint8Value := reflect.ValueOf(uint8(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, uint8Value, uint8(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), uint8Value, uint8(0), true}, + {reflect.ValueOf("42.42"), uint8Value, uint8(0), true}, + {reflect.ValueOf("-42"), uint8Value, uint8(0), true}, + {reflect.ValueOf("0b101010"), uint8Value, uint8(42), false}, + {reflect.ValueOf("052"), uint8Value, uint8(42), false}, + {reflect.ValueOf("0o52"), uint8Value, uint8(42), false}, + {reflect.ValueOf("0x2a"), uint8Value, uint8(42), false}, + {reflect.ValueOf("0X2A"), uint8Value, uint8(42), false}, + {reflect.ValueOf("0"), uint8Value, uint8(0), false}, + {reflect.ValueOf("0.0"), uint8Value, uint8(0), true}, + } + + for i, tc := range cases { + f := StringToUint8HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToInt16HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + int16Value := reflect.ValueOf(int16(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, int16Value, int16(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), int16Value, int16(0), true}, + {reflect.ValueOf("42.42"), int16Value, int16(0), true}, + {reflect.ValueOf("-42"), int16Value, int16(-42), false}, + {reflect.ValueOf("0b101010"), int16Value, int16(42), false}, + {reflect.ValueOf("052"), int16Value, int16(42), false}, + {reflect.ValueOf("0o52"), int16Value, int16(42), false}, + {reflect.ValueOf("0x2a"), int16Value, int16(42), false}, + {reflect.ValueOf("0X2A"), int16Value, int16(42), false}, + {reflect.ValueOf("0"), int16Value, int16(0), false}, + {reflect.ValueOf("0.0"), int16Value, int16(0), true}, + } + + for i, tc := range cases { + f := StringToInt16HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToUint16HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + uint16Value := reflect.ValueOf(uint16(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, uint16Value, uint16(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), uint16Value, uint16(0), true}, + {reflect.ValueOf("42.42"), uint16Value, uint16(0), true}, + {reflect.ValueOf("-42"), uint16Value, uint16(0), true}, + {reflect.ValueOf("0b101010"), uint16Value, uint16(42), false}, + {reflect.ValueOf("052"), uint16Value, uint16(42), false}, + {reflect.ValueOf("0o52"), uint16Value, uint16(42), false}, + {reflect.ValueOf("0x2a"), uint16Value, uint16(42), false}, + {reflect.ValueOf("0X2A"), uint16Value, uint16(42), false}, + {reflect.ValueOf("0"), uint16Value, uint16(0), false}, + {reflect.ValueOf("0.0"), uint16Value, uint16(0), true}, + } + + for i, tc := range cases { + f := StringToUint16HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToInt32HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + int32Value := reflect.ValueOf(int32(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, int32Value, int32(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), int32Value, int32(0), true}, + {reflect.ValueOf("42.42"), int32Value, int32(0), true}, + {reflect.ValueOf("-42"), int32Value, int32(-42), false}, + {reflect.ValueOf("0b101010"), int32Value, int32(42), false}, + {reflect.ValueOf("052"), int32Value, int32(42), false}, + {reflect.ValueOf("0o52"), int32Value, int32(42), false}, + {reflect.ValueOf("0x2a"), int32Value, int32(42), false}, + {reflect.ValueOf("0X2A"), int32Value, int32(42), false}, + {reflect.ValueOf("0"), int32Value, int32(0), false}, + {reflect.ValueOf("0.0"), int32Value, int32(0), true}, + } + + for i, tc := range cases { + f := StringToInt32HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToUint32HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + uint32Value := reflect.ValueOf(uint32(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, uint32Value, uint32(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), uint32Value, uint32(0), true}, + {reflect.ValueOf("42.42"), uint32Value, uint32(0), true}, + {reflect.ValueOf("-42"), uint32Value, uint32(0), true}, + {reflect.ValueOf("0b101010"), uint32Value, uint32(42), false}, + {reflect.ValueOf("052"), uint32Value, uint32(42), false}, + {reflect.ValueOf("0o52"), uint32Value, uint32(42), false}, + {reflect.ValueOf("0x2a"), uint32Value, uint32(42), false}, + {reflect.ValueOf("0X2A"), uint32Value, uint32(42), false}, + {reflect.ValueOf("0"), uint32Value, uint32(0), false}, + {reflect.ValueOf("0.0"), uint32Value, uint32(0), true}, + } + + for i, tc := range cases { + f := StringToUint32HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToInt64HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + int64Value := reflect.ValueOf(int64(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, int64Value, int64(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), int64Value, int64(0), true}, + {reflect.ValueOf("42.42"), int64Value, int64(0), true}, + {reflect.ValueOf("-42"), int64Value, int64(-42), false}, + {reflect.ValueOf("0b101010"), int64Value, int64(42), false}, + {reflect.ValueOf("052"), int64Value, int64(42), false}, + {reflect.ValueOf("0o52"), int64Value, int64(42), false}, + {reflect.ValueOf("0x2a"), int64Value, int64(42), false}, + {reflect.ValueOf("0X2A"), int64Value, int64(42), false}, + {reflect.ValueOf("0"), int64Value, int64(0), false}, + {reflect.ValueOf("0.0"), int64Value, int64(0), true}, + } + + for i, tc := range cases { + f := StringToInt64HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToUint64HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + uint64Value := reflect.ValueOf(uint64(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, uint64Value, uint64(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), uint64Value, uint64(0), true}, + {reflect.ValueOf("42.42"), uint64Value, uint64(0), true}, + {reflect.ValueOf("-42"), uint64Value, uint64(0), true}, + {reflect.ValueOf("0b101010"), uint64Value, uint64(42), false}, + {reflect.ValueOf("052"), uint64Value, uint64(42), false}, + {reflect.ValueOf("0o52"), uint64Value, uint64(42), false}, + {reflect.ValueOf("0x2a"), uint64Value, uint64(42), false}, + {reflect.ValueOf("0X2A"), uint64Value, uint64(42), false}, + {reflect.ValueOf("0"), uint64Value, uint64(0), false}, + {reflect.ValueOf("0.0"), uint64Value, uint64(0), true}, + } + + for i, tc := range cases { + f := StringToUint64HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToIntHookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + intValue := reflect.ValueOf(int(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, intValue, int(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), intValue, int(0), true}, + {reflect.ValueOf("42.42"), intValue, int(0), true}, + {reflect.ValueOf("-42"), intValue, int(-42), false}, + {reflect.ValueOf("0b101010"), intValue, int(42), false}, + {reflect.ValueOf("052"), intValue, int(42), false}, + {reflect.ValueOf("0o52"), intValue, int(42), false}, + {reflect.ValueOf("0x2a"), intValue, int(42), false}, + {reflect.ValueOf("0X2A"), intValue, int(42), false}, + {reflect.ValueOf("0"), intValue, int(0), false}, + {reflect.ValueOf("0.0"), intValue, int(0), true}, + } + + for i, tc := range cases { + f := StringToIntHookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToUintHookFunc(t *testing.T) { + strValue := reflect.ValueOf("42") + uintValue := reflect.ValueOf(uint(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, uintValue, uint(42), false}, + {strValue, strValue, "42", false}, + {reflect.ValueOf(strings.Repeat("42", 42)), uintValue, uint(0), true}, + {reflect.ValueOf("42.42"), uintValue, uint(0), true}, + {reflect.ValueOf("-42"), uintValue, uint(0), true}, + {reflect.ValueOf("0b101010"), uintValue, uint(42), false}, + {reflect.ValueOf("052"), uintValue, uint(42), false}, + {reflect.ValueOf("0o52"), uintValue, uint(42), false}, + {reflect.ValueOf("0x2a"), uintValue, uint(42), false}, + {reflect.ValueOf("0X2A"), uintValue, uint(42), false}, + {reflect.ValueOf("0"), uintValue, uint(0), false}, + {reflect.ValueOf("0.0"), uintValue, uint(0), true}, + } + + for i, tc := range cases { + f := StringToUintHookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, tc.err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToFloat32HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42.42") + float32Value := reflect.ValueOf(float32(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, float32Value, float32(42.42), false}, + {strValue, strValue, "42.42", false}, + {reflect.ValueOf(strings.Repeat("42", 420)), float32Value, float32(0), true}, + {reflect.ValueOf("42.42.42"), float32Value, float32(0), true}, + {reflect.ValueOf("-42.42"), float32Value, float32(-42.42), false}, + {reflect.ValueOf("0"), float32Value, float32(0), false}, + {reflect.ValueOf("1e3"), float32Value, float32(1000), false}, + {reflect.ValueOf("1e-3"), float32Value, float32(0.001), false}, + } + + for i, tc := range cases { + f := StringToFloat32HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToFloat64HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42.42") + float64Value := reflect.ValueOf(float64(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, float64Value, float64(42.42), false}, + {strValue, strValue, "42.42", false}, + {reflect.ValueOf(strings.Repeat("42", 420)), float64Value, float64(0), true}, + {reflect.ValueOf("42.42.42"), float64Value, float64(0), true}, + {reflect.ValueOf("-42.42"), float64Value, float64(-42.42), false}, + {reflect.ValueOf("0"), float64Value, float64(0), false}, + {reflect.ValueOf("0.0"), float64Value, float64(0), false}, + {reflect.ValueOf("1e3"), float64Value, float64(1000), false}, + {reflect.ValueOf("1e-3"), float64Value, float64(0.001), false}, + } + + for i, tc := range cases { + f := StringToFloat64HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToComplex64HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42.42+42.42i") + complex64Value := reflect.ValueOf(complex64(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, complex64Value, complex(float32(42.42), float32(42.42)), false}, + {strValue, strValue, "42.42+42.42i", false}, + {reflect.ValueOf(strings.Repeat("42", 420)), complex64Value, complex(float32(0), 0), true}, + {reflect.ValueOf("42.42.42"), complex64Value, complex(float32(0), 0), true}, + {reflect.ValueOf("-42.42"), complex64Value, complex(float32(-42.42), 0), false}, + {reflect.ValueOf("0"), complex64Value, complex(float32(0), 0), false}, + {reflect.ValueOf("0.0"), complex64Value, complex(float32(0), 0), false}, + {reflect.ValueOf("1e3"), complex64Value, complex(float32(1000), 0), false}, + {reflect.ValueOf("1e-3"), complex64Value, complex(float32(0.001), 0), false}, + {reflect.ValueOf("1e3i"), complex64Value, complex(float32(0), 1000), false}, + {reflect.ValueOf("1e-3i"), complex64Value, complex(float32(0), 0.001), false}, + } + + for i, tc := range cases { + f := StringToComplex64HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} + +func TestStringToComplex128HookFunc(t *testing.T) { + strValue := reflect.ValueOf("42.42+42.42i") + complex128Value := reflect.ValueOf(complex128(0)) + + cases := []struct { + f, t reflect.Value + result interface{} + err bool + }{ + {strValue, complex128Value, complex(42.42, 42.42), false}, + {strValue, strValue, "42.42+42.42i", false}, + {reflect.ValueOf(strings.Repeat("42", 420)), complex128Value, complex(0, 0), true}, + {reflect.ValueOf("42.42.42"), complex128Value, complex(0, 0), true}, + {reflect.ValueOf("-42.42"), complex128Value, complex(-42.42, 0), false}, + {reflect.ValueOf("0"), complex128Value, complex(0, 0), false}, + {reflect.ValueOf("0.0"), complex128Value, complex(0, 0), false}, + {reflect.ValueOf("1e3"), complex128Value, complex(1000, 0), false}, + {reflect.ValueOf("1e-3"), complex128Value, complex(0.001, 0), false}, + {reflect.ValueOf("1e3i"), complex128Value, complex(0, 1000), false}, + {reflect.ValueOf("1e-3i"), complex128Value, complex(0, 0.001), false}, + } + + for i, tc := range cases { + f := StringToComplex128HookFunc() + actual, err := DecodeHookExec(f, tc.f, tc.t) + if tc.err != (err != nil) { + t.Fatalf("case %d: expected err %#v", i, err) + } + if !tc.err && !reflect.DeepEqual(actual, tc.result) { + t.Fatalf( + "case %d: expected %#v, got %#v", + i, tc.result, actual) + } + } +} From 231f31b7a6019ef5f2e735f9d134ffb86332b411 Mon Sep 17 00:00:00 2001 From: tlipoca9 Date: Sun, 25 Feb 2024 01:34:15 +0800 Subject: [PATCH 2/2] feat: add decodeComplex --- mapstructure.go | 20 ++++++++++++++++++++ mapstructure_test.go | 16 ++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/mapstructure.go b/mapstructure.go index 27f21bc..c8619fa 100644 --- a/mapstructure.go +++ b/mapstructure.go @@ -478,6 +478,8 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e err = d.decodeUint(name, input, outVal) case reflect.Float32: err = d.decodeFloat(name, input, outVal) + case reflect.Complex64: + err = d.decodeComplex(name, input, outVal) case reflect.Struct: err = d.decodeStruct(name, input, outVal) case reflect.Map: @@ -796,6 +798,22 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) return nil } +func (d *Decoder) decodeComplex(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + switch { + case dataKind == reflect.Complex64: + val.SetComplex(dataVal.Complex()) + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", + name, val.Type(), dataVal.Type(), data) + } + + return nil +} + func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error { valType := val.Type() valKeyType := valType.Key() @@ -1531,6 +1549,8 @@ func getKind(val reflect.Value) reflect.Kind { return reflect.Uint case kind >= reflect.Float32 && kind <= reflect.Float64: return reflect.Float32 + case kind >= reflect.Complex64 && kind <= reflect.Complex128: + return reflect.Complex64 default: return kind } diff --git a/mapstructure_test.go b/mapstructure_test.go index 7d86acd..d604fcf 100644 --- a/mapstructure_test.go +++ b/mapstructure_test.go @@ -28,6 +28,8 @@ type Basic struct { VjsonUint64 uint64 VjsonFloat float64 VjsonNumber json.Number + Vcomplex64 complex64 + Vcomplex128 complex128 } type BasicPointer struct { @@ -248,6 +250,8 @@ func TestBasicTypes(t *testing.T) { "vjsonUint64": json.Number("9223372036854775809"), // 2^63 + 1 "vjsonFloat": json.Number("1234.5"), "vjsonNumber": json.Number("1234.5"), + "vcomplex64": complex(float32(42), float32(42)), + "vcomplex128": complex(42, 42), } var result Basic @@ -320,6 +324,14 @@ func TestBasicTypes(t *testing.T) { if !reflect.DeepEqual(result.VjsonNumber, json.Number("1234.5")) { t.Errorf("vjsonnumber value should be '1234.5': %T, %#v", result.VjsonNumber, result.VjsonNumber) } + + if real(result.Vcomplex64) != 42 || imag(result.Vcomplex64) != 42 { + t.Errorf("vcomplex64 value shou be 42+42i: %#v", result.Vcomplex64) + } + + if real(result.Vcomplex128) != 42 || imag(result.Vcomplex128) != 42 { + t.Errorf("vcomplex64 value shou be 42+42i: %#v", result.Vcomplex128) + } } func TestBasic_IntWithFloat(t *testing.T) { @@ -1933,6 +1945,8 @@ func TestDecodeTable(t *testing.T) { "VjsonUint64": uint64(0), "VjsonFloat": 0.0, "VjsonNumber": json.Number(""), + "Vcomplex64": complex64(0), + "Vcomplex128": complex128(0), }, false, }, @@ -1975,6 +1989,8 @@ func TestDecodeTable(t *testing.T) { "VjsonUint64": uint64(0), "VjsonFloat": 0.0, "VjsonNumber": json.Number(""), + "Vcomplex64": complex64(0), + "Vcomplex128": complex128(0), }, }, false,