package mathutil import ( "fmt" "math" "reflect" "strconv" "strings" "time" "github.com/gookit/goutil/comdef" "github.com/gookit/goutil/internal/checkfn" ) /************************************************************* * region convert to int *************************************************************/ // Int convert value to int func Int(in any) (int, error) { return ToInt(in) } // SafeInt convert value to int, will ignore error func SafeInt(in any) int { val, _ := ToInt(in) return val } // QuietInt convert value to int, will ignore error func QuietInt(in any) int { return SafeInt(in) } // IntOrPanic convert value to int, will panic on error func IntOrPanic(in any) int { val, err := ToInt(in) if err != nil { panic(err) } return val } // MustInt convert value to int, will panic on error func MustInt(in any) int { return IntOrPanic(in) } // IntOrDefault convert value to int, return defaultVal on failed func IntOrDefault(in any, defVal int) int { return IntOr(in, defVal) } // IntOr convert value to int, return defaultVal on failed func IntOr(in any, defVal int) int { val, err := ToIntWith(in) if err != nil { return defVal } return val } // IntOrErr convert value to int, return error on failed func IntOrErr(in any) (int, error) { return ToIntWith(in) } // ToInt convert value to int, return error on failed func ToInt(in any) (int, error) { return ToIntWith(in) } // ToIntWith convert value to int, can with some option func. // // Example: // // ToIntWithFunc(val, mathutil.WithNilAsFail, mathutil.WithUserConvFn(func(in any) (int, error) { // }) func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error) { if len(optFns) == 0 && in == nil { return 0, nil } var opt *ConvOption[int] if len(optFns) > 0 { opt = NewConvOption(optFns...) if in == nil && opt.NilAsFail { err = comdef.ErrConvType return } } if tVal, ok := in.(string); ok { // in strict mode, cannot convert string to int if opt != nil && opt.StrictMode { err = comdef.ErrConvType return } // try convert to int iVal, err = TryStrInt(tVal) return } switch tVal := in.(type) { case int: iVal = tVal case *int: // default support int ptr type iVal = *tVal case int8: iVal = int(tVal) case int16: iVal = int(tVal) case int32: iVal = int(tVal) case int64: if tVal > math.MaxInt32 { err = fmt.Errorf("value overflow int32. input: %v", tVal) } else { iVal = int(tVal) } case uint: if tVal > math.MaxInt32 { err = fmt.Errorf("value overflow int32. input: %v", tVal) } else { iVal = int(tVal) } case uint8: iVal = int(tVal) case uint16: iVal = int(tVal) case uint32: if tVal > math.MaxInt32 { err = fmt.Errorf("value overflow int32. input: %v", tVal) } else { iVal = int(tVal) } case uint64: if tVal > math.MaxInt32 { err = fmt.Errorf("value overflow int32. input: %v", tVal) } else { iVal = int(tVal) } case float32: iVal = int(tVal) case float64: iVal = int(tVal) case time.Duration: if tVal > math.MaxInt32 { err = fmt.Errorf("value overflow int32. input: %v", tVal) } else { iVal = int(tVal) } case comdef.Int64able: // eg: json.Number var i64 int64 if i64, err = tVal.Int64(); err == nil { if i64 > math.MaxInt32 { err = fmt.Errorf("value overflow int32. input: %v", tVal) } else { iVal = int(i64) } } default: if opt == nil { err = comdef.ErrConvType return } if opt.UserConvFn != nil { iVal, err = opt.UserConvFn(in) } else 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...) } } } else { err = comdef.ErrConvType } } return } /************************************************************* * region convert to int64 *************************************************************/ // Int64 convert value to int64, return error on failed func Int64(in any) (int64, error) { return ToInt64(in) } // SafeInt64 convert value to int64, will ignore error func SafeInt64(in any) int64 { i64, _ := ToInt64With(in) return i64 } // QuietInt64 convert value to int64, will ignore error func QuietInt64(in any) int64 { return SafeInt64(in) } // MustInt64 convert value to int64, will panic on error func MustInt64(in any) int64 { i64, err := ToInt64With(in) if err != nil { panic(err) } return i64 } // Int64OrDefault convert value to int64, return default val on failed func Int64OrDefault(in any, defVal int64) int64 { return Int64Or(in, defVal) } // Int64Or convert value to int64, return default val on failed func Int64Or(in any, defVal int64) int64 { i64, err := ToInt64With(in) if err != nil { return defVal } return i64 } // ToInt64 convert value to int64, return error on failed func ToInt64(in any) (int64, error) { return ToInt64With(in) } // Int64OrErr convert value to int64, return error on failed func Int64OrErr(in any) (int64, error) { return ToInt64With(in) } // ToInt64With try to convert value to int64. can with some option func, more see ConvOption. func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error) { if len(optFns) == 0 && in == nil { return 0, nil } var opt *ConvOption[int64] if len(optFns) > 0 { opt = NewConvOption(optFns...) if in == nil && opt.NilAsFail { err = comdef.ErrConvType return } } if tVal, ok := in.(string); ok { // in strict mode, cannot convert string to int if opt != nil && opt.StrictMode { err = comdef.ErrConvType return } // try convert to int64 i64, err = TryStrInt64(tVal) return } switch tVal := in.(type) { case int: i64 = int64(tVal) case int8: i64 = int64(tVal) case int16: i64 = int64(tVal) case int32: i64 = int64(tVal) case int64: i64 = tVal case *int64: // default support int64 ptr type i64 = *tVal case uint: i64 = int64(tVal) case uint8: i64 = int64(tVal) case uint16: i64 = int64(tVal) case uint32: i64 = int64(tVal) case uint64: i64 = int64(tVal) case float32: // in strict mode, cannot convert float to int if opt != nil && opt.StrictMode { err = comdef.ErrConvType return } i64 = int64(tVal) case float64: // in strict mode, cannot convert float to int if opt != nil && opt.StrictMode { err = comdef.ErrConvType return } if tVal > math.MaxInt64 { err = comdef.ErrConvType return } i64 = int64(tVal) case time.Duration: i64 = int64(tVal) case comdef.Int64able: // eg: json.Number i64, err = tVal.Int64() default: if opt == nil { err = comdef.ErrConvType return } if opt.UserConvFn != nil { i64, err = opt.UserConvFn(in) } else 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...) } } } else { err = comdef.ErrConvType } } return } /************************************************************* * region convert to uint *************************************************************/ // Uint convert any to uint, return error on failed func Uint(in any) (uint, error) { return ToUint(in) } // SafeUint convert any to uint, will ignore error func SafeUint(in any) uint { val, _ := ToUint(in) return val } // QuietUint convert any to uint, will ignore error func QuietUint(in any) uint { return SafeUint(in) } // MustUint convert any to uint, will panic on error func MustUint(in any) uint { val, err := ToUintWith(in) if err != nil { panic(err) } return val } // UintOrDefault convert any to uint, return default val on failed func UintOrDefault(in any, defVal uint) uint { return UintOr(in, defVal) } // UintOr convert any to uint, return default val on failed func UintOr(in any, defVal uint) uint { val, err := ToUintWith(in) if err != nil { return defVal } return val } // UintOrErr convert value to uint, return error on failed func UintOrErr(in any) (uint, error) { return ToUintWith(in) } // ToUint convert value to uint, return error on failed func ToUint(in any) (u64 uint, err error) { return ToUintWith(in) } // ToUintWith try to convert value to uint. can with some option func, more see ConvOption. func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error) { if len(optFns) == 0 && in == nil { return 0, nil } var opt *ConvOption[uint] if len(optFns) > 0 { opt = NewConvOption(optFns...) if in == nil && opt.NilAsFail { err = comdef.ErrConvType return } } if tVal, ok := in.(string); ok { // in strict mode, cannot convert string to int if opt != nil && opt.StrictMode { err = comdef.ErrConvType return } // try convert to uint64 var u64 uint64 if u64, err = TryStrUint64(tVal); err == nil { uVal = uint(u64) } return } switch tVal := in.(type) { case int: uVal = uint(tVal) case int8: uVal = uint(tVal) case int16: uVal = uint(tVal) case int32: uVal = uint(tVal) case int64: uVal = uint(tVal) case uint: uVal = tVal case *uint: // default support uint ptr type uVal = *tVal case uint8: uVal = uint(tVal) case uint16: uVal = uint(tVal) case uint32: uVal = uint(tVal) case uint64: uVal = uint(tVal) case float32: uVal = uint(tVal) case float64: uVal = uint(tVal) case time.Duration: uVal = uint(tVal) case comdef.Int64able: // eg: json.Number var i64 int64 i64, err = tVal.Int64() uVal = uint(i64) default: if opt == nil { err = comdef.ErrConvType return } if opt.UserConvFn != nil { uVal, err = opt.UserConvFn(in) } else 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...) } } } else { err = comdef.ErrConvType } } return } /************************************************************* * region convert to uint64 *************************************************************/ // Uint64 convert any to uint64, return error on failed func Uint64(in any) (uint64, error) { return ToUint64(in) } // QuietUint64 convert any to uint64, will ignore error func QuietUint64(in any) uint64 { return SafeUint64(in) } // SafeUint64 convert any to uint64, will ignore error func SafeUint64(in any) uint64 { val, _ := ToUint64(in) return val } // MustUint64 convert any to uint64, will panic on error func MustUint64(in any) uint64 { val, err := ToUint64With(in) if err != nil { panic(err) } return val } // Uint64OrDefault convert any to uint64, return default val on failed func Uint64OrDefault(in any, defVal uint64) uint64 { return Uint64Or(in, defVal) } // Uint64Or convert any to uint64, return default val on failed func Uint64Or(in any, defVal uint64) uint64 { val, err := ToUint64With(in) if err != nil { return defVal } return val } // Uint64OrErr convert value to uint64, return error on failed func Uint64OrErr(in any) (uint64, error) { return ToUint64With(in) } // ToUint64 convert value to uint64, return error on failed func ToUint64(in any) (uint64, error) { return ToUint64With(in) } // ToUint64With try to convert value to uint64. can with some option func, more see ConvOption. func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error) { if len(optFns) == 0 && in == nil { return 0, nil } var opt *ConvOption[uint64] if len(optFns) > 0 { opt = NewConvOption(optFns...) if in == nil && opt.NilAsFail { err = comdef.ErrConvType return } } if tVal, ok := in.(string); ok { // in strict mode, cannot convert string to int if opt != nil && opt.StrictMode { err = comdef.ErrConvType return } // try convert to uint64 u64, err = TryStrUint64(tVal) return } switch tVal := in.(type) { case int: u64 = uint64(tVal) case int8: u64 = uint64(tVal) case int16: u64 = uint64(tVal) case int32: u64 = uint64(tVal) case int64: u64 = uint64(tVal) case uint: u64 = uint64(tVal) case uint8: u64 = uint64(tVal) case uint16: u64 = uint64(tVal) case uint32: u64 = uint64(tVal) case uint64: u64 = tVal case *uint64: // default support uint64 ptr type u64 = *tVal case float32: u64 = uint64(tVal) case float64: u64 = uint64(tVal) case time.Duration: u64 = uint64(tVal) case comdef.Int64able: // eg: json.Number var i64 int64 i64, err = tVal.Int64() u64 = uint64(i64) default: if opt == nil { err = comdef.ErrConvType return } if opt.UserConvFn != nil { u64, err = opt.UserConvFn(in) } else 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...) } } } else { err = comdef.ErrConvType } } return } /************************************************************* * region string to intX/uintX *************************************************************/ // StrInt convert string to int, ignore error func StrInt(s string) int { iVal, _ := strconv.Atoi(strings.TrimSpace(s)) return iVal } // StrIntOr convert string to int, return default val on failed func StrIntOr(s string, defVal int) int { iVal, err := strconv.Atoi(strings.TrimSpace(s)) if err != nil { return defVal } return iVal } // TryStrInt convert string to int, return error on failed. // // - empty string will return 0. // - allow float string. func TryStrInt(s string) (int, error) { s = strings.TrimSpace(s) if s == "" { return 0, nil } // try convert to int iVal, err := strconv.Atoi(s) // handle the case where the string might be a float if err != nil && checkfn.IsNumeric(s) { var floatVal float64 if floatVal, err = strconv.ParseFloat(s, 64); err == nil { iVal = int(math.Round(floatVal)) err = nil } } return iVal, err } // TryStrInt64 convert string to int64, return error on failed. // // - empty string will return 0. // - allow float string. func TryStrInt64(s string) (int64, error) { s = strings.TrimSpace(s) if s == "" { return 0, nil } i64, err := strconv.ParseInt(s, 10, 0) // handle the case where the string might be a float if err != nil && checkfn.IsNumeric(s) { var floatVal float64 if floatVal, err = strconv.ParseFloat(s, 64); err == nil { i64 = int64(math.Round(floatVal)) err = nil } } return i64, err } // TryStrUint64 try to convert string to uint64, return error on failed // // - empty string will return 0. // - allow float string. func TryStrUint64(s string) (uint64, error) { s = strings.TrimSpace(s) if s == "" { return 0, nil } // try convert to int64 u64, err := strconv.ParseUint(s, 10, 0) // handle the case where the string might be a float if err != nil && checkfn.IsPositiveNum(s) { var floatVal float64 if floatVal, err = strconv.ParseFloat(s, 64); err == nil { u64 = uint64(math.Round(floatVal)) err = nil } } return u64, err }