Skip to content

Commit

Permalink
✨ feat: goutil - add new convert func ConvTo, ToKind, SafeConv for qu…
Browse files Browse the repository at this point in the history
…ick conv value type
  • Loading branch information
inhere committed Sep 1, 2023
1 parent da08abc commit c8acbff
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 23 deletions.
157 changes: 154 additions & 3 deletions conv.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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
}
78 changes: 78 additions & 0 deletions conv_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package goutil_test

import (
"math"
"reflect"
"testing"

"github.com/gookit/goutil"
Expand Down Expand Up @@ -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)
}
Loading

0 comments on commit c8acbff

Please sign in to comment.