Skip to content

Commit

Permalink
add int64
Browse files Browse the repository at this point in the history
  • Loading branch information
shockerli committed Mar 9, 2021
1 parent 68df522 commit e6f13d5
Show file tree
Hide file tree
Showing 6 changed files with 350 additions and 53 deletions.
13 changes: 13 additions & 0 deletions cvt.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,16 @@ func Uint(v interface{}, def ...uint) uint {

return 0
}

// Int64 convert an interface to a int64 type, with default value
func Int64(v interface{}, def ...int64) int64 {
if v, err := Int64E(v); err == nil {
return v
}

if len(def) > 0 {
return def[0]
}

return 0
}
93 changes: 93 additions & 0 deletions cvt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,3 +586,96 @@ func TestUint_BaseLine(t *testing.T) {
assert.Equal(t, tt.expect, v, msg)
}
}

func TestInt64_HasDefault(t *testing.T) {
tests := []struct {
input interface{}
def int64
expect int64
}{
// supported value, def is not used, def != expect
{int(8), 1, 8},
{int8(8), 1, 8},
{int16(8), 1, 8},
{int32(8), 1, 8},
{int64(8), 1, 8},
{uint(8), 1, 8},
{uint8(8), 1, 8},
{uint16(8), 1, 8},
{uint32(8), 1, 8},
{uint64(8), 1, 8},
{float32(8.31), 1, 8},
{float64(8.31), 1, 8},
{"8", 2, 8},
{"8.00", 2, 8},
{"8.01", 2, 8},
{int(-8), 1, -8},
{int8(-8), 1, -8},
{int16(-8), 1, -8},
{int32(-8), 1, -8},
{int64(-8), 1, -8},
{float32(-8.31), 1, -8},
{float64(-8.31), 1, -8},
{"-8", 1, -8},
{"-8.01", 1, -8},
{true, 2, 1},
{false, 2, 0},
{nil, 2, 0},
{aliasTypeInt_0, 2, 0},
{&aliasTypeInt_0, 2, 0},
{aliasTypeInt_1, 2, 1},
{&aliasTypeInt_1, 2, 1},
{aliasTypeString_0, 2, 0},
{&aliasTypeString_0, 2, 0},
{aliasTypeString_1, 2, 1},
{&aliasTypeString_1, 2, 1},
{aliasTypeString_8d15, 2, 8},
{&aliasTypeString_8d15, 2, 8},
{aliasTypeString_8d15_minus, 1, -8},
{&aliasTypeString_8d15_minus, 1, -8},

// unsupported value, def == expect
{"10a", 1, 1},
{"a10a", 1, 1},
{"8.01a", 1, 1},
{"8.01 ", 1, 1},
{"hello", 1, 1},
{testing.T{}, 1, 1},
{&testing.T{}, 1, 1},
{[]int{}, 1, 1},
{[]string{}, 1, 1},
{[...]string{}, 1, 1},
{map[int]string{}, 1, 1},
}

for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect)

v := cvt.Int64(tt.input, tt.def)
assert.Equal(t, tt.expect, v, msg)
}
}

func TestInt64_BaseLine(t *testing.T) {
tests := []struct {
input interface{}
expect int64
}{
{testing.T{}, 0},
{&testing.T{}, 0},
{[]int{}, 0},
{[]int{1, 2, 3}, 0},
{[]string{}, 0},
{[]string{"a", "b", "c"}, 0},
{[...]string{}, 0},
{map[int]string{}, 0},
{"4873546382743564386435354655456575456754356765546554643456", 0},
}

for i, tt := range tests {
msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect)

v := cvt.Int64(tt.input)
assert.Equal(t, tt.expect, v, msg)
}
}
174 changes: 128 additions & 46 deletions cvte.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package cvt

import (
"errors"
"fmt"
"math"
"reflect"
"strconv"
"strings"
)

var convErr = errors.New("convert failed")
var formatOutOfLimit = "%w, out of max limit value(%d)"
var formatExtend = "%v, %w"

// BoolE convert an interface to a bool type
func BoolE(val interface{}) (bool, error) {
v, rk, rv := Indirect(val)
Expand Down Expand Up @@ -35,7 +40,7 @@ func BoolE(val interface{}) (bool, error) {
return rv.Len() > 0, nil
}

return false, err(val, "bool")
return false, newErr(val, "bool")
}

// returns the boolean value represented by the string
Expand All @@ -52,95 +57,162 @@ func str2bool(str string) (bool, error) {
case "off":
return false, nil
default:
return false, err(str, "bool")
return false, newErr(str, "bool")
}
}

// Uint64E convert an interface to a uint64 type
func Uint64E(val interface{}) (uint64, error) {
e := err(val, "uint64")
v, _, rv := Indirect(val)

switch vv := v.(type) {
case nil:
return 0, nil
case bool:
if vv {
return 1, nil
}
return 0, nil
case string:
vvv, err := strconv.ParseFloat(vv, 64)
if err == nil && vvv >= 0 && vvv <= math.MaxUint64 {
return uint64(math.Floor(vvv)), nil
}
case uint, uint8, uint16, uint32, uint64:
return rv.Uint(), nil
case int, int8, int16, int32, int64:
if rv.Int() >= 0 {
return uint64(rv.Int()), nil
}
case float32, float64:
if rv.Float() >= 0 && rv.Float() <= math.MaxUint64 {
return uint64(math.Floor(rv.Float())), nil
}
v, e := convUint64(val)
if e := catch("uint64", val, e); e != nil {
return 0, e
}
if v > math.MaxUint64 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint64"), uint64(math.MaxUint64))
}

return 0, e
return v, nil
}

