diff --git a/internal/comfunc/convert.go b/internal/comfunc/convert.go index e6d6c6ba0..3d4f3b278 100644 --- a/internal/comfunc/convert.go +++ b/internal/comfunc/convert.go @@ -70,9 +70,10 @@ type ConvOption struct { // if ture: value is nil, will return convert error; // if false(default): value is nil, will convert to zero value NilAsFail bool - // EnablePtr auto convert ptr type(int,float,string) value. eg: *int to int + // HandlePtr auto convert ptr type(int,float,string) value. eg: *int to int // - if true: will use real type try convert. default is false - EnablePtr bool + // - NOTE: current T type's ptr is default support. + HandlePtr bool // set custom fallback convert func for not supported type. UserConvFn comdef.ToStringFunc } @@ -85,6 +86,11 @@ var StrBySprintFn = func(v any) (string, error) { return fmt.Sprint(v), nil } +// WithHandlePtr set ConvOption.HandlePtr option +func WithHandlePtr(opt *ConvOption) { + opt.HandlePtr = true +} + // WithUserConvFn set ConvOption.UserConvFn option func WithUserConvFn(fn comdef.ToStringFunc) ConvOptionFn { return func(opt *ConvOption) { @@ -155,7 +161,7 @@ func ToStringWith(in any, optFns ...ConvOptionFn) (str string, err error) { case error: str = value.Error() default: - if opt.EnablePtr { + if opt.HandlePtr { if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { rv = rv.Elem() if checkfn.IsSimpleKind(rv.Kind()) { diff --git a/mathutil/convert.go b/mathutil/convert.go index 2d245b41e..0369519f2 100644 --- a/mathutil/convert.go +++ b/mathutil/convert.go @@ -3,11 +3,13 @@ package mathutil import ( "fmt" "math" + "reflect" "strconv" "strings" "time" "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/internal/checkfn" "github.com/gookit/goutil/internal/comfunc" ) @@ -34,6 +36,10 @@ type ConvOption[T any] struct { // if ture: value is nil, will return convert error; // if false(default): value is nil, will convert to zero value NilAsFail bool + // HandlePtr auto convert ptr type(int,float,string) value. eg: *int to int + // - if true: will use real type try convert. default is false + // - NOTE: current T type's ptr is default support. + HandlePtr bool // set custom fallback convert func for not supported type. UserConvFn ToTypeFunc[T] } @@ -66,6 +72,11 @@ func WithNilAsFail[T any](opt *ConvOption[T]) { opt.NilAsFail = true } +// WithHandlePtr set ConvOption.HandlePtr option +func WithHandlePtr[T any](opt *ConvOption[T]) { + opt.HandlePtr = true +} + // WithUserConvFn set ConvOption.UserConvFn option func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T] { return func(opt *ConvOption[T]) { @@ -134,6 +145,8 @@ func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error) { switch tVal := in.(type) { case int: iVal = tVal + case *int: // default support int ptr type + iVal = *tVal case int8: iVal = int(tVal) case int16: @@ -190,6 +203,15 @@ func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error) { } } default: + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToIntWith(rv.Interface(), optFns...) + } + } + } + if opt.UserConvFn != nil { return opt.UserConvFn(in) } @@ -276,6 +298,8 @@ func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error) { i64 = int64(tVal) case int64: i64 = tVal + case *int64: // default support int64 ptr type + i64 = *tVal case uint: i64 = int64(tVal) case uint8: @@ -295,6 +319,15 @@ func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error) { case comdef.Int64able: // eg: json.Number i64, err = tVal.Int64() default: + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToInt64With(rv.Interface(), optFns...) + } + } + } + if opt.UserConvFn != nil { i64, err = opt.UserConvFn(in) } else { @@ -367,6 +400,8 @@ func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error) { uVal = uint(tVal) case uint: uVal = tVal + case *uint: // default support uint ptr type + uVal = *tVal case uint8: uVal = uint(tVal) case uint16: @@ -390,6 +425,15 @@ func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error) { u64, err = strconv.ParseUint(strings.TrimSpace(tVal), 10, 0) uVal = uint(u64) default: + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToUintWith(rv.Interface(), optFns...) + } + } + } + if opt.UserConvFn != nil { uVal, err = opt.UserConvFn(in) } else { @@ -470,6 +514,8 @@ func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error u64 = uint64(tVal) case uint64: u64 = tVal + case *uint64: // default support uint64 ptr type + u64 = *tVal case float32: u64 = uint64(tVal) case float64: @@ -483,6 +529,15 @@ func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error case string: u64, err = strconv.ParseUint(strings.TrimSpace(tVal), 10, 0) default: + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToUint64With(rv.Interface(), optFns...) + } + } + } + if opt.UserConvFn != nil { u64, err = opt.UserConvFn(in) } else { @@ -572,11 +627,22 @@ func ToFloatWith(in any, optFns ...ConvOptionFn[float64]) (f64 float64, err erro f64 = float64(tVal) case float64: f64 = tVal + case *float64: // default support float64 ptr type + f64 = *tVal case time.Duration: f64 = float64(tVal) case comdef.Float64able: // eg: json.Number f64, err = tVal.Float64() default: + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToFloatWith(rv.Interface(), optFns...) + } + } + } + if opt.UserConvFn != nil { f64, err = opt.UserConvFn(in) } else { diff --git a/mathutil/convert_test.go b/mathutil/convert_test.go index 0f3453df1..691bab01e 100644 --- a/mathutil/convert_test.go +++ b/mathutil/convert_test.go @@ -98,6 +98,104 @@ func TestWithNilAsFail(t *testing.T) { is.Err(err) } +func TestWithHandlePtr(t *testing.T) { + var err error + is := assert.New(t) + + iv1 := 2 + i641 := int64(2) + + // int + t.Run("to int", func(t *testing.T) { + var iv int + iv, err = mathutil.ToIntWith(&iv1) + is.NoErr(err) + is.Eq(2, iv) + + _, err = mathutil.ToIntWith(&i641) + is.Err(err) + iv, err = mathutil.ToIntWith(&i641, mathutil.WithHandlePtr[int]) + is.NoErr(err) + is.Eq(2, iv) + }) + + // int64 + t.Run("to int64", func(t *testing.T) { + var i64 int64 + i64, err = mathutil.ToInt64With(&i641) + is.NoErr(err) + is.Eq(int64(2), i64) + + _, err = mathutil.ToInt64With(&iv1) + is.Err(err) + i64, err = mathutil.ToInt64With(&iv1, mathutil.WithHandlePtr[int64]) + is.NoErr(err) + is.Eq(int64(2), i64) + }) + + // uint + t.Run("to uint", func(t *testing.T) { + var u uint + u1 := uint(2) + u, err = mathutil.ToUintWith(&u1) + is.NoErr(err) + is.Eq(uint(2), u) + + _, err = mathutil.ToUintWith(&iv1) + is.Err(err) + u, err = mathutil.ToUintWith(&iv1, mathutil.WithHandlePtr[uint]) + is.NoErr(err) + is.Eq(uint(2), u) + }) + + // uint64 + t.Run("to uint64", func(t *testing.T) { + var u64 uint64 + u641 := uint64(2) + u64, err = mathutil.ToUint64With(&u641) + is.NoErr(err) + is.Eq(uint64(2), u64) + + _, err = mathutil.ToUint64With(&iv1) + is.Err(err) + u64, err = mathutil.ToUint64With(&iv1, mathutil.WithHandlePtr[uint64]) + is.NoErr(err) + is.Eq(uint64(2), u64) + }) + + // float + t.Run("to float", func(t *testing.T) { + var f float64 + f1 := float64(2) + f, err = mathutil.ToFloatWith(&f1) + is.NoErr(err) + is.Eq(float64(2), f) + + _, err = mathutil.ToFloatWith(&iv1) + is.Err(err) + f, err = mathutil.ToFloatWith(&iv1, mathutil.WithHandlePtr[float64]) + is.NoErr(err) + is.Eq(float64(2), f) + }) + + // string + t.Run("to string", func(t *testing.T) { + var s string + s1 := "2" + s, err = mathutil.ToStringWith(&s1) + is.NoErr(err) + is.Eq("2", s) + + _, err = mathutil.ToStringWith(&iv1) + is.Err(err) + s, err = mathutil.ToStringWith(&iv1, func(opt *comfunc.ConvOption) { + opt.HandlePtr = true + }) + is.NoErr(err) + is.Eq("2", s) + }) +} + func TestToInt(t *testing.T) { is := assert.New(t)