diff --git a/conv.go b/conv.go index 567ee4075..c86424155 100644 --- a/conv.go +++ b/conv.go @@ -1,9 +1,12 @@ package goutil import ( + "fmt" + "math" "reflect" "strconv" + "github.com/gookit/goutil/comdef" "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/mathutil" "github.com/gookit/goutil/reflects" @@ -65,6 +68,11 @@ func ToUint(v any) (uint64, error) { return mathutil.ToUint(v) } +// BoolString convert bool to string +func BoolString(bl bool) string { + return strconv.FormatBool(bl) +} + // BaseTypeVal convert custom type or intX,uintX,floatX to generic base type. // // intX/unitX => int64 @@ -76,7 +84,150 @@ func BaseTypeVal(val any) (value any, err error) { return reflects.BaseTypeVal(reflect.ValueOf(val)) } -// BoolString convert bool to string -func BoolString(bl bool) string { - return strconv.FormatBool(bl) +// SafeKind convert input any value to given reflect.Kind type. +func SafeKind(val any, kind reflect.Kind) (newVal any) { + newVal, _ = ToKind(val, kind, nil) + return +} + +// SafeConv convert input any value to given reflect.Kind type. +func SafeConv(val any, kind reflect.Kind) (newVal any) { + newVal, _ = ToKind(val, kind, nil) + return +} + +// ConvTo convert input any value to given reflect.Kind. +func ConvTo(val any, kind reflect.Kind) (newVal any, err error) { + return ToKind(val, kind, nil) +} + +// ConvOrDefault convert input any value to given reflect.Kind. +// if fail will return default value. +func ConvOrDefault(val any, kind reflect.Kind, defVal any) any { + newVal, err := ToKind(val, kind, nil) + if err != nil { + return defVal + } + return newVal +} + +// ToType +// func ToType[T any](val any, kind reflect.Kind, fbFunc func(val any) (T, error)) (newVal T, err error) { +// switch typVal.(type) { // assert ERROR +// case string: +// } +// } + +// ToKind convert input any value to given reflect.Kind type. +// +// TIPs: Only support kind: string, bool, intX, uintX, floatX +// +// Examples: +// +// val, err := ToKind("123", reflect.Int) // 123 +func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newVal any, err error) { + switch kind { + case reflect.Int: + var dstV int + if dstV, err = mathutil.ToInt(val); err == nil { + if dstV > math.MaxInt { + return nil, fmt.Errorf("value overflow int. val: %v", val) + } + newVal = dstV + } + case reflect.Int8: + var dstV int + if dstV, err = mathutil.ToInt(val); err == nil { + if dstV > math.MaxInt8 { + return nil, fmt.Errorf("value overflow int8. val: %v", val) + } + newVal = int8(dstV) + } + case reflect.Int16: + var dstV int + if dstV, err = mathutil.ToInt(val); err == nil { + if dstV > math.MaxInt16 { + return nil, fmt.Errorf("value overflow int16. val: %v", val) + } + newVal = int16(dstV) + } + case reflect.Int32: + var dstV int + if dstV, err = mathutil.ToInt(val); err == nil { + if dstV > math.MaxInt32 { + return nil, fmt.Errorf("value overflow int32. val: %v", val) + } + newVal = int32(dstV) + } + case reflect.Int64: + var dstV int64 + if dstV, err = mathutil.ToInt64(val); err == nil { + newVal = dstV + } + case reflect.Uint: + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { + newVal = uint(dstV) + } + case reflect.Uint8: + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { + if dstV > math.MaxUint8 { + return nil, fmt.Errorf("value overflow uint8. val: %v", val) + } + newVal = uint8(dstV) + } + case reflect.Uint16: + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { + if dstV > math.MaxUint16 { + return nil, fmt.Errorf("value overflow uint16. val: %v", val) + } + newVal = uint16(dstV) + } + case reflect.Uint32: + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { + if dstV > math.MaxUint32 { + return nil, fmt.Errorf("value overflow uint32. val: %v", val) + } + newVal = uint32(dstV) + } + case reflect.Uint64: + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { + newVal = dstV + } + case reflect.Float32: + var dstV float64 + if dstV, err = mathutil.ToFloat(val); err == nil { + if dstV > math.MaxFloat32 { + return nil, fmt.Errorf("value overflow float32. val: %v", val) + } + newVal = float32(dstV) + } + case reflect.Float64: + var dstV float64 + if dstV, err = mathutil.ToFloat(val); err == nil { + newVal = dstV + } + case reflect.String: + var dstV string + if dstV, err = strutil.ToString(val); err == nil { + newVal = dstV + } + case reflect.Bool: + if bl, err1 := comfunc.ToBool(val); err1 == nil { + newVal = bl + } else { + err = err1 + } + default: + if fbFunc != nil { + newVal, err = fbFunc(val) + } else { + err = comdef.ErrConvType + } + } + return } diff --git a/conv_test.go b/conv_test.go index 8f062ab73..23b56c61b 100644 --- a/conv_test.go +++ b/conv_test.go @@ -1,6 +1,8 @@ package goutil_test import ( + "math" + "reflect" "testing" "github.com/gookit/goutil" @@ -78,3 +80,79 @@ func TestBaseTypeVal(t *testing.T) { is.Err(err) is.Nil(val) } + +func TestConvTo(t *testing.T) { + is := assert.New(t) + + tests := []struct { + val any + kind reflect.Kind + out any + ok bool + }{ + // success + {"23", reflect.Int, 23, true}, + {"23", reflect.Int8, int8(23), true}, + {"23", reflect.Uint8, uint8(23), true}, + {"23", reflect.Int16, int16(23), true}, + {"23", reflect.Uint16, uint16(23), true}, + {"23", reflect.Int32, int32(23), true}, + {"23", reflect.Uint32, uint32(23), true}, + {"23", reflect.Int64, int64(23), true}, + {"23", reflect.Uint64, uint64(23), true}, + {"23", reflect.Float64, float64(23), true}, + {"23", reflect.Float32, float32(23), true}, + {"23", reflect.String, "23", true}, + {"true", reflect.Bool, true, true}, + // failed + {nil, reflect.Int, nil, false}, + {"23", reflect.Bool, nil, false}, + {"abc", reflect.Float64, nil, false}, + {"abc", reflect.Float32, nil, false}, + {"abc", reflect.Int, nil, false}, + {"abc", reflect.Int8, nil, false}, + {"abc", reflect.Int16, nil, false}, + {"abc", reflect.Int32, nil, false}, + {"abc23", reflect.Int64, nil, false}, + {"abc", reflect.Uint, nil, false}, + {"abc", reflect.Uint8, nil, false}, + {"abc", reflect.Uint16, nil, false}, + {"abc", reflect.Uint32, nil, false}, + {"abc45", reflect.Uint64, nil, false}, + // overflow + {uint64(math.MaxInt + 2), reflect.Int, nil, false}, + {uint64(math.MaxInt8 + 2), reflect.Int8, nil, false}, + {uint64(math.MaxUint8 + 2), reflect.Uint8, nil, false}, + {uint64(math.MaxInt16 + 2), reflect.Int16, nil, false}, + {uint64(math.MaxUint16 + 2), reflect.Uint16, nil, false}, + {uint64(math.MaxInt32 + 2), reflect.Int32, nil, false}, + {uint64(math.MaxUint32 + 2), reflect.Uint32, nil, false}, + {math.MaxFloat32 * 2.0, reflect.Float32, nil, false}, + } + + // test for goutil.ConvTo + for _, item := range tests { + val, err := goutil.ConvTo(item.val, item.kind) + if item.ok { + is.NoErr(err) + is.Eq(item.out, val) + } else { + is.Err(err, "val: %v, kind: %v", item.val, item.kind) + } + } + + t.Run("extra func", func(t *testing.T) { + // SafeConv + is.Eq(23, goutil.SafeConv("23", reflect.Int)) + is.Eq(23, goutil.SafeKind("23", reflect.Int)) + is.Eq(nil, goutil.SafeKind("abc", reflect.Int)) + + // ConvOrDefault + is.Eq(23, goutil.ConvOrDefault("23", reflect.Int, 0)) + is.Eq(23, goutil.ConvOrDefault("abc", reflect.Int, 23)) + }) + + // ToKind + _, err := goutil.ToKind([]int{23}, reflect.Int, nil) + is.Err(err) +} diff --git a/reflects/conv.go b/reflects/conv.go index aeb91bf9c..ef6786669 100644 --- a/reflects/conv.go +++ b/reflects/conv.go @@ -13,13 +13,18 @@ import ( ) // BaseTypeVal convert custom type or intX,uintX,floatX to generic base type. +func BaseTypeVal(v reflect.Value) (value any, err error) { + return ToBaseVal(v) +} + +// ToBaseVal convert custom type or intX,uintX,floatX to generic base type. // // intX/unitX => int64 // floatX => float64 // string => string // // returns int64,string,float or error -func BaseTypeVal(v reflect.Value) (value any, err error) { +func ToBaseVal(v reflect.Value) (value any, err error) { v = reflect.Indirect(v) switch v.Kind() { @@ -41,7 +46,7 @@ func BaseTypeVal(v reflect.Value) (value any, err error) { func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) { // handle kind: string, bool, intX, uintX, floatX if typ.Kind() == reflect.String || typ.Kind() <= reflect.Float64 { - return ValueByKind(val, typ.Kind()) + return ConvToKind(val, typ.Kind()) } newRv := reflect.ValueOf(val) @@ -60,90 +65,112 @@ func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) { return } -// ValueByKind create reflect.Value by give reflect.Kind +// ValueByKind convert and create reflect.Value by give reflect.Kind +func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { + return ConvToKind(val, kind) +} + +// ConvToKind convert and create reflect.Value by give reflect.Kind // // TIPs: // // Only support kind: string, bool, intX, uintX, floatX -func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { +func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { switch kind { case reflect.Int: - if dstV, err1 := mathutil.ToInt(val); err1 == nil { + var dstV int + if dstV, err = mathutil.ToInt(val); err == nil { rv = reflect.ValueOf(dstV) } case reflect.Int8: - if dstV, err1 := mathutil.ToInt(val); err1 == nil { + var dstV int + if dstV, err = mathutil.ToInt(val); err == nil { if dstV > math.MaxInt8 { return rv, fmt.Errorf("value overflow int8. val: %v", val) } rv = reflect.ValueOf(int8(dstV)) } case reflect.Int16: - if dstV, err1 := mathutil.ToInt(val); err1 == nil { + var dstV int + if dstV, err = mathutil.ToInt(val); err == nil { if dstV > math.MaxInt16 { return rv, fmt.Errorf("value overflow int16. val: %v", val) } rv = reflect.ValueOf(int16(dstV)) } case reflect.Int32: - if dstV, err1 := mathutil.ToInt(val); err1 == nil { + var dstV int + if dstV, err = mathutil.ToInt(val); err == nil { if dstV > math.MaxInt32 { return rv, fmt.Errorf("value overflow int32. val: %v", val) } rv = reflect.ValueOf(int32(dstV)) } case reflect.Int64: - if dstV, err1 := mathutil.ToInt64(val); err1 == nil { + var dstV int64 + if dstV, err = mathutil.ToInt64(val); err == nil { rv = reflect.ValueOf(dstV) } case reflect.Uint: - if dstV, err1 := mathutil.ToUint(val); err1 == nil { + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { rv = reflect.ValueOf(uint(dstV)) } case reflect.Uint8: - if dstV, err1 := mathutil.ToUint(val); err1 == nil { + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint8 { return rv, fmt.Errorf("value overflow uint8. val: %v", val) } rv = reflect.ValueOf(uint8(dstV)) } case reflect.Uint16: - if dstV, err1 := mathutil.ToUint(val); err1 == nil { + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint16 { return rv, fmt.Errorf("value overflow uint16. val: %v", val) } rv = reflect.ValueOf(uint16(dstV)) } case reflect.Uint32: - if dstV, err1 := mathutil.ToUint(val); err1 == nil { + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint32 { return rv, fmt.Errorf("value overflow uint32. val: %v", val) } rv = reflect.ValueOf(uint32(dstV)) } case reflect.Uint64: - if dstV, err1 := mathutil.ToUint(val); err1 == nil { + var dstV uint64 + if dstV, err = mathutil.ToUint(val); err == nil { rv = reflect.ValueOf(dstV) } case reflect.Float32: - if dstV, err1 := mathutil.ToFloat(val); err1 == nil { + var dstV float64 + if dstV, err = mathutil.ToFloat(val); err == nil { + if dstV > math.MaxFloat32 { + return rv, fmt.Errorf("value overflow float32. val: %v", val) + } rv = reflect.ValueOf(float32(dstV)) } case reflect.Float64: - if dstV, err1 := mathutil.ToFloat(val); err1 == nil { + var dstV float64 + if dstV, err = mathutil.ToFloat(val); err == nil { rv = reflect.ValueOf(dstV) } case reflect.String: if dstV, err1 := strutil.ToString(val); err1 == nil { rv = reflect.ValueOf(dstV) + } else { + err = err1 } case reflect.Bool: - if bl, err := comfunc.ToBool(val); err == nil { + if bl, err1 := comfunc.ToBool(val); err1 == nil { rv = reflect.ValueOf(bl) + } else { + err = err1 } - } - - if !rv.IsValid() { + default: err = comdef.ErrConvType } return