// Uint32E convert an interface to a uint32 type
func Uint32E(val interface{}) (uint32, error) {
v, e := Uint64E(val)
if e != nil {
return 0, err(val, "uint32")
v, e := convUint64(val)
if e := catch("uint32", val, e); e != nil {
return 0, e
}
if v > math.MaxUint32 {
return 0, fmt.Errorf("%w, out of max limit value(%d)", err(val, "uint32"), math.MaxUint32)
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint32"), uint32(math.MaxUint32))
}

return uint32(v), nil
}

// Uint16E convert an interface to a uint16 type
func Uint16E(val interface{}) (uint16, error) {
v, e := Uint64E(val)
if e != nil {
return 0, err(val, "uint16")
v, e := convUint64(val)
if e := catch("uint16", val, e); e != nil {
return 0, e
}
if v > math.MaxUint16 {
return 0, fmt.Errorf("%w, out of max limit value(%d)", err(val, "uint16"), math.MaxUint16)
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint16"), uint16(math.MaxUint16))
}

return uint16(v), nil
}

// Uint8E convert an interface to a uint8 type
func Uint8E(val interface{}) (uint8, error) {
v, e := Uint64E(val)
if e != nil {
return 0, err(val, "uint8")
v, e := convUint64(val)
if e := catch("uint8", val, e); e != nil {
return 0, e
}
if v > math.MaxUint8 {
return 0, fmt.Errorf("%w, out of max limit value(%d)", err(val, "uint8"), math.MaxUint8)
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint8"), uint8(math.MaxUint8))
}

return uint8(v), nil
}

// UintE convert an interface to a uint type
func UintE(val interface{}) (uint, error) {
v, e := Uint64E(val)
if e != nil {
return 0, err(val, "uint")
v, e := convUint64(val)
if e := catch("uint", val, e); e != nil {
return 0, e
}
if v > uint64(^uint(0)) {
return 0, fmt.Errorf("%w, out of max limit value(%d)", err(val, "uint"), ^uint(0))
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "uint"), ^uint(0))
}

return uint(v), nil
}

func convUint64(val interface{}) (uint64, error) {
v, _, rv := Indirect(val)

switch vv := v.(type) {
case nil:
return 0, nil
case bool:
if vv {
return 1, nil
}
return 0, nil
case string:
vvv, err := strconv.ParseFloat(vv, 64)
if err == nil && vvv >= 0 && vvv <= math.MaxUint64 {
return uint64(math.Trunc(vvv)), nil
}
case []byte:
vvv, err := strconv.ParseFloat(string(vv), 64)
if err == nil && vvv >= 0 && vvv <= math.MaxUint64 {
return uint64(math.Trunc(vvv)), nil
}
case uint, uint8, uint16, uint32, uint64:
return rv.Uint(), nil
case int, int8, int16, int32, int64:
if rv.Int() >= 0 {
return uint64(rv.Int()), nil
}
case float32, float64:
if rv.Float() >= 0 && rv.Float() <= math.MaxUint64 {
return uint64(math.Trunc(rv.Float())), nil
}
}

return 0, convErr
}

// Int64E convert an interface to a int64 type
func Int64E(val interface{}) (int64, error) {
v, e := convInt64(val)
if e := catch("int64", val, e); e != nil {
return 0, e
}
if strconv.IntSize == 64 && v > math.MaxInt64 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "int64"), math.MaxInt64)
} else if strconv.IntSize == 32 && v > math.MaxInt32 {
return 0, fmt.Errorf(formatOutOfLimit, newErr(val, "int64"), math.MaxInt32)
}

return v, nil
}

func convInt64(val interface{}) (int64, error) {
v, _, rv := Indirect(val)

switch vv := v.(type) {
case nil:
return 0, nil
case bool:
if vv {
return 1, nil
}
return 0, nil
case string:
vvv, err := strconv.ParseFloat(vv, 64)
if err == nil && vvv <= math.MaxInt64 {
return int64(math.Trunc(vvv)), nil
}
case []byte:
vvv, err := strconv.ParseFloat(string(vv), 64)
if err == nil && vvv <= math.MaxInt64 {
return int64(math.Trunc(vvv)), nil
}
case uint, uint8, uint16, uint32, uint64, uintptr:
if rv.Uint() <= math.MaxInt64 {
return int64(rv.Uint()), nil
}
case int, int8, int16, int32, int64:
return rv.Int(), nil
case float32, float64:
if rv.Float() <= math.MaxInt64 {
return int64(math.Trunc(rv.Float())), nil
}
}

return 0, convErr
}

// Indirect returns the value with base type
func Indirect(a interface{}) (val interface{}, k reflect.Kind, v reflect.Value) {
if a == nil {
Expand Down Expand Up @@ -198,6 +270,16 @@ func Indirect(a interface{}) (val interface{}, k reflect.Kind, v reflect.Value)
return
}

func err(val interface{}, t string) error {
func newErr(val interface{}, t string) error {
return fmt.Errorf("unable to convert %#v of type %T to %s", val, val, t)
}

func catch(t string, val interface{}, e error) error {
if e != nil {
if errors.Is(e, convErr) {
return newErr(val, t)
}
return fmt.Errorf(formatExtend, newErr(val, t), e)
}
return nil
}
Loading

0 comments on commit e6f13d5

Please sign in to comment.