From 53e4fc10b21e738d43c24cd9caf182f07cdb18ef Mon Sep 17 00:00:00 2001 From: shockerli Date: Thu, 22 Jul 2021 19:53:11 +0800 Subject: [PATCH] reorganize code structure --- README.md | 5 +- README_ZH.md | 5 +- bool.go | 68 ++ bool_test.go | 209 +++++ cvt.go | 211 ----- cvte.go | 592 +------------ cvte_test.go | 1682 ------------------------------------ float.go | 86 ++ float_test.go | 378 ++++++++ go.mod | 2 +- int.go | 335 +++++++ cvt_test.go => int_test.go | 1118 +++++++++++++++++------- slice.go | 175 ++++ slice_test.go | 398 +++++++++ string.go | 62 ++ string_test.go | 159 ++++ time.go | 86 ++ time_test.go | 125 +++ 18 files changed, 2886 insertions(+), 2810 deletions(-) create mode 100644 bool.go create mode 100644 bool_test.go delete mode 100644 cvt.go create mode 100644 float.go create mode 100644 float_test.go create mode 100644 int.go rename cvt_test.go => int_test.go (50%) create mode 100644 slice.go create mode 100644 slice_test.go create mode 100644 string.go create mode 100644 string_test.go create mode 100644 time.go create mode 100644 time_test.go diff --git a/README.md b/README.md index 48ab068..c5a2d82 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # cvt -[![PkgGoDev](https://pkg.go.dev/badge/github.com/shockerli/cvt)](https://pkg.go.dev/github.com/shockerli/cvt) [![Go Report Card](https://goreportcard.com/badge/github.com/shockerli/cvt)](https://goreportcard.com/report/github.com/shockerli/cvt) [![Build Status](https://travis-ci.com/shockerli/cvt.svg?branch=master)](https://travis-ci.com/shockerli/cvt) ![GitHub top language](https://img.shields.io/github/languages/top/shockerli/cvt) ![GitHub](https://img.shields.io/github/license/shockerli/cvt) [![Sourcegraph](https://sourcegraph.com/github.com/shockerli/cvt/-/badge.svg)](https://sourcegraph.com/github.com/shockerli/cvt?badge) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/shockerli/cvt)](https://pkg.go.dev/github.com/shockerli/cvt) [![Go Report Card](https://goreportcard.com/badge/github.com/shockerli/cvt)](https://goreportcard.com/report/github.com/shockerli/cvt) [![Build Status](https://travis-ci.com/shockerli/cvt.svg?branch=master)](https://travis-ci.com/shockerli/cvt) ![GitHub top language](https://img.shields.io/github/languages/top/shockerli/cvt) ![GitHub](https://img.shields.io/github/license/shockerli/cvt) > Simple, safe conversion of any type, including custom types. > @@ -60,7 +60,7 @@ cvt.Float("hello", 12.34) // 12.34 ### more -> For more examples, see tests [cvt_test.go](cvt_test.go) and [cvte_test.go](cvte_test.go) +> For more examples, see `*_test.go` ## API @@ -103,7 +103,6 @@ cvt.Float("hello", 12.34) // 12.34 - Uint8E - UintE - ## License This project is licensed under the terms of the [MIT](LICENSE) license. diff --git a/README_ZH.md b/README_ZH.md index be731e2..55a5067 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -1,6 +1,6 @@ # cvt -[![PkgGoDev](https://pkg.go.dev/badge/github.com/shockerli/cvt)](https://pkg.go.dev/github.com/shockerli/cvt) [![Go Report Card](https://goreportcard.com/badge/github.com/shockerli/cvt)](https://goreportcard.com/report/github.com/shockerli/cvt) [![Build Status](https://travis-ci.com/shockerli/cvt.svg?branch=master)](https://travis-ci.com/shockerli/cvt) ![GitHub top language](https://img.shields.io/github/languages/top/shockerli/cvt) ![GitHub](https://img.shields.io/github/license/shockerli/cvt) [![Sourcegraph](https://sourcegraph.com/github.com/shockerli/cvt/-/badge.svg)](https://sourcegraph.com/github.com/shockerli/cvt?badge) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/shockerli/cvt)](https://pkg.go.dev/github.com/shockerli/cvt) [![Go Report Card](https://goreportcard.com/badge/github.com/shockerli/cvt)](https://goreportcard.com/report/github.com/shockerli/cvt) [![Build Status](https://travis-ci.com/shockerli/cvt.svg?branch=master)](https://travis-ci.com/shockerli/cvt) ![GitHub top language](https://img.shields.io/github/languages/top/shockerli/cvt) ![GitHub](https://img.shields.io/github/license/shockerli/cvt) > 简单、安全的转换任意类型值,包括自定义类型 @@ -58,7 +58,7 @@ cvt.Float("hello", 12.34) // 12.34 ### more -> 所有示例,可通过单元测试了解:[cvt_test.go](cvt_test.go)、 [cvte_test.go](cvte_test.go) +> 所有示例,可通过单元测试了解:`*_test.go` ## API @@ -102,7 +102,6 @@ cvt.Float("hello", 12.34) // 12.34 - Uint8E - UintE - ## 开源协议 本项目基于 [MIT](LICENSE) 协议开放源代码。 diff --git a/bool.go b/bool.go new file mode 100644 index 0000000..2d54558 --- /dev/null +++ b/bool.go @@ -0,0 +1,68 @@ +package cvt + +import ( + "reflect" + "strconv" + "strings" +) + +// Bool convert an interface to a bool type, with default value +func Bool(v interface{}, def ...bool) bool { + if v, err := BoolE(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return false +} + +// BoolE convert an interface to a bool type +func BoolE(val interface{}) (bool, error) { + v, rk, rv := indirect(val) + + switch vv := v.(type) { + case bool: + return vv, nil + case int, int8, int16, int32, int64: + return rv.Int() != 0, nil + case uint, uint8, uint16, uint32, uint64: + return rv.Uint() != 0, nil + case float32, float64: + return rv.Float() != 0, nil + case []byte: + return str2bool(string(vv)) + case string: + return str2bool(vv) + case nil: + return false, nil + } + + switch rk.Kind() { + // by elem length + case reflect.Array, reflect.Slice, reflect.Map: + return rv.Len() > 0, nil + } + + return false, newErr(val, "bool") +} + +// returns the boolean value represented by the string +func str2bool(str string) (bool, error) { + if val, err := strconv.ParseBool(str); err == nil { + return val, nil + } else if val, err := strconv.ParseFloat(str, 64); err == nil { + return val != 0, nil + } + + switch strings.ToLower(strings.TrimSpace(str)) { + case "on": + return true, nil + case "off": + return false, nil + default: + return false, newErr(str, "bool") + } +} diff --git a/bool_test.go b/bool_test.go new file mode 100644 index 0000000..3ab46c0 --- /dev/null +++ b/bool_test.go @@ -0,0 +1,209 @@ +package cvt_test + +import ( + "fmt" + "testing" + + "github.com/shockerli/cvt" + "github.com/stretchr/testify/assert" +) + +func TestBool_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def bool + expect bool + }{ + // supported value, def is not used, def != expect + {0, true, false}, + {float64(0.00), true, false}, + {int(0.00), true, false}, + {int64(0.00), true, false}, + {uint(0.00), true, false}, + {uint64(0.00), true, false}, + {uint8(0.00), true, false}, + {nil, true, false}, + {"false", true, false}, + {"FALSE", true, false}, + {"False", true, false}, + {"f", true, false}, + {"F", true, false}, + {false, true, false}, + {"off", true, false}, + {"Off", true, false}, + {[]byte("Off"), true, false}, + {aliasTypeInt_0, true, false}, + {&aliasTypeInt_0, true, false}, + {aliasTypeString_0, true, false}, + {&aliasTypeString_0, true, false}, + {aliasTypeString_off, true, false}, + {&aliasTypeString_off, true, false}, + + {[]int{}, true, false}, + {[]string{}, true, false}, + {[...]string{}, true, false}, + {map[int]int{}, true, false}, + {map[string]string{}, true, false}, + + {"true", false, true}, + {"TRUE", false, true}, + {"True", false, true}, + {"t", false, true}, + {"T", false, true}, + {1, false, true}, + {true, false, true}, + {-1, false, true}, + {"on", false, true}, + {"On", false, true}, + {0.01, false, true}, + {aliasTypeInt_1, false, true}, + {&aliasTypeInt_1, false, true}, + {aliasTypeString_1, false, true}, + {&aliasTypeString_1, false, true}, + {aliasTypeString_on, false, true}, + {&aliasTypeString_on, false, true}, + + {[]int{1, 2, 3}, false, true}, + {[]string{"a", "b", "c"}, false, true}, + {[...]string{"a", "b", "c"}, false, true}, + {map[int]int{1: 111, 2: 222}, false, true}, + {map[string]string{"a": "aaa"}, false, true}, + + // unsupported value, def == expect + {"hello", true, true}, + {"hello", false, false}, + {testing.T{}, true, true}, + {testing.T{}, false, false}, + {&testing.T{}, true, true}, + {&testing.T{}, false, false}, + } + + 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.Bool(tt.input, tt.def) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestBool_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect bool + }{ + {testing.T{}, false}, + {&testing.T{}, false}, + {[]int{}, false}, + {[]int{1, 2, 3}, true}, + {[]string{}, false}, + {[]string{"a", "b", "c"}, true}, + {[...]string{}, false}, + {map[int]string{}, false}, + {aliasTypeString_8d15_minus, true}, + {&aliasTypeString_8d15_minus, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + + v := cvt.Bool(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestBoolE(t *testing.T) { + tests := []struct { + input interface{} + expect bool + isErr bool + }{ + // false/scale + {0, false, false}, + {float64(0.00), false, false}, + {int(0.00), false, false}, + {int64(0.00), false, false}, + {uint(0.00), false, false}, + {uint64(0.00), false, false}, + {uint8(0.00), false, false}, + {nil, false, false}, + {false, false, false}, + {"false", false, false}, + {"FALSE", false, false}, + {"False", false, false}, + {"f", false, false}, + {"F", false, false}, + {"off", false, false}, + {"Off", false, false}, + {"0", false, false}, + {"0.00", false, false}, + {[]byte("false"), false, false}, + {[]byte("Off"), false, false}, + {aliasTypeInt_0, false, false}, + {&aliasTypeInt_0, false, false}, + {aliasTypeString_0, false, false}, + {&aliasTypeString_0, false, false}, + {aliasTypeString_off, false, false}, + {&aliasTypeString_off, false, false}, + {aliasTypeBool_false, false, false}, + {&aliasTypeBool_false, false, false}, + + // false/slice/array/map + {[]int{}, false, false}, + {[]string{}, false, false}, + {[...]string{}, false, false}, + {map[int]int{}, false, false}, + {map[string]string{}, false, false}, + + // true/scale + {true, true, false}, + {"true", true, false}, + {"TRUE", true, false}, + {"True", true, false}, + {"t", true, false}, + {"T", true, false}, + {1, true, false}, + {-1, true, false}, + {"on", true, false}, + {"On", true, false}, + {0.01, true, false}, + {"0.01", true, false}, + {[]byte("true"), true, false}, + {aliasTypeInt_1, true, false}, + {&aliasTypeInt_1, true, false}, + {aliasTypeString_1, true, false}, + {&aliasTypeString_1, true, false}, + {aliasTypeString_on, true, false}, + {&aliasTypeString_on, true, false}, + {aliasTypeBool_true, true, false}, + {&aliasTypeBool_true, true, false}, + + // true/slice/array/map + {[]int{1, 2, 3}, true, false}, + {[]string{"a", "b", "c"}, true, false}, + {[...]string{"a", "b", "c"}, true, false}, + {map[int]int{1: 111, 2: 222}, true, false}, + {map[string]string{"a": "aaa"}, true, false}, + + // errors + {"hello", false, true}, + {testing.T{}, false, true}, + {&testing.T{}, false, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.BoolE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Bool(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} diff --git a/cvt.go b/cvt.go deleted file mode 100644 index e738a15..0000000 --- a/cvt.go +++ /dev/null @@ -1,211 +0,0 @@ -package cvt - -import "time" - -// Bool convert an interface to a bool type, with default value -func Bool(v interface{}, def ...bool) bool { - if v, err := BoolE(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return false -} - -// Uint64 convert an interface to a uint64 type, with default value -func Uint64(v interface{}, def ...uint64) uint64 { - if v, err := Uint64E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Uint32 convert an interface to a uint32 type, with default value -func Uint32(v interface{}, def ...uint32) uint32 { - if v, err := Uint32E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Uint16 convert an interface to a uint16 type, with default value -func Uint16(v interface{}, def ...uint16) uint16 { - if v, err := Uint16E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Uint8 convert an interface to a uint8 type, with default value -func Uint8(v interface{}, def ...uint8) uint8 { - if v, err := Uint8E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Uint convert an interface to a uint type, with default value -func Uint(v interface{}, def ...uint) uint { - if v, err := UintE(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - 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 -} - -// Int32 convert an interface to a int32 type, with default value -func Int32(v interface{}, def ...int32) int32 { - if v, err := Int32E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Int16 convert an interface to a int16 type, with default value -func Int16(v interface{}, def ...int16) int16 { - if v, err := Int16E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Int8 convert an interface to a int8 type, with default value -func Int8(v interface{}, def ...int8) int8 { - if v, err := Int8E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Int convert an interface to a int type, with default value -func Int(v interface{}, def ...int) int { - if v, err := IntE(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Float64 convert an interface to a float64 type, with default value -func Float64(v interface{}, def ...float64) float64 { - if v, err := Float64E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// Float32 convert an interface to a float32 type, with default value -func Float32(v interface{}, def ...float32) float32 { - if v, err := Float32E(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return 0 -} - -// String convert an interface to a string type, with default value -func String(v interface{}, def ...string) string { - if v, err := StringE(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return "" -} - -// Slice convert an interface to a []interface{} type, with default value -func Slice(v interface{}, def ...[]interface{}) []interface{} { - if v, err := SliceE(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return nil -} - -// Time convert an interface to a time.Time type, with default value -func Time(v interface{}, def ...time.Time) time.Time { - if v, err := TimeE(v); err == nil { - return v - } - - if len(def) > 0 { - return def[0] - } - - return time.Time{} -} diff --git a/cvte.go b/cvte.go index bf59665..20aea62 100644 --- a/cvte.go +++ b/cvte.go @@ -1,15 +1,11 @@ package cvt import ( - "encoding/json" "errors" "fmt" - "math" "reflect" "sort" - "strconv" "strings" - "time" ) var errConvFail = errors.New("convert failed") @@ -19,514 +15,30 @@ var formatOutOfLimitInt = "%w, out of max limit value(%d)" var formatOutOfLimitFloat = "%w, out of max limit value(%f)" var formatExtend = "%v, %w" -// BoolE convert an interface to a bool type -func BoolE(val interface{}) (bool, error) { - v, rk, rv := indirect(val) - - switch vv := v.(type) { - case bool: - return vv, nil - case int, int8, int16, int32, int64: - return rv.Int() != 0, nil - case uint, uint8, uint16, uint32, uint64: - return rv.Uint() != 0, nil - case float32, float64: - return rv.Float() != 0, nil - case []byte: - return str2bool(string(vv)) - case string: - return str2bool(vv) - case nil: - return false, nil - } - - switch rk.Kind() { - // by elem length - case reflect.Array, reflect.Slice, reflect.Map: - return rv.Len() > 0, nil - } - - return false, newErr(val, "bool") -} - -// returns the boolean value represented by the string -func str2bool(str string) (bool, error) { - if val, err := strconv.ParseBool(str); err == nil { - return val, nil - } else if val, err := strconv.ParseFloat(str, 64); err == nil { - return val != 0, nil - } - - switch strings.ToLower(strings.TrimSpace(str)) { - case "on": - return true, nil - case "off": - return false, nil - default: - return false, newErr(str, "bool") - } -} - -// Uint64E convert an interface to a uint64 type -func Uint64E(val interface{}) (uint64, error) { - v, e := convUint64(val) - if e := catch("uint64", val, e); e != nil { - return 0, e - } - - return v, nil -} - -// Uint32E convert an interface to a uint32 type -func Uint32E(val interface{}) (uint32, error) { - v, e := convUint64(val) - if e := catch("uint32", val, e); e != nil { - return 0, e - } - if v > math.MaxUint32 { - return 0, fmt.Errorf(formatOutOfLimitInt, 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 := convUint64(val) - if e := catch("uint16", val, e); e != nil { - return 0, e - } - if v > math.MaxUint16 { - return 0, fmt.Errorf(formatOutOfLimitInt, 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 := convUint64(val) - if e := catch("uint8", val, e); e != nil { - return 0, e - } - if v > math.MaxUint8 { - return 0, fmt.Errorf(formatOutOfLimitInt, 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 := convUint64(val) - if e := catch("uint", val, e); e != nil { - return 0, e - } - if v > uint64(^uint(0)) { - return 0, fmt.Errorf(formatOutOfLimitInt, 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, errConvFail -} - -// 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 - } - - return v, nil -} - -// Int32E convert an interface to a int32 type -func Int32E(val interface{}) (int32, error) { - v, e := convInt64(val) - if e := catch("int32", val, e); e != nil { - return 0, e - } - if v > math.MaxInt32 { - return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int32"), int32(math.MaxInt32)) - } - - return int32(v), nil -} - -// Int16E convert an interface to a int16 type -func Int16E(val interface{}) (int16, error) { - v, e := convInt64(val) - if e := catch("int16", val, e); e != nil { - return 0, e - } - if v > math.MaxInt16 { - return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int16"), int16(math.MaxInt16)) - } - - return int16(v), nil -} - -// Int8E convert an interface to a int8 type -func Int8E(val interface{}) (int8, error) { - v, e := convInt64(val) - if e := catch("int8", val, e); e != nil { - return 0, e - } - if v > math.MaxInt8 { - return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int8"), int8(math.MaxInt8)) - } - - return int8(v), nil -} - -// IntE convert an interface to a int type -func IntE(val interface{}) (int, error) { - v, e := convInt64(val) - if e := catch("int", val, e); e != nil { - return 0, e - } - if strconv.IntSize == 32 && v > math.MaxInt32 { - return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int"), int32(math.MaxInt32)) - } - - return int(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, errConvFail -} - -// Float64E convert an interface to a float64 type -func Float64E(val interface{}) (float64, 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 { - return vvv, nil - } - case []byte: - vvv, err := strconv.ParseFloat(string(vv), 64) - if err == nil { - return vvv, nil - } - case uint, uint8, uint16, uint32, uint64, uintptr: - return float64(rv.Uint()), nil - case int, int8, int16, int32, int64: - return float64(rv.Int()), nil - case float32: - // use fmt to fix float32 -> float64 precision loss - // eg: cvt.Float64E(float32(8.31)) - vvv, err := strconv.ParseFloat(fmt.Sprintf("%f", vv), 64) - if err == nil { - return vvv, nil - } - case float64: - return vv, nil - } - - return 0, errConvFail -} - -// Float32E convert an interface to a float32 type -func Float32E(val interface{}) (float32, error) { - v, e := Float64E(val) - if e := catch("float32", val, e); e != nil { - return 0, e - } - if v > math.MaxFloat32 { - return 0, fmt.Errorf(formatOutOfLimitFloat, newErr(val, "float32"), float32(math.MaxFloat32)) - } - - return float32(v), nil -} - -// StringE convert an interface to a string type -func StringE(val interface{}) (string, error) { - v, _, rv := indirect(val) - - // interface implements - switch vv := val.(type) { - case fmt.Stringer: - return vv.String(), nil - case error: - return vv.Error(), nil - case json.Marshaler: - vvv, e := vv.MarshalJSON() - if e == nil { - return string(vvv), nil - } - } - - // source type - switch vv := v.(type) { - case nil: - return "", nil - case bool: - return strconv.FormatBool(vv), nil - case string: - return vv, nil - case []byte: - return string(vv), nil - case []rune: - return string(vv), nil - case uint, uint8, uint16, uint32, uint64, uintptr: - return strconv.FormatUint(rv.Uint(), 10), nil - case int, int8, int16, int32, int64: - return strconv.FormatInt(rv.Int(), 10), nil - case float64: - return strconv.FormatFloat(vv, 'f', -1, 64), nil - case float32: - return strconv.FormatFloat(float64(vv), 'f', -1, 32), nil - } - - return "", newErr(val, "string") -} - -// TimeE convert an interface to a time.Time type -func TimeE(val interface{}) (t time.Time, err error) { - v, _, _ := indirect(val) - - // source type - switch vv := v.(type) { - case time.Time: - return vv, nil - case string: - return parseDate(vv) - case int, int32, int64, uint, uint32, uint64: - return time.Unix(Int64(vv), 0), nil - } - - // interface implements - switch vv := val.(type) { - case fmt.Stringer: - return parseDate(vv.String()) - } - - return time.Time{}, newErr(val, "time.Time") -} - -func parseDate(s string) (t time.Time, err error) { - fs := []string{ - time.RFC3339, - "2006-01-02T15:04:05", // iso8601 without timezone - time.RFC1123Z, - time.RFC1123, - time.RFC822Z, - time.RFC822, - time.RFC850, - time.ANSIC, - time.UnixDate, - time.RubyDate, - "2006-01-02 15:04:05.999999999 -0700 MST", // Time.String() - "2006-01-02", - "02 Jan 2006", - "2006-01-02T15:04:05-0700", // RFC3339 without timezone hh:mm colon - "2006-01-02 15:04:05 -07:00", - "2006-01-02 15:04:05 -0700", - "2006-01-02 15:04:05Z07:00", // RFC3339 without T - "2006-01-02 15:04:05Z0700", // RFC3339 without T or timezone hh:mm colon - "2006-01-02 15:04:05", - "2006.01.02", - "2006.01.02 15:04:05", - "2006/01/02", - "2006/01/02 15:04:05", - "2006年01月02日", - "2006年01月02日 15:04:05", - "2006年01月02日 15时04分05秒", - time.Kitchen, - time.Stamp, - time.StampMilli, - time.StampMicro, - time.StampNano, - } - - for _, dateType := range fs { - if t, err = time.Parse(dateType, s); err == nil { - return - } - } - - return t, fmt.Errorf("unable to parse date: %s", s) -} - -// SliceE convert an interface to a []interface{} type -func SliceE(val interface{}) (sl []interface{}, err error) { +// FieldE return the field value from map/struct, ignore the filed type +func FieldE(val interface{}, field interface{}) (interface{}, error) { if val == nil { return nil, errUnsupportedTypeNil } + sf := String(field) // match with the String of field, so field can be any type _, rt, rv := indirect(val) switch rt.Kind() { - case reflect.String: - for _, vvv := range rv.String() { - sl = append(sl, vvv) - } - return - case reflect.Slice, reflect.Array: - for j := 0; j < rv.Len(); j++ { - sl = append(sl, rv.Index(j).Interface()) - } - return - case reflect.Map: - for _, key := range sortedMapKeys(rv) { - sl = append(sl, rv.MapIndex(key).Interface()) - } - return - case reflect.Struct: - sl = deepStructValues(rt, rv) - return - } - - return nil, newErr(val, "slice") -} - -// SliceIntE convert an interface to a []int type -func SliceIntE(val interface{}) (sl []int, err error) { - list, err := SliceE(val) - if err != nil { - return - } - - for _, v := range list { - vv, err := IntE(v) - if err != nil { - return nil, err - } - sl = append(sl, vv) - } - - return -} - -// SliceInt64E convert an interface to a []int64 type -func SliceInt64E(val interface{}) (sl []int64, err error) { - list, err := SliceE(val) - if err != nil { - return - } - - for _, v := range list { - vv, err := Int64E(v) - if err != nil { - return nil, err - } - sl = append(sl, vv) - } - - return -} - -// SliceFloat64E convert an interface to a []float64 type -func SliceFloat64E(val interface{}) (sl []float64, err error) { - list, err := SliceE(val) - if err != nil { - return - } - - for _, v := range list { - vv, err := Float64E(v) - if err != nil { - return nil, err + case reflect.Map: // key of map + for _, key := range rv.MapKeys() { + if String(key.Interface()) == sf { + return rv.MapIndex(key).Interface(), nil + } } - sl = append(sl, vv) - } - - return -} - -// SliceStringE convert an interface to a []string type -func SliceStringE(val interface{}) (sl []string, err error) { - list, err := SliceE(val) - if err != nil { - return - } - - for _, v := range list { - vv, err := StringE(v) - if err != nil { - return nil, err + case reflect.Struct: // field of struct + vv := rv.FieldByName(sf) + if vv.IsValid() { + return vv.Interface(), nil } - sl = append(sl, vv) } - return + return nil, fmt.Errorf("%w(%s)", errFieldNotFound, sf) } // return the values of struct fields, and deep find the embedded fields @@ -550,84 +62,6 @@ func sortedMapKeys(v reflect.Value) (s []reflect.Value) { return } -// FieldE return the field value from map/struct, ignore the filed type -func FieldE(val interface{}, field interface{}) (interface{}, error) { - if val == nil { - return nil, errUnsupportedTypeNil - } - - sf := String(field) // match with the String of field, so field can be any type - _, rt, rv := indirect(val) - - switch rt.Kind() { - case reflect.Map: // key of map - for _, key := range rv.MapKeys() { - if String(key.Interface()) == sf { - return rv.MapIndex(key).Interface(), nil - } - } - case reflect.Struct: // field of struct - vv := rv.FieldByName(sf) - if vv.IsValid() { - return vv.Interface(), nil - } - } - - return nil, fmt.Errorf("%w(%s)", errFieldNotFound, sf) -} - -// ColumnsE return the values from a single column in the input array/slice/map of struct/map -func ColumnsE(val interface{}, field interface{}) (sl []interface{}, err error) { - if val == nil { - return nil, errUnsupportedTypeNil - } - - _, rt, rv := indirect(val) - - switch rt.Kind() { - case reflect.Slice, reflect.Array: - for j := 0; j < rv.Len(); j++ { - vv, e := FieldE(rv.Index(j).Interface(), field) - if e == nil { - sl = append(sl, vv) - } - } - case reflect.Map: - for _, key := range sortedMapKeys(rv) { - vv, e := FieldE(rv.MapIndex(key).Interface(), field) - if e == nil { - sl = append(sl, vv) - } - } - } - - // non valid field value, means error - if len(sl) > 0 { - return - } - - return nil, fmt.Errorf("unsupported type: %s", rt.Name()) -} - -// KeysE return the keys of map, sorted by asc -func KeysE(val interface{}) (sl []interface{}, err error) { - if val == nil { - return nil, errUnsupportedTypeNil - } - - _, rt, rv := indirect(val) - - switch rt.Kind() { - case reflect.Map: - for _, key := range sortedMapKeys(rv) { - sl = append(sl, key.Interface()) - } - return - } - - return nil, fmt.Errorf("unsupported type: %s", rt.Name()) -} - // returns the value with base type func indirect(a interface{}) (val interface{}, rt reflect.Type, rv reflect.Value) { if a == nil { diff --git a/cvte_test.go b/cvte_test.go index ebfe172..74cc057 100644 --- a/cvte_test.go +++ b/cvte_test.go @@ -1,11 +1,7 @@ package cvt_test import ( - "errors" "fmt" - "html/template" - "math" - "math/big" "testing" "time" @@ -95,1524 +91,6 @@ func Benchmark(b *testing.B) { // [function tests] -func TestBoolE(t *testing.T) { - tests := []struct { - input interface{} - expect bool - isErr bool - }{ - // false/scale - {0, false, false}, - {float64(0.00), false, false}, - {int(0.00), false, false}, - {int64(0.00), false, false}, - {uint(0.00), false, false}, - {uint64(0.00), false, false}, - {uint8(0.00), false, false}, - {nil, false, false}, - {false, false, false}, - {"false", false, false}, - {"FALSE", false, false}, - {"False", false, false}, - {"f", false, false}, - {"F", false, false}, - {"off", false, false}, - {"Off", false, false}, - {"0", false, false}, - {"0.00", false, false}, - {[]byte("false"), false, false}, - {[]byte("Off"), false, false}, - {aliasTypeInt_0, false, false}, - {&aliasTypeInt_0, false, false}, - {aliasTypeString_0, false, false}, - {&aliasTypeString_0, false, false}, - {aliasTypeString_off, false, false}, - {&aliasTypeString_off, false, false}, - {aliasTypeBool_false, false, false}, - {&aliasTypeBool_false, false, false}, - - // false/slice/array/map - {[]int{}, false, false}, - {[]string{}, false, false}, - {[...]string{}, false, false}, - {map[int]int{}, false, false}, - {map[string]string{}, false, false}, - - // true/scale - {true, true, false}, - {"true", true, false}, - {"TRUE", true, false}, - {"True", true, false}, - {"t", true, false}, - {"T", true, false}, - {1, true, false}, - {-1, true, false}, - {"on", true, false}, - {"On", true, false}, - {0.01, true, false}, - {"0.01", true, false}, - {[]byte("true"), true, false}, - {aliasTypeInt_1, true, false}, - {&aliasTypeInt_1, true, false}, - {aliasTypeString_1, true, false}, - {&aliasTypeString_1, true, false}, - {aliasTypeString_on, true, false}, - {&aliasTypeString_on, true, false}, - {aliasTypeBool_true, true, false}, - {&aliasTypeBool_true, true, false}, - - // true/slice/array/map - {[]int{1, 2, 3}, true, false}, - {[]string{"a", "b", "c"}, true, false}, - {[...]string{"a", "b", "c"}, true, false}, - {map[int]int{1: 111, 2: 222}, true, false}, - {map[string]string{"a": "aaa"}, true, false}, - - // errors - {"hello", false, true}, - {testing.T{}, false, true}, - {&testing.T{}, false, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.BoolE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Bool(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestUint64E(t *testing.T) { - tests := []struct { - input interface{} - expect uint64 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {int64(1487354638276643554), 1487354638276643554, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {uint64(1487354638276643554), 1487354638276643554, false}, - {uint(math.MaxUint32), uint64(math.MaxUint32), false}, - {uint32(math.MaxUint32), uint64(math.MaxUint32), false}, - {uint64(math.MaxUint64), uint64(math.MaxUint64), false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {[]byte("8"), 8, false}, - {[]byte("8.00"), 8, false}, - {[]byte("8.01"), 8, false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - {aliasTypeBool_true, 1, false}, - {&aliasTypeBool_true, 1, false}, - {aliasTypeBool_false, 0, false}, - {&aliasTypeBool_false, 0, false}, - - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"-8.01", 0, true}, - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - {aliasTypeString_8d15_minus, 0, true}, - {&aliasTypeString_8d15_minus, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Uint64E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Uint64(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestUint32E(t *testing.T) { - tests := []struct { - input interface{} - expect uint32 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {uint(math.MaxUint32), uint32(math.MaxUint32), false}, - {uint32(math.MaxUint32), uint32(math.MaxUint32), false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"-8.01", 0, true}, - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {uint64(math.MaxUint64), 0, true}, - {int64(1487354638276643554), 0, true}, - {uint64(1487354638276643554), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - {aliasTypeString_8d15_minus, 0, true}, - {&aliasTypeString_8d15_minus, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Uint32E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Uint32(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestUint16E(t *testing.T) { - tests := []struct { - input interface{} - expect uint16 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"-8.01", 0, true}, - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {uint64(math.MaxUint64), 0, true}, - {int64(1487354638276643554), 0, true}, - {uint64(1487354638276643554), 0, true}, - {uint(math.MaxUint32), 0, true}, - {uint32(math.MaxUint32), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - {aliasTypeString_8d15_minus, 0, true}, - {&aliasTypeString_8d15_minus, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Uint16E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Uint16(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestUint8E(t *testing.T) { - tests := []struct { - input interface{} - expect uint8 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"-8.01", 0, true}, - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {uint64(math.MaxUint64), 0, true}, - {int64(1487354638276643554), 0, true}, - {uint64(1487354638276643554), 0, true}, - {uint(math.MaxUint32), 0, true}, - {uint32(math.MaxUint32), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - {aliasTypeString_8d15_minus, 0, true}, - {&aliasTypeString_8d15_minus, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Uint8E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Uint8(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestUintE(t *testing.T) { - tests := []struct { - input interface{} - expect uint - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {uint64(math.MaxUint64), uint(math.MaxUint64), false}, - {uint32(math.MaxUint32), uint(math.MaxUint32), false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"-8.01", 0, true}, - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - {aliasTypeString_8d15_minus, 0, true}, - {&aliasTypeString_8d15_minus, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.UintE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Uint(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestInt64E(t *testing.T) { - tests := []struct { - input interface{} - expect int64 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {int(-8), -8, false}, - {int8(-8), -8, false}, - {int16(-8), -8, false}, - {int32(-8), -8, false}, - {int64(-8), -8, false}, - {float32(-8.31), -8, false}, - {float64(-8.31), -8, false}, - {"-8", -8, false}, - {"-8.01", -8, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {[]byte("-8"), -8, false}, - {[]byte("-8.01"), -8, false}, - {[]byte("8"), 8, false}, - {[]byte("8.00"), 8, false}, - {[]byte("8.01"), 8, false}, - {uint32(math.MaxUint32), int64(math.MaxUint32), false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - {aliasTypeString_8d15_minus, -8, false}, - {&aliasTypeString_8d15_minus, -8, false}, - {aliasTypeBool_true, 1, false}, - {&aliasTypeBool_true, 1, false}, - {aliasTypeBool_false, 0, false}, - {&aliasTypeBool_false, 0, false}, - - // errors - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {uint64(math.MaxUint64), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Int64E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Int64(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestInt32E(t *testing.T) { - tests := []struct { - input interface{} - expect int32 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {int(-8), -8, false}, - {int8(-8), -8, false}, - {int16(-8), -8, false}, - {int32(-8), -8, false}, - {int64(-8), -8, false}, - {float32(-8.31), -8, false}, - {float64(-8.31), -8, false}, - {"-8", -8, false}, - {"-8.01", -8, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {[]byte("-8"), -8, false}, - {[]byte("-8.01"), -8, false}, - {[]byte("8"), 8, false}, - {[]byte("8.00"), 8, false}, - {[]byte("8.01"), 8, false}, - {math.MaxInt32, int32(math.MaxInt32), false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - {aliasTypeString_8d15_minus, -8, false}, - {&aliasTypeString_8d15_minus, -8, false}, - {aliasTypeBool_true, 1, false}, - {&aliasTypeBool_true, 1, false}, - {aliasTypeBool_false, 0, false}, - {&aliasTypeBool_false, 0, false}, - - // errors - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {uint64(math.MaxUint64), 0, true}, - {int64(math.MaxInt64), 0, true}, - {uint32(math.MaxUint32), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Int32E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Int32(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestInt16E(t *testing.T) { - tests := []struct { - input interface{} - expect int16 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {int(-8), -8, false}, - {int8(-8), -8, false}, - {int16(-8), -8, false}, - {int32(-8), -8, false}, - {int64(-8), -8, false}, - {float32(-8.31), -8, false}, - {float64(-8.31), -8, false}, - {"-8", -8, false}, - {"-8.01", -8, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {[]byte("-8"), -8, false}, - {[]byte("-8.01"), -8, false}, - {[]byte("8"), 8, false}, - {[]byte("8.00"), 8, false}, - {[]byte("8.01"), 8, false}, - {math.MaxInt16, int16(math.MaxInt16), false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - {aliasTypeString_8d15_minus, -8, false}, - {&aliasTypeString_8d15_minus, -8, false}, - {aliasTypeBool_true, 1, false}, - {&aliasTypeBool_true, 1, false}, - {aliasTypeBool_false, 0, false}, - {&aliasTypeBool_false, 0, false}, - - // errors - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {uint64(math.MaxUint64), 0, true}, - {uint32(math.MaxUint32), 0, true}, - {int64(math.MaxInt64), 0, true}, - {int32(math.MaxInt32), 0, true}, - {uint16(math.MaxUint16), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Int16E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Int16(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestInt8E(t *testing.T) { - tests := []struct { - input interface{} - expect int8 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {int(-8), -8, false}, - {int8(-8), -8, false}, - {int16(-8), -8, false}, - {int32(-8), -8, false}, - {int64(-8), -8, false}, - {float32(-8.31), -8, false}, - {float64(-8.31), -8, false}, - {"-8", -8, false}, - {"-8.01", -8, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {[]byte("-8"), -8, false}, - {[]byte("-8.01"), -8, false}, - {[]byte("8"), 8, false}, - {[]byte("8.00"), 8, false}, - {[]byte("8.01"), 8, false}, - {int8(math.MaxInt8), math.MaxInt8, false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - {aliasTypeString_8d15_minus, -8, false}, - {&aliasTypeString_8d15_minus, -8, false}, - {aliasTypeBool_true, 1, false}, - {&aliasTypeBool_true, 1, false}, - {aliasTypeBool_false, 0, false}, - {&aliasTypeBool_false, 0, false}, - - // errors - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {uint64(math.MaxUint64), 0, true}, - {uint32(math.MaxUint32), 0, true}, - {int64(math.MaxInt64), 0, true}, - {int32(math.MaxInt32), 0, true}, - {int16(math.MaxInt16), 0, true}, - {uint8(math.MaxUint8), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Int8E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Int8(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestIntE(t *testing.T) { - tests := []struct { - input interface{} - expect int - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {int(-8), -8, false}, - {int8(-8), -8, false}, - {int16(-8), -8, false}, - {int32(-8), -8, false}, - {int64(-8), -8, false}, - {float32(-8.31), -8, false}, - {float64(-8.31), -8, false}, - {"-8", -8, false}, - {"-8.01", -8, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8, false}, - {[]byte("-8"), -8, false}, - {[]byte("-8.01"), -8, false}, - {[]byte("8"), 8, false}, - {[]byte("8.00"), 8, false}, - {[]byte("8.01"), 8, false}, - {int(math.MaxInt32), int(math.MaxInt32), false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8, false}, - {&aliasTypeString_8d15, 8, false}, - {aliasTypeString_8d15_minus, -8, false}, - {&aliasTypeString_8d15_minus, -8, false}, - {aliasTypeBool_true, 1, false}, - {&aliasTypeBool_true, 1, false}, - {aliasTypeBool_false, 0, false}, - {&aliasTypeBool_false, 0, false}, - - // errors - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"4873546382743564386435354655456575456754356765546554643456", 0, true}, - {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, - {uint64(math.MaxUint64), 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.IntE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Int(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestFloat64E(t *testing.T) { - tests := []struct { - input interface{} - expect float64 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), float64(8.31), false}, - {float64(8.31), float64(8.31), false}, - {true, 1, false}, - {false, 0, false}, - {int(-8), -8, false}, - {int8(-8), -8, false}, - {int16(-8), -8, false}, - {int32(-8), -8, false}, - {int64(-8), -8, false}, - {float32(-8.31), -8.31, false}, - {float32(-8.3190), -8.3190, false}, - {float64(-8.31), -8.31, false}, - {"-8", -8, false}, - {"-8.01", -8.01, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8.01, false}, - {[]byte("-8"), -8, false}, - {[]byte("-8.01"), -8.01, false}, - {[]byte("8"), 8, false}, - {[]byte("8.00"), 8, false}, - {[]byte("8.01"), 8.01, false}, - {int64(math.MaxInt64), float64(math.MaxInt64), false}, - {uint64(math.MaxUint64), float64(math.MaxUint64), false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8.15, false}, - {&aliasTypeString_8d15, 8.15, false}, - {aliasTypeString_8d15_minus, -8.15, false}, - {&aliasTypeString_8d15_minus, -8.15, false}, - {aliasTypeBool_true, 1, false}, - {&aliasTypeBool_true, 1, false}, - {aliasTypeBool_false, 0, false}, - {&aliasTypeBool_false, 0, false}, - - // errors - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"hello", 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Float64E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Float64(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestFloat32E(t *testing.T) { - tests := []struct { - input interface{} - expect float32 - isErr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), float32(8.31), false}, - {float64(8.31), float32(8.31), false}, - {true, 1, false}, - {false, 0, false}, - {int(-8), -8, false}, - {int8(-8), -8, false}, - {int16(-8), -8, false}, - {int32(-8), -8, false}, - {int64(-8), -8, false}, - {float32(-8.31), -8.31, false}, - {float64(-8.31), -8.31, false}, - {"-8", -8, false}, - {"-8.01", -8.01, false}, - {"8", 8, false}, - {"8.00", 8, false}, - {"8.01", 8.01, false}, - {[]byte("-8"), -8, false}, - {[]byte("-8.01"), -8.01, false}, - {[]byte("8"), 8, false}, - {[]byte("8.00"), 8, false}, - {[]byte("8.01"), 8.01, false}, - {int(math.MaxInt32), float32(math.MaxInt32), false}, - {nil, 0, false}, - {aliasTypeInt_0, 0, false}, - {&aliasTypeInt_0, 0, false}, - {aliasTypeInt_1, 1, false}, - {&aliasTypeInt_1, 1, false}, - {aliasTypeString_0, 0, false}, - {&aliasTypeString_0, 0, false}, - {aliasTypeString_1, 1, false}, - {&aliasTypeString_1, 1, false}, - {aliasTypeString_8d15, 8.15, false}, - {&aliasTypeString_8d15, 8.15, false}, - {aliasTypeString_8d15_minus, -8.15, false}, - {&aliasTypeString_8d15_minus, -8.15, false}, - {aliasTypeBool_true, 1, false}, - {&aliasTypeBool_true, 1, false}, - {aliasTypeBool_false, 0, false}, - {&aliasTypeBool_false, 0, false}, - - // errors - {"10a", 0, true}, - {"a10a", 0, true}, - {"8.01a", 0, true}, - {"8.01 ", 0, true}, - {"hello", 0, true}, - {float64(math.MaxFloat64), 0, true}, - {testing.T{}, 0, true}, - {&testing.T{}, 0, true}, - {[]int{}, 0, true}, - {[]string{}, 0, true}, - {[...]string{}, 0, true}, - {map[int]string{}, 0, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.Float32E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Float32(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestStringE(t *testing.T) { - tests := []struct { - input interface{} - expect string - isErr bool - }{ - {int(8), "8", false}, - {int8(8), "8", false}, - {int16(8), "8", false}, - {int32(8), "8", false}, - {int64(8), "8", false}, - {uint(8), "8", false}, - {uint8(8), "8", false}, - {uint16(8), "8", false}, - {uint32(8), "8", false}, - {uint64(8), "8", false}, - {float32(8.31), "8.31", false}, - {float64(8.31), "8.31", false}, - {true, "true", false}, - {false, "false", false}, - {int(-8), "-8", false}, - {int8(-8), "-8", false}, - {int16(-8), "-8", false}, - {int32(-8), "-8", false}, - {int64(-8), "-8", false}, - {float32(-8.31), "-8.31", false}, - {float64(-8.31), "-8.31", false}, - {[]byte("-8"), "-8", false}, - {[]byte("-8.01"), "-8.01", false}, - {[]byte("8"), "8", false}, - {[]byte("8.00"), "8.00", false}, - {[]byte("8.01"), "8.01", false}, - {[]rune("我❤️中国"), "我❤️中国", false}, - {nil, "", false}, - {aliasTypeInt_0, "0", false}, - {&aliasTypeInt_0, "0", false}, - {aliasTypeInt_1, "1", false}, - {&aliasTypeInt_1, "1", false}, - {aliasTypeString_0, "0", false}, - {&aliasTypeString_0, "0", false}, - {aliasTypeString_1, "1", false}, - {&aliasTypeString_1, "1", false}, - {aliasTypeString_8d15, "8.15", false}, - {&aliasTypeString_8d15, "8.15", false}, - {aliasTypeString_8d15_minus, "-8.15", false}, - {&aliasTypeString_8d15_minus, "-8.15", false}, - {aliasTypeBool_true, "true", false}, - {&aliasTypeBool_true, "true", false}, - {aliasTypeBool_false, "false", false}, - {&aliasTypeBool_false, "false", false}, - {errors.New("errors"), "errors", false}, - {time.Friday, "Friday", false}, - {big.NewInt(123), "123", false}, - {TestMarshalJSON{}, "MarshalJSON", false}, - {&TestMarshalJSON{}, "MarshalJSON", false}, - {template.URL("http://host.foo"), "http://host.foo", false}, - - // errors - {testing.T{}, "", true}, - {&testing.T{}, "", true}, - {[]int{}, "", true}, - {[]string{}, "", true}, - {[...]string{}, "", true}, - {map[int]string{}, "", true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.StringE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.String(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestSliceE(t *testing.T) { - tests := []struct { - input interface{} - expect []interface{} - isErr bool - }{ - {"hello", []interface{}{'h', 'e', 'l', 'l', 'o'}, false}, - {[]byte("hey"), []interface{}{byte('h'), byte('e'), byte('y')}, false}, - {[]rune("我爱中国"), []interface{}{'我', '爱', '中', '国'}, false}, - {[]int{}, nil, false}, - {[]int{1, 2, 3}, []interface{}{1, 2, 3}, false}, - {[]string{}, nil, false}, - {[]string{"a", "b", "c"}, []interface{}{"a", "b", "c"}, false}, - {[]interface{}{1, "a", -1, nil}, []interface{}{1, "a", -1, nil}, false}, - {[...]string{}, nil, false}, - {[...]string{"a", "b", "c"}, []interface{}{"a", "b", "c"}, false}, - {map[int]string{}, nil, false}, - {map[int]string{1: "111", 2: "222"}, []interface{}{"111", "222"}, false}, - {map[int]TestStructC{}, nil, false}, - {map[int]TestStructC{1: {"c1"}, 2: {"c2"}}, []interface{}{TestStructC{"c1"}, TestStructC{"c2"}}, false}, - // map key convert to string, and sorted by key asc - {map[interface{}]string{ - "k": "k", - 1: "1", - 0: "0", - "b": "b", - -1: "-1", - "3c": "3c", - -0.1: "-0.1", - }, []interface{}{"-0.1", "-1", "0", "1", "3c", "b", "k"}, false}, - - {testing.T{}, nil, false}, - {&testing.T{}, nil, false}, - {TestStructA{}, []interface{}{0, "", 0, "", TestStructD{0}}, false}, - {&TestStructB{}, []interface{}{"", 0}, false}, - {&TestStructE{}, []interface{}{0, (*TestStructD)(nil)}, false}, - - // errors - {int(123), nil, true}, - {uint16(123), nil, true}, - {float64(12.3), nil, true}, - {func() {}, nil, true}, - {nil, nil, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.SliceE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - - // Non-E test with no default value: - v = cvt.Slice(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestSliceIntE(t *testing.T) { - tests := []struct { - input interface{} - expect []int - isErr bool - }{ - {[]int{}, nil, false}, - {[]int{1, 2, 3}, []int{1, 2, 3}, false}, - {[]string{}, nil, false}, - {[]interface{}{1, "-1", -1, nil}, []int{1, -1, -1, 0}, false}, - {[...]string{}, nil, false}, - {[...]string{"1", "2", "3"}, []int{1, 2, 3}, false}, - - // sorted by key asc - {map[int]string{}, nil, false}, - {map[int]string{2: "222", 1: "111"}, []int{111, 222}, false}, - {map[int]TestStructC{}, nil, false}, - {map[interface{}]string{ - 1: "1", - 0: "0", - -1: "-1", - -0.1: "-0.1", - }, []int{0, -1, 0, 1}, false}, - - {testing.T{}, nil, false}, - {&testing.T{}, nil, false}, - - // errors - {int(123), nil, true}, - {uint16(123), nil, true}, - {float64(12.3), nil, true}, - {func() {}, nil, true}, - {nil, nil, true}, - {[]string{"a", "b", "c"}, nil, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.SliceIntE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestSliceInt64E(t *testing.T) { - tests := []struct { - input interface{} - expect []int64 - isErr bool - }{ - {[]int{}, nil, false}, - {[]int{1, 2, 3}, []int64{1, 2, 3}, false}, - {[]float64{1, 2, 3.3, 4.8}, []int64{1, 2, 3, 4}, false}, - {[]string{}, nil, false}, - {[]interface{}{1, "-1", -1, nil}, []int64{1, -1, -1, 0}, false}, - {[...]string{}, nil, false}, - {[...]string{"1", "2", "3"}, []int64{1, 2, 3}, false}, - - // sorted by key asc - {map[int]string{}, nil, false}, - {map[int]string{2: "222", 1: "111"}, []int64{111, 222}, false}, - {map[int]TestStructC{}, nil, false}, - {map[interface{}]string{ - 1: "1", - 0: "0", - -1: "-1", - -0.1: "-0.1", - }, []int64{0, -1, 0, 1}, false}, - - {testing.T{}, nil, false}, - {&testing.T{}, nil, false}, - - // errors - {int(123), nil, true}, - {uint16(123), nil, true}, - {float64(12.3), nil, true}, - {func() {}, nil, true}, - {nil, nil, true}, - {[]string{"a", "b", "c"}, nil, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.SliceInt64E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestSliceFloat64E(t *testing.T) { - tests := []struct { - input interface{} - expect []float64 - isErr bool - }{ - {[]int{}, nil, false}, - {[]int{1, 2, 3}, []float64{1, 2, 3}, false}, - {[]float64{1, 2, 3}, []float64{1, 2, 3}, false}, - {[]float64{1.1, 2.2, 3}, []float64{1.1, 2.2, 3}, false}, - {[]string{}, nil, false}, - {[]interface{}{1, "-1.1", -1.7, nil}, []float64{1, -1.1, -1.7, 0}, false}, - {[...]string{}, nil, false}, - {[...]string{"1.01", "2.22", "3.30", "-1"}, []float64{1.01, 2.22, 3.3, -1}, false}, - - // sorted by key asc - {map[int]string{}, nil, false}, - {map[int]string{2: "222", 1: "11.1"}, []float64{11.1, 222}, false}, - {map[int]TestStructC{}, nil, false}, - {map[interface{}]string{ - 1: "1", - 0.9: "0.9", - -1: "-1", - -0.1: "-0.1", - }, []float64{-0.1, -1, 0.9, 1}, false}, - - {testing.T{}, nil, false}, - {&testing.T{}, nil, false}, - - // errors - {int(123), nil, true}, - {uint16(123), nil, true}, - {float64(12.3), nil, true}, - {func() {}, nil, true}, - {nil, nil, true}, - {[]string{"a", "b", "c"}, nil, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.SliceFloat64E(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestSliceStringE(t *testing.T) { - tests := []struct { - input interface{} - expect []string - isErr bool - }{ - {[]int{}, nil, false}, - {[]int{1, 2, 3}, []string{"1", "2", "3"}, false}, - {[]float64{1, 2, 3}, []string{"1", "2", "3"}, false}, - {[]float64{1.1, 2.2, 3.0}, []string{"1.1", "2.2", "3"}, false}, - {[]string{}, nil, false}, - {[]interface{}{1, "-1.1", -1.7, nil}, []string{"1", "-1.1", "-1.7", ""}, false}, - {[...]string{}, nil, false}, - {[...]string{"1.01", "2.22", "3.30", "-1"}, []string{"1.01", "2.22", "3.30", "-1"}, false}, - {[]string{"a", "b", "c"}, []string{"a", "b", "c"}, false}, - - // sorted by key asc - {map[int]string{}, nil, false}, - {map[int]string{2: "222", 1: "11.1"}, []string{"11.1", "222"}, false}, - {map[int]TestStructC{1: {"C12"}}, []string{"C12"}, false}, - - {testing.T{}, nil, false}, - {&testing.T{}, nil, false}, - - // errors - {int(123), nil, true}, - {uint16(123), nil, true}, - {float64(12.3), nil, true}, - {func() {}, nil, true}, - {nil, nil, true}, - {[]interface{}{testing.T{}}, nil, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) - - v, err := cvt.SliceStringE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - } -} - func TestFieldE(t *testing.T) { tests := []struct { input interface{} @@ -1654,163 +132,3 @@ func TestFieldE(t *testing.T) { assert.Equal(t, tt.expect, v, msg) } } - -func TestColumnsE(t *testing.T) { - tests := []struct { - input interface{} - field interface{} - expect interface{} - isErr bool - }{ - {[]interface{}{TestStructE{D1: 1, DD: &TestStructD{D1: 2}}}, "D1", []interface{}{1}, false}, - {[]TestStructE{{D1: 1}, {D1: 2}}, "D1", []interface{}{1, 2}, false}, - {[]TestStructE{{DD: &TestStructD{}}, {D1: 2}}, "DD", []interface{}{&TestStructD{}, (*TestStructD)(nil)}, false}, - {[]interface{}{TestStructE{D1: 1, DD: &TestStructD{D1: 2}}}, "DD", []interface{}{&TestStructD{D1: 2}}, false}, - {[]map[string]interface{}{{"1": 111, "DDD": "D1"}, {"2": 222, "DDD": "D2"}, {"DDD": nil}}, "DDD", []interface{}{"D1", "D2", nil}, false}, - {map[int]map[string]interface{}{1: {"1": 111, "DDD": "D1"}, 2: {"2": 222, "DDD": "D2"}, 3: {"DDD": nil}}, "DDD", []interface{}{"D1", "D2", nil}, false}, - {map[int]TestStructD{1: {11}, 2: {22}}, "D1", []interface{}{11, 22}, false}, - - // errors - {TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "", nil, true}, - {TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "Age", nil, true}, - {int(123), "Name", nil, true}, - {uint16(123), "Name", nil, true}, - {float64(12.3), "Name", nil, true}, - {"Name", "Name", nil, true}, - {func() {}, "Name", nil, true}, - {nil, "Name", nil, true}, - {TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "D1", nil, true}, - {TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "DD", nil, true}, - {TestStructB{B1: 1, TestStructC: TestStructC{C1: "c1"}}, "C1", nil, true}, - {map[int]interface{}{123: "112233"}, "123", nil, true}, - {map[int]interface{}{123: "112233"}, 123, nil, true}, - {map[string]interface{}{"123": "112233"}, 123, nil, true}, - {map[string]interface{}{"c": "ccc"}, TestStructC{C1: "c"}, nil, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf( - "i = %d, input[%+v], field[%s], expect[%+v], isErr[%v]", - i, tt.input, tt.field, tt.expect, tt.isErr, - ) - - v, err := cvt.ColumnsE(tt.input, tt.field) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestTimeE(t *testing.T) { - loc := time.UTC - - tests := []struct { - input interface{} - expect time.Time - isErr bool - }{ - {"2009-11-10 23:00:00 +0000 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // Time.String() - {"Tue Nov 10 23:00:00 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // ANSIC - {"Tue Nov 10 23:00:00 UTC 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // UnixDate - {"Tue Nov 10 23:00:00 +0000 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RubyDate - {"10 Nov 09 23:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC822 - {"10 Nov 09 23:00 +0000", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC822Z - {"Tuesday, 10-Nov-09 23:00:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC850 - {"Tue, 10 Nov 2009 23:00:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC1123 - {"Tue, 10 Nov 2009 23:00:00 +0000", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC1123Z - {"2009-11-10T23:00:00Z", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC3339 - {"2018-10-21T23:21:29+0200", time.Date(2018, 10, 21, 21, 21, 29, 0, loc), false}, // RFC3339 without timezone hh:mm colon - {"2009-11-10T23:00:00Z", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC3339Nano - {"11:00PM", time.Date(0, 1, 1, 23, 0, 0, 0, loc), false}, // Kitchen - {"Nov 10 23:00:00", time.Date(0, 11, 10, 23, 0, 0, 0, loc), false}, // Stamp - {"Nov 10 23:00:00.000", time.Date(0, 11, 10, 23, 0, 0, 0, loc), false}, // StampMilli - {"Nov 10 23:00:00.000000", time.Date(0, 11, 10, 23, 0, 0, 0, loc), false}, // StampMicro - {"Nov 10 23:00:00.000000000", time.Date(0, 11, 10, 23, 0, 0, 0, loc), false}, // StampNano - {"2016-03-06 15:28:01-00:00", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, // RFC3339 without T - {"2016-03-06 15:28:01-0000", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, // RFC3339 without T or timezone hh:mm colon - {"2016-03-06 15:28:01", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, - {"2016-03-06 15:28:01 -0000", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, - {"2016-03-06 15:28:01 -00:00", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, - {"2006-01-02", time.Date(2006, 1, 2, 0, 0, 0, 0, loc), false}, - {"02 Jan 2006", time.Date(2006, 1, 2, 0, 0, 0, 0, loc), false}, - {"2010.03.07", time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, - {"2010.03.07 18:08:18", time.Date(2010, 3, 7, 18, 8, 18, 0, loc), false}, - {"2010/03/07", time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, - {"2010/03/07 18:08:18", time.Date(2010, 3, 7, 18, 8, 18, 0, loc), false}, - {"2010年03月07日", time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, - {"2010年03月07日 18:08:18", time.Date(2010, 3, 7, 18, 8, 18, 0, loc), false}, - {"2010年03月07日 18时08分18秒", time.Date(2010, 3, 7, 18, 8, 18, 0, loc), false}, - {1472574600, time.Date(2016, 8, 30, 16, 30, 0, 0, loc), false}, - {int(1482597504), time.Date(2016, 12, 24, 16, 38, 24, 0, loc), false}, - {int64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, - {int32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, - {uint(1482597504), time.Date(2016, 12, 24, 16, 38, 24, 0, loc), false}, - {uint64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, - {uint32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, - {time.Date(2009, 2, 13, 23, 31, 30, 0, loc), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, - {TestTimeStringer{time.Date(2010, 3, 7, 0, 0, 0, 0, loc)}, time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, - - // errors - {"2006", time.Time{}, true}, - {"hello world", time.Time{}, true}, - {testing.T{}, time.Time{}, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf( - "i = %d, input[%+v], expect[%+v], isErr[%v]", - i, tt.input, tt.expect, tt.isErr, - ) - - v, err := cvt.TimeE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v.UTC(), v, msg) - - // Non-E test - v = cvt.Time(tt.input) - assert.Equal(t, tt.expect, v.UTC(), msg) - } -} - -func TestKeysE(t *testing.T) { - tests := []struct { - input interface{} - expect []interface{} - isErr bool - }{ - {map[int]map[string]interface{}{1: {"1": 111, "DDD": 12.3}, 2: {"2": 222, "DDD": "321"}, 3: {"DDD": nil}}, []interface{}{1, 2, 3}, false}, - {map[string]interface{}{"A": 1, "2": 2}, []interface{}{"2", "A"}, false}, - {map[float64]TestStructD{1: {11}, 2: {22}}, []interface{}{float64(1), float64(2)}, false}, - {map[interface{}]interface{}{1: 1, 2.2: 2.22, "A": "A"}, []interface{}{1, 2.2, "A"}, false}, - - // errors - {nil, nil, true}, - {"Name", nil, true}, - {testing.T{}, nil, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf( - "i = %d, input[%+v], expect[%+v], isErr[%v]", - i, tt.input, tt.expect, tt.isErr, - ) - - v, err := cvt.KeysE(tt.input) - if tt.isErr { - assert.Error(t, err, msg) - continue - } - - assert.NoError(t, err, msg) - assert.Equal(t, tt.expect, v, msg) - } -} diff --git a/float.go b/float.go new file mode 100644 index 0000000..6b9503b --- /dev/null +++ b/float.go @@ -0,0 +1,86 @@ +package cvt + +import ( + "fmt" + "math" + "strconv" +) + +// Float64 convert an interface to a float64 type, with default value +func Float64(v interface{}, def ...float64) float64 { + if v, err := Float64E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Float64E convert an interface to a float64 type +func Float64E(val interface{}) (float64, 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 { + return vvv, nil + } + case []byte: + vvv, err := strconv.ParseFloat(string(vv), 64) + if err == nil { + return vvv, nil + } + case uint, uint8, uint16, uint32, uint64, uintptr: + return float64(rv.Uint()), nil + case int, int8, int16, int32, int64: + return float64(rv.Int()), nil + case float32: + // use fmt to fix float32 -> float64 precision loss + // eg: cvt.Float64E(float32(8.31)) + vvv, err := strconv.ParseFloat(fmt.Sprintf("%f", vv), 64) + if err == nil { + return vvv, nil + } + case float64: + return vv, nil + } + + return 0, errConvFail +} + +// Float32 convert an interface to a float32 type, with default value +func Float32(v interface{}, def ...float32) float32 { + if v, err := Float32E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Float32E convert an interface to a float32 type +func Float32E(val interface{}) (float32, error) { + v, e := Float64E(val) + if e := catch("float32", val, e); e != nil { + return 0, e + } + if v > math.MaxFloat32 { + return 0, fmt.Errorf(formatOutOfLimitFloat, newErr(val, "float32"), float32(math.MaxFloat32)) + } + + return float32(v), nil +} diff --git a/float_test.go b/float_test.go new file mode 100644 index 0000000..f15935e --- /dev/null +++ b/float_test.go @@ -0,0 +1,378 @@ +package cvt_test + +import ( + "fmt" + "math" + "testing" + + "github.com/shockerli/cvt" + "github.com/stretchr/testify/assert" +) + +func TestFloat64_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def float64 + expect float64 + }{ + // 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.31}, + {float64(8.31), 1, 8.31}, + {"8", 2, 8}, + {"8.00", 2, 8}, + {"8.01", 2, 8.01}, + {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.31}, + {float64(-8.31), 1, -8.31}, + {int64(math.MaxInt64), 1, float64(math.MaxInt64)}, + {uint64(math.MaxUint64), 1, float64(math.MaxUint64)}, + {"-8", 1, -8}, + {"-8.01", 1, -8.01}, + {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.15}, + {&aliasTypeString_8d15, 2, 8.15}, + {aliasTypeString_8d15_minus, 1, -8.15}, + {&aliasTypeString_8d15_minus, 1, -8.15}, + + // unsupported value, def == expect + {"10a", 1.11, 1.11}, + {"a10a", 1.11, 1.11}, + {"8.01a", 1.11, 1.11}, + {"8.01 ", 1.11, 1.11}, + {"hello", 1.11, 1.11}, + {testing.T{}, 1.11, 1.11}, + {&testing.T{}, 1.11, 1.11}, + {[]int{}, 1.11, 1.11}, + {[]string{}, 1.11, 1.11}, + {[...]string{}, 1.11, 1.11}, + {map[int]string{}, 1.11, 1.11}, + } + + 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.Float64(tt.input, tt.def) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestFloat64_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect float64 + }{ + {"8.01a", 0}, + {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}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + + v := cvt.Float64(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestFloat32_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def float32 + expect float32 + }{ + // 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.31}, + {float64(8.31), 1, 8.31}, + {"8", 2, 8}, + {"8.00", 2, 8}, + {"8.01", 2, 8.01}, + {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.31}, + {float64(-8.31), 1, -8.31}, + {int(math.MaxInt32), 1, float32(math.MaxInt32)}, + {"-8", 1, -8}, + {"-8.01", 1, -8.01}, + {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.15}, + {&aliasTypeString_8d15, 2, 8.15}, + {aliasTypeString_8d15_minus, 1, -8.15}, + {&aliasTypeString_8d15_minus, 1, -8.15}, + + // unsupported value, def == expect + {"10a", 1.11, 1.11}, + {"a10a", 1.11, 1.11}, + {"8.01a", 1.11, 1.11}, + {"8.01 ", 1.11, 1.11}, + {"hello", 1.11, 1.11}, + {testing.T{}, 1.11, 1.11}, + {&testing.T{}, 1.11, 1.11}, + {[]int{}, 1.11, 1.11}, + {[]string{}, 1.11, 1.11}, + {[...]string{}, 1.11, 1.11}, + {map[int]string{}, 1.11, 1.11}, + } + + 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.Float32(tt.input, tt.def) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestFloat32_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect float32 + }{ + {"8.01a", 0}, + {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}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + + v := cvt.Float32(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestFloat64E(t *testing.T) { + tests := []struct { + input interface{} + expect float64 + isErr bool + }{ + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), float64(8.31), false}, + {float64(8.31), float64(8.31), false}, + {true, 1, false}, + {false, 0, false}, + {int(-8), -8, false}, + {int8(-8), -8, false}, + {int16(-8), -8, false}, + {int32(-8), -8, false}, + {int64(-8), -8, false}, + {float32(-8.31), -8.31, false}, + {float32(-8.3190), -8.3190, false}, + {float64(-8.31), -8.31, false}, + {"-8", -8, false}, + {"-8.01", -8.01, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8.01, false}, + {[]byte("-8"), -8, false}, + {[]byte("-8.01"), -8.01, false}, + {[]byte("8"), 8, false}, + {[]byte("8.00"), 8, false}, + {[]byte("8.01"), 8.01, false}, + {int64(math.MaxInt64), float64(math.MaxInt64), false}, + {uint64(math.MaxUint64), float64(math.MaxUint64), false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8.15, false}, + {&aliasTypeString_8d15, 8.15, false}, + {aliasTypeString_8d15_minus, -8.15, false}, + {&aliasTypeString_8d15_minus, -8.15, false}, + {aliasTypeBool_true, 1, false}, + {&aliasTypeBool_true, 1, false}, + {aliasTypeBool_false, 0, false}, + {&aliasTypeBool_false, 0, false}, + + // errors + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Float64E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Float64(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestFloat32E(t *testing.T) { + tests := []struct { + input interface{} + expect float32 + isErr bool + }{ + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), float32(8.31), false}, + {float64(8.31), float32(8.31), false}, + {true, 1, false}, + {false, 0, false}, + {int(-8), -8, false}, + {int8(-8), -8, false}, + {int16(-8), -8, false}, + {int32(-8), -8, false}, + {int64(-8), -8, false}, + {float32(-8.31), -8.31, false}, + {float64(-8.31), -8.31, false}, + {"-8", -8, false}, + {"-8.01", -8.01, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8.01, false}, + {[]byte("-8"), -8, false}, + {[]byte("-8.01"), -8.01, false}, + {[]byte("8"), 8, false}, + {[]byte("8.00"), 8, false}, + {[]byte("8.01"), 8.01, false}, + {int(math.MaxInt32), float32(math.MaxInt32), false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8.15, false}, + {&aliasTypeString_8d15, 8.15, false}, + {aliasTypeString_8d15_minus, -8.15, false}, + {&aliasTypeString_8d15_minus, -8.15, false}, + {aliasTypeBool_true, 1, false}, + {&aliasTypeBool_true, 1, false}, + {aliasTypeBool_false, 0, false}, + {&aliasTypeBool_false, 0, false}, + + // errors + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"hello", 0, true}, + {float64(math.MaxFloat64), 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Float32E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Float32(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} diff --git a/go.mod b/go.mod index 0f60b77..ee587f1 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/shockerli/cvt -go 1.15 +go 1.16 require github.com/stretchr/testify v1.7.0 diff --git a/int.go b/int.go new file mode 100644 index 0000000..b28921b --- /dev/null +++ b/int.go @@ -0,0 +1,335 @@ +package cvt + +import ( + "fmt" + "math" + "strconv" +) + +// Uint64 convert an interface to a uint64 type, with default value +func Uint64(v interface{}, def ...uint64) uint64 { + if v, err := Uint64E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Uint64E convert an interface to a uint64 type +func Uint64E(val interface{}) (uint64, error) { + v, e := convUint64(val) + if e := catch("uint64", val, e); e != nil { + return 0, e + } + + return v, nil +} + +// Uint32 convert an interface to a uint32 type, with default value +func Uint32(v interface{}, def ...uint32) uint32 { + if v, err := Uint32E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Uint32E convert an interface to a uint32 type +func Uint32E(val interface{}) (uint32, error) { + v, e := convUint64(val) + if e := catch("uint32", val, e); e != nil { + return 0, e + } + if v > math.MaxUint32 { + return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "uint32"), uint32(math.MaxUint32)) + } + + return uint32(v), nil +} + +// Uint16 convert an interface to a uint16 type, with default value +func Uint16(v interface{}, def ...uint16) uint16 { + if v, err := Uint16E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Uint16E convert an interface to a uint16 type +func Uint16E(val interface{}) (uint16, error) { + v, e := convUint64(val) + if e := catch("uint16", val, e); e != nil { + return 0, e + } + if v > math.MaxUint16 { + return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "uint16"), uint16(math.MaxUint16)) + } + + return uint16(v), nil +} + +// Uint8 convert an interface to a uint8 type, with default value +func Uint8(v interface{}, def ...uint8) uint8 { + if v, err := Uint8E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Uint8E convert an interface to a uint8 type +func Uint8E(val interface{}) (uint8, error) { + v, e := convUint64(val) + if e := catch("uint8", val, e); e != nil { + return 0, e + } + if v > math.MaxUint8 { + return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "uint8"), uint8(math.MaxUint8)) + } + + return uint8(v), nil +} + +// Uint convert an interface to a uint type, with default value +func Uint(v interface{}, def ...uint) uint { + if v, err := UintE(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// UintE convert an interface to a uint type +func UintE(val interface{}) (uint, error) { + v, e := convUint64(val) + if e := catch("uint", val, e); e != nil { + return 0, e + } + if v > uint64(^uint(0)) { + return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "uint"), ^uint(0)) + } + + return uint(v), nil +} + +// 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 +} + +// 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 + } + + return v, nil +} + +// Int32 convert an interface to a int32 type, with default value +func Int32(v interface{}, def ...int32) int32 { + if v, err := Int32E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Int32E convert an interface to a int32 type +func Int32E(val interface{}) (int32, error) { + v, e := convInt64(val) + if e := catch("int32", val, e); e != nil { + return 0, e + } + if v > math.MaxInt32 { + return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int32"), int32(math.MaxInt32)) + } + + return int32(v), nil +} + +// Int16 convert an interface to a int16 type, with default value +func Int16(v interface{}, def ...int16) int16 { + if v, err := Int16E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Int16E convert an interface to a int16 type +func Int16E(val interface{}) (int16, error) { + v, e := convInt64(val) + if e := catch("int16", val, e); e != nil { + return 0, e + } + if v > math.MaxInt16 { + return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int16"), int16(math.MaxInt16)) + } + + return int16(v), nil +} + +// Int8 convert an interface to a int8 type, with default value +func Int8(v interface{}, def ...int8) int8 { + if v, err := Int8E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// Int8E convert an interface to a int8 type +func Int8E(val interface{}) (int8, error) { + v, e := convInt64(val) + if e := catch("int8", val, e); e != nil { + return 0, e + } + if v > math.MaxInt8 { + return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int8"), int8(math.MaxInt8)) + } + + return int8(v), nil +} + +// Int convert an interface to a int type, with default value +func Int(v interface{}, def ...int) int { + if v, err := IntE(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return 0 +} + +// IntE convert an interface to a int type +func IntE(val interface{}) (int, error) { + v, e := convInt64(val) + if e := catch("int", val, e); e != nil { + return 0, e + } + if strconv.IntSize == 32 && v > math.MaxInt32 { + return 0, fmt.Errorf(formatOutOfLimitInt, newErr(val, "int"), int32(math.MaxInt32)) + } + + return int(v), nil +} + +// convert any value to uint64 +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, errConvFail +} + +// convert any value to int64 +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, errConvFail +} diff --git a/cvt_test.go b/int_test.go similarity index 50% rename from cvt_test.go rename to int_test.go index a809eca..1487324 100644 --- a/cvt_test.go +++ b/int_test.go @@ -1,121 +1,14 @@ package cvt_test import ( - "errors" "fmt" "math" - "math/big" "testing" - "time" - - "github.com/stretchr/testify/assert" "github.com/shockerli/cvt" + "github.com/stretchr/testify/assert" ) -func TestBool_HasDefault(t *testing.T) { - tests := []struct { - input interface{} - def bool - expect bool - }{ - // supported value, def is not used, def != expect - {0, true, false}, - {float64(0.00), true, false}, - {int(0.00), true, false}, - {int64(0.00), true, false}, - {uint(0.00), true, false}, - {uint64(0.00), true, false}, - {uint8(0.00), true, false}, - {nil, true, false}, - {"false", true, false}, - {"FALSE", true, false}, - {"False", true, false}, - {"f", true, false}, - {"F", true, false}, - {false, true, false}, - {"off", true, false}, - {"Off", true, false}, - {[]byte("Off"), true, false}, - {aliasTypeInt_0, true, false}, - {&aliasTypeInt_0, true, false}, - {aliasTypeString_0, true, false}, - {&aliasTypeString_0, true, false}, - {aliasTypeString_off, true, false}, - {&aliasTypeString_off, true, false}, - - {[]int{}, true, false}, - {[]string{}, true, false}, - {[...]string{}, true, false}, - {map[int]int{}, true, false}, - {map[string]string{}, true, false}, - - {"true", false, true}, - {"TRUE", false, true}, - {"True", false, true}, - {"t", false, true}, - {"T", false, true}, - {1, false, true}, - {true, false, true}, - {-1, false, true}, - {"on", false, true}, - {"On", false, true}, - {0.01, false, true}, - {aliasTypeInt_1, false, true}, - {&aliasTypeInt_1, false, true}, - {aliasTypeString_1, false, true}, - {&aliasTypeString_1, false, true}, - {aliasTypeString_on, false, true}, - {&aliasTypeString_on, false, true}, - - {[]int{1, 2, 3}, false, true}, - {[]string{"a", "b", "c"}, false, true}, - {[...]string{"a", "b", "c"}, false, true}, - {map[int]int{1: 111, 2: 222}, false, true}, - {map[string]string{"a": "aaa"}, false, true}, - - // unsupported value, def == expect - {"hello", true, true}, - {"hello", false, false}, - {testing.T{}, true, true}, - {testing.T{}, false, false}, - {&testing.T{}, true, true}, - {&testing.T{}, false, false}, - } - - 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.Bool(tt.input, tt.def) - assert.Equal(t, tt.expect, v, msg) - } -} - -func TestBool_BaseLine(t *testing.T) { - tests := []struct { - input interface{} - expect bool - }{ - {testing.T{}, false}, - {&testing.T{}, false}, - {[]int{}, false}, - {[]int{1, 2, 3}, true}, - {[]string{}, false}, - {[]string{"a", "b", "c"}, true}, - {[...]string{}, false}, - {map[int]string{}, false}, - {aliasTypeString_8d15_minus, true}, - {&aliasTypeString_8d15_minus, true}, - } - - for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) - - v := cvt.Bool(tt.input) - assert.Equal(t, tt.expect, v, msg) - } -} - func TestUint64_HasDefault(t *testing.T) { tests := []struct { input interface{} @@ -1056,334 +949,897 @@ func TestInt_BaseLine(t *testing.T) { } } -func TestFloat64_HasDefault(t *testing.T) { +func TestUint64E(t *testing.T) { tests := []struct { input interface{} - def float64 - expect float64 + expect uint64 + isErr bool }{ - // 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.31}, - {float64(8.31), 1, 8.31}, - {"8", 2, 8}, - {"8.00", 2, 8}, - {"8.01", 2, 8.01}, - {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.31}, - {float64(-8.31), 1, -8.31}, - {int64(math.MaxInt64), 1, float64(math.MaxInt64)}, - {uint64(math.MaxUint64), 1, float64(math.MaxUint64)}, - {"-8", 1, -8}, - {"-8.01", 1, -8.01}, - {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.15}, - {&aliasTypeString_8d15, 2, 8.15}, - {aliasTypeString_8d15_minus, 1, -8.15}, - {&aliasTypeString_8d15_minus, 1, -8.15}, - - // unsupported value, def == expect - {"10a", 1.11, 1.11}, - {"a10a", 1.11, 1.11}, - {"8.01a", 1.11, 1.11}, - {"8.01 ", 1.11, 1.11}, - {"hello", 1.11, 1.11}, - {testing.T{}, 1.11, 1.11}, - {&testing.T{}, 1.11, 1.11}, - {[]int{}, 1.11, 1.11}, - {[]string{}, 1.11, 1.11}, - {[...]string{}, 1.11, 1.11}, - {map[int]string{}, 1.11, 1.11}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {int64(1487354638276643554), 1487354638276643554, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {uint64(1487354638276643554), 1487354638276643554, false}, + {uint(math.MaxUint32), uint64(math.MaxUint32), false}, + {uint32(math.MaxUint32), uint64(math.MaxUint32), false}, + {uint64(math.MaxUint64), uint64(math.MaxUint64), false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {[]byte("8"), 8, false}, + {[]byte("8.00"), 8, false}, + {[]byte("8.01"), 8, false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + {aliasTypeBool_true, 1, false}, + {&aliasTypeBool_true, 1, false}, + {aliasTypeBool_false, 0, false}, + {&aliasTypeBool_false, 0, false}, + + // errors + {int(-8), 0, true}, + {int8(-8), 0, true}, + {int16(-8), 0, true}, + {int32(-8), 0, true}, + {int64(-8), 0, true}, + {float32(-8.31), 0, true}, + {float64(-8.31), 0, true}, + {"-8", 0, true}, + {"-8.01", 0, true}, + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, + {aliasTypeString_8d15_minus, 0, true}, + {&aliasTypeString_8d15_minus, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) - v := cvt.Float64(tt.input, tt.def) + v, err := cvt.Uint64E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Uint64(tt.input) assert.Equal(t, tt.expect, v, msg) } } -func TestFloat64_BaseLine(t *testing.T) { +func TestUint32E(t *testing.T) { tests := []struct { input interface{} - expect float64 + expect uint32 + isErr bool }{ - {"8.01a", 0}, - {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}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {uint(math.MaxUint32), uint32(math.MaxUint32), false}, + {uint32(math.MaxUint32), uint32(math.MaxUint32), false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + + // errors + {int(-8), 0, true}, + {int8(-8), 0, true}, + {int16(-8), 0, true}, + {int32(-8), 0, true}, + {int64(-8), 0, true}, + {float32(-8.31), 0, true}, + {float64(-8.31), 0, true}, + {"-8", 0, true}, + {"-8.01", 0, true}, + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {uint64(math.MaxUint64), 0, true}, + {int64(1487354638276643554), 0, true}, + {uint64(1487354638276643554), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, + {aliasTypeString_8d15_minus, 0, true}, + {&aliasTypeString_8d15_minus, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Uint32E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) - v := cvt.Float64(tt.input) + // Non-E test with no default value: + v = cvt.Uint32(tt.input) assert.Equal(t, tt.expect, v, msg) } } -func TestFloat32_HasDefault(t *testing.T) { +func TestUint16E(t *testing.T) { tests := []struct { input interface{} - def float32 - expect float32 + expect uint16 + isErr bool }{ - // 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.31}, - {float64(8.31), 1, 8.31}, - {"8", 2, 8}, - {"8.00", 2, 8}, - {"8.01", 2, 8.01}, - {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.31}, - {float64(-8.31), 1, -8.31}, - {int(math.MaxInt32), 1, float32(math.MaxInt32)}, - {"-8", 1, -8}, - {"-8.01", 1, -8.01}, - {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.15}, - {&aliasTypeString_8d15, 2, 8.15}, - {aliasTypeString_8d15_minus, 1, -8.15}, - {&aliasTypeString_8d15_minus, 1, -8.15}, - - // unsupported value, def == expect - {"10a", 1.11, 1.11}, - {"a10a", 1.11, 1.11}, - {"8.01a", 1.11, 1.11}, - {"8.01 ", 1.11, 1.11}, - {"hello", 1.11, 1.11}, - {testing.T{}, 1.11, 1.11}, - {&testing.T{}, 1.11, 1.11}, - {[]int{}, 1.11, 1.11}, - {[]string{}, 1.11, 1.11}, - {[...]string{}, 1.11, 1.11}, - {map[int]string{}, 1.11, 1.11}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + + // errors + {int(-8), 0, true}, + {int8(-8), 0, true}, + {int16(-8), 0, true}, + {int32(-8), 0, true}, + {int64(-8), 0, true}, + {float32(-8.31), 0, true}, + {float64(-8.31), 0, true}, + {"-8", 0, true}, + {"-8.01", 0, true}, + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {uint64(math.MaxUint64), 0, true}, + {int64(1487354638276643554), 0, true}, + {uint64(1487354638276643554), 0, true}, + {uint(math.MaxUint32), 0, true}, + {uint32(math.MaxUint32), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, + {aliasTypeString_8d15_minus, 0, true}, + {&aliasTypeString_8d15_minus, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Uint16E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } - v := cvt.Float32(tt.input, tt.def) + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Uint16(tt.input) assert.Equal(t, tt.expect, v, msg) } } -func TestFloat32_BaseLine(t *testing.T) { +func TestUint8E(t *testing.T) { tests := []struct { input interface{} - expect float32 + expect uint8 + isErr bool }{ - {"8.01a", 0}, - {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}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + + // errors + {int(-8), 0, true}, + {int8(-8), 0, true}, + {int16(-8), 0, true}, + {int32(-8), 0, true}, + {int64(-8), 0, true}, + {float32(-8.31), 0, true}, + {float64(-8.31), 0, true}, + {"-8", 0, true}, + {"-8.01", 0, true}, + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {uint64(math.MaxUint64), 0, true}, + {int64(1487354638276643554), 0, true}, + {uint64(1487354638276643554), 0, true}, + {uint(math.MaxUint32), 0, true}, + {uint32(math.MaxUint32), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, + {aliasTypeString_8d15_minus, 0, true}, + {&aliasTypeString_8d15_minus, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%+v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Uint8E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } - v := cvt.Float32(tt.input) + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Uint8(tt.input) assert.Equal(t, tt.expect, v, msg) } } -func TestString_HasDefault(t *testing.T) { +func TestUintE(t *testing.T) { tests := []struct { input interface{} - def string - expect string + expect uint + isErr bool }{ - // supported value, def is not used, def != expect - {uint64(8), "xxx", "8"}, - {float32(8.31), "xxx", "8.31"}, - {float64(-8.31), "xxx", "-8.31"}, - {true, "xxx", "true"}, - {int64(-8), "xxx", "-8"}, - {[]byte("8.01"), "xxx", "8.01"}, - {[]rune("我❤️中国"), "xxx", "我❤️中国"}, - {nil, "xxx", ""}, - {aliasTypeInt_0, "xxx", "0"}, - {&aliasTypeString_8d15_minus, "xxx", "-8.15"}, - {aliasTypeBool_true, "xxx", "true"}, - {errors.New("errors"), "xxx", "errors"}, - {time.Friday, "xxx", "Friday"}, - {big.NewInt(123), "xxx", "123"}, - {TestMarshalJSON{}, "xxx", "MarshalJSON"}, - {&TestMarshalJSON{}, "xxx", "MarshalJSON"}, - - // unsupported value, def == expect - {testing.T{}, "xxx", "xxx"}, - {&testing.T{}, "xxx", "xxx"}, - {[]int{}, "xxx", "xxx"}, - {[]string{}, "xxx", "xxx"}, - {[...]string{}, "xxx", "xxx"}, - {map[int]string{}, "xxx", "xxx"}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {uint64(math.MaxUint64), uint(math.MaxUint64), false}, + {uint32(math.MaxUint32), uint(math.MaxUint32), false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + + // errors + {int(-8), 0, true}, + {int8(-8), 0, true}, + {int16(-8), 0, true}, + {int32(-8), 0, true}, + {int64(-8), 0, true}, + {float32(-8.31), 0, true}, + {float64(-8.31), 0, true}, + {"-8", 0, true}, + {"-8.01", 0, true}, + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, + {aliasTypeString_8d15_minus, 0, true}, + {&aliasTypeString_8d15_minus, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.UintE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } - v := cvt.String(tt.input, tt.def) + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Uint(tt.input) assert.Equal(t, tt.expect, v, msg) } } -func TestString_BaseLine(t *testing.T) { +func TestInt64E(t *testing.T) { tests := []struct { input interface{} - expect string + expect int64 + isErr bool }{ - {testing.T{}, ""}, - {&testing.T{}, ""}, - {[]int{}, ""}, - {[]string{}, ""}, - {[...]string{}, ""}, - {map[int]string{}, ""}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {int(-8), -8, false}, + {int8(-8), -8, false}, + {int16(-8), -8, false}, + {int32(-8), -8, false}, + {int64(-8), -8, false}, + {float32(-8.31), -8, false}, + {float64(-8.31), -8, false}, + {"-8", -8, false}, + {"-8.01", -8, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {[]byte("-8"), -8, false}, + {[]byte("-8.01"), -8, false}, + {[]byte("8"), 8, false}, + {[]byte("8.00"), 8, false}, + {[]byte("8.01"), 8, false}, + {uint32(math.MaxUint32), int64(math.MaxUint32), false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + {aliasTypeString_8d15_minus, -8, false}, + {&aliasTypeString_8d15_minus, -8, false}, + {aliasTypeBool_true, 1, false}, + {&aliasTypeBool_true, 1, false}, + {aliasTypeBool_false, 0, false}, + {&aliasTypeBool_false, 0, false}, + + // errors + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {uint64(math.MaxUint64), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Int64E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } - v := cvt.String(tt.input) + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Int64(tt.input) assert.Equal(t, tt.expect, v, msg) } } -func TestSlice_HasDefault(t *testing.T) { +func TestInt32E(t *testing.T) { tests := []struct { input interface{} - def []interface{} - expect []interface{} + expect int32 + isErr bool }{ - // supported value, def is not used, def != expect - {[]int{1, 2, 3}, []interface{}{"a", "b"}, []interface{}{1, 2, 3}}, - {testing.T{}, []interface{}{1, 2, 3}, nil}, - - // unsupported value, def == expect - {int(123), []interface{}{"hello"}, []interface{}{"hello"}}, - {uint16(123), nil, nil}, - {func() {}, []interface{}{}, []interface{}{}}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {int(-8), -8, false}, + {int8(-8), -8, false}, + {int16(-8), -8, false}, + {int32(-8), -8, false}, + {int64(-8), -8, false}, + {float32(-8.31), -8, false}, + {float64(-8.31), -8, false}, + {"-8", -8, false}, + {"-8.01", -8, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {[]byte("-8"), -8, false}, + {[]byte("-8.01"), -8, false}, + {[]byte("8"), 8, false}, + {[]byte("8.00"), 8, false}, + {[]byte("8.01"), 8, false}, + {math.MaxInt32, int32(math.MaxInt32), false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + {aliasTypeString_8d15_minus, -8, false}, + {&aliasTypeString_8d15_minus, -8, false}, + {aliasTypeBool_true, 1, false}, + {&aliasTypeBool_true, 1, false}, + {aliasTypeBool_false, 0, false}, + {&aliasTypeBool_false, 0, false}, + + // errors + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {uint64(math.MaxUint64), 0, true}, + {int64(math.MaxInt64), 0, true}, + {uint32(math.MaxUint32), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Int32E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } - v := cvt.Slice(tt.input, tt.def) + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Int32(tt.input) assert.Equal(t, tt.expect, v, msg) } } -func TestSlice_BaseLine(t *testing.T) { +func TestInt16E(t *testing.T) { tests := []struct { input interface{} - expect []interface{} + expect int16 + isErr bool }{ - {int(123), nil}, - {uint16(123), nil}, - {func() {}, nil}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {int(-8), -8, false}, + {int8(-8), -8, false}, + {int16(-8), -8, false}, + {int32(-8), -8, false}, + {int64(-8), -8, false}, + {float32(-8.31), -8, false}, + {float64(-8.31), -8, false}, + {"-8", -8, false}, + {"-8.01", -8, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {[]byte("-8"), -8, false}, + {[]byte("-8.01"), -8, false}, + {[]byte("8"), 8, false}, + {[]byte("8.00"), 8, false}, + {[]byte("8.01"), 8, false}, + {math.MaxInt16, int16(math.MaxInt16), false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + {aliasTypeString_8d15_minus, -8, false}, + {&aliasTypeString_8d15_minus, -8, false}, + {aliasTypeBool_true, 1, false}, + {&aliasTypeBool_true, 1, false}, + {aliasTypeBool_false, 0, false}, + {&aliasTypeBool_false, 0, false}, + + // errors + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {uint64(math.MaxUint64), 0, true}, + {uint32(math.MaxUint32), 0, true}, + {int64(math.MaxInt64), 0, true}, + {int32(math.MaxInt32), 0, true}, + {uint16(math.MaxUint16), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Int16E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } - v := cvt.Slice(tt.input) + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Int16(tt.input) assert.Equal(t, tt.expect, v, msg) } } -func TestTime_HasDefault(t *testing.T) { - loc := time.UTC +func TestInt8E(t *testing.T) { tests := []struct { input interface{} - def time.Time - expect time.Time + expect int8 + isErr bool }{ - // supported value, def is not used, def != expect - {"2018-10-21T23:21:29+0200", time.Date(2010, 4, 23, 11, 11, 11, 0, loc), time.Date(2018, 10, 21, 21, 21, 29, 0, loc)}, - - // unsupported value, def == expect - {"hello world", time.Date(2010, 4, 23, 11, 11, 11, 0, loc), time.Date(2010, 4, 23, 11, 11, 11, 0, loc)}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {int(-8), -8, false}, + {int8(-8), -8, false}, + {int16(-8), -8, false}, + {int32(-8), -8, false}, + {int64(-8), -8, false}, + {float32(-8.31), -8, false}, + {float64(-8.31), -8, false}, + {"-8", -8, false}, + {"-8.01", -8, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {[]byte("-8"), -8, false}, + {[]byte("-8.01"), -8, false}, + {[]byte("8"), 8, false}, + {[]byte("8.00"), 8, false}, + {[]byte("8.01"), 8, false}, + {int8(math.MaxInt8), math.MaxInt8, false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + {aliasTypeString_8d15_minus, -8, false}, + {&aliasTypeString_8d15_minus, -8, false}, + {aliasTypeBool_true, 1, false}, + {&aliasTypeBool_true, 1, false}, + {aliasTypeBool_false, 0, false}, + {&aliasTypeBool_false, 0, false}, + + // errors + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {uint64(math.MaxUint64), 0, true}, + {uint32(math.MaxUint32), 0, true}, + {int64(math.MaxInt64), 0, true}, + {int32(math.MaxInt32), 0, true}, + {int16(math.MaxInt16), 0, true}, + {uint8(math.MaxUint8), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], def[%+v], expect[%+v]", i, tt.input, tt.def, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.Int8E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } - v := cvt.Time(tt.input, tt.def) - assert.Equal(t, tt.expect, v.UTC(), msg) + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Int8(tt.input) + assert.Equal(t, tt.expect, v, msg) } } -func TestTime_BaseLine(t *testing.T) { +func TestIntE(t *testing.T) { tests := []struct { input interface{} - expect time.Time + expect int + isErr bool }{ - {"hello world", time.Time{}}, - {testing.T{}, time.Time{}}, + {int(8), 8, false}, + {int8(8), 8, false}, + {int16(8), 8, false}, + {int32(8), 8, false}, + {int64(8), 8, false}, + {uint(8), 8, false}, + {uint8(8), 8, false}, + {uint16(8), 8, false}, + {uint32(8), 8, false}, + {uint64(8), 8, false}, + {float32(8.31), 8, false}, + {float64(8.31), 8, false}, + {true, 1, false}, + {false, 0, false}, + {int(-8), -8, false}, + {int8(-8), -8, false}, + {int16(-8), -8, false}, + {int32(-8), -8, false}, + {int64(-8), -8, false}, + {float32(-8.31), -8, false}, + {float64(-8.31), -8, false}, + {"-8", -8, false}, + {"-8.01", -8, false}, + {"8", 8, false}, + {"8.00", 8, false}, + {"8.01", 8, false}, + {[]byte("-8"), -8, false}, + {[]byte("-8.01"), -8, false}, + {[]byte("8"), 8, false}, + {[]byte("8.00"), 8, false}, + {[]byte("8.01"), 8, false}, + {int(math.MaxInt32), int(math.MaxInt32), false}, + {nil, 0, false}, + {aliasTypeInt_0, 0, false}, + {&aliasTypeInt_0, 0, false}, + {aliasTypeInt_1, 1, false}, + {&aliasTypeInt_1, 1, false}, + {aliasTypeString_0, 0, false}, + {&aliasTypeString_0, 0, false}, + {aliasTypeString_1, 1, false}, + {&aliasTypeString_1, 1, false}, + {aliasTypeString_8d15, 8, false}, + {&aliasTypeString_8d15, 8, false}, + {aliasTypeString_8d15_minus, -8, false}, + {&aliasTypeString_8d15_minus, -8, false}, + {aliasTypeBool_true, 1, false}, + {&aliasTypeBool_true, 1, false}, + {aliasTypeBool_false, 0, false}, + {&aliasTypeBool_false, 0, false}, + + // errors + {"10a", 0, true}, + {"a10a", 0, true}, + {"8.01a", 0, true}, + {"8.01 ", 0, true}, + {"4873546382743564386435354655456575456754356765546554643456", 0, true}, + {float64(4873546382743564386435354655456575456754356765546554643456), 0, true}, + {uint64(math.MaxUint64), 0, true}, + {"hello", 0, true}, + {testing.T{}, 0, true}, + {&testing.T{}, 0, true}, + {[]int{}, 0, true}, + {[]string{}, 0, true}, + {[...]string{}, 0, true}, + {map[int]string{}, 0, true}, } for i, tt := range tests { - msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.IntE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) - v := cvt.Time(tt.input) + // Non-E test with no default value: + v = cvt.Int(tt.input) assert.Equal(t, tt.expect, v, msg) } } diff --git a/slice.go b/slice.go new file mode 100644 index 0000000..aa01b94 --- /dev/null +++ b/slice.go @@ -0,0 +1,175 @@ +package cvt + +import ( + "fmt" + "reflect" +) + +// Slice convert an interface to a []interface{} type, with default value +func Slice(v interface{}, def ...[]interface{}) []interface{} { + if v, err := SliceE(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return nil +} + +// SliceE convert an interface to a []interface{} type +func SliceE(val interface{}) (sl []interface{}, err error) { + if val == nil { + return nil, errUnsupportedTypeNil + } + + _, rt, rv := indirect(val) + + switch rt.Kind() { + case reflect.String: + for _, vvv := range rv.String() { + sl = append(sl, vvv) + } + return + case reflect.Slice, reflect.Array: + for j := 0; j < rv.Len(); j++ { + sl = append(sl, rv.Index(j).Interface()) + } + return + case reflect.Map: + for _, key := range sortedMapKeys(rv) { + sl = append(sl, rv.MapIndex(key).Interface()) + } + return + case reflect.Struct: + sl = deepStructValues(rt, rv) + return + } + + return nil, newErr(val, "slice") +} + +// SliceIntE convert an interface to a []int type +func SliceIntE(val interface{}) (sl []int, err error) { + list, err := SliceE(val) + if err != nil { + return + } + + for _, v := range list { + vv, err := IntE(v) + if err != nil { + return nil, err + } + sl = append(sl, vv) + } + + return +} + +// SliceInt64E convert an interface to a []int64 type +func SliceInt64E(val interface{}) (sl []int64, err error) { + list, err := SliceE(val) + if err != nil { + return + } + + for _, v := range list { + vv, err := Int64E(v) + if err != nil { + return nil, err + } + sl = append(sl, vv) + } + + return +} + +// SliceFloat64E convert an interface to a []float64 type +func SliceFloat64E(val interface{}) (sl []float64, err error) { + list, err := SliceE(val) + if err != nil { + return + } + + for _, v := range list { + vv, err := Float64E(v) + if err != nil { + return nil, err + } + sl = append(sl, vv) + } + + return +} + +// SliceStringE convert an interface to a []string type +func SliceStringE(val interface{}) (sl []string, err error) { + list, err := SliceE(val) + if err != nil { + return + } + + for _, v := range list { + vv, err := StringE(v) + if err != nil { + return nil, err + } + sl = append(sl, vv) + } + + return +} + +// ColumnsE return the values from a single column in the input array/slice/map of struct/map +func ColumnsE(val interface{}, field interface{}) (sl []interface{}, err error) { + if val == nil { + return nil, errUnsupportedTypeNil + } + + _, rt, rv := indirect(val) + + switch rt.Kind() { + case reflect.Slice, reflect.Array: + for j := 0; j < rv.Len(); j++ { + vv, e := FieldE(rv.Index(j).Interface(), field) + if e == nil { + sl = append(sl, vv) + } + } + case reflect.Map: + for _, key := range sortedMapKeys(rv) { + vv, e := FieldE(rv.MapIndex(key).Interface(), field) + if e == nil { + sl = append(sl, vv) + } + } + } + + // non valid field value, means error + if len(sl) > 0 { + return + } + + return nil, fmt.Errorf("unsupported type: %s", rt.Name()) +} + +// KeysE return the keys of map, sorted by asc +func KeysE(val interface{}) (sl []interface{}, err error) { + if val == nil { + return nil, errUnsupportedTypeNil + } + + _, rt, rv := indirect(val) + + switch rt.Kind() { + case reflect.Map: + for _, key := range sortedMapKeys(rv) { + sl = append(sl, key.Interface()) + } + return + } + + return nil, fmt.Errorf("unsupported type: %s", rt.Name()) +} diff --git a/slice_test.go b/slice_test.go new file mode 100644 index 0000000..429204f --- /dev/null +++ b/slice_test.go @@ -0,0 +1,398 @@ +package cvt_test + +import ( + "fmt" + "testing" + + "github.com/shockerli/cvt" + "github.com/stretchr/testify/assert" +) + +func TestSlice_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def []interface{} + expect []interface{} + }{ + // supported value, def is not used, def != expect + {[]int{1, 2, 3}, []interface{}{"a", "b"}, []interface{}{1, 2, 3}}, + {testing.T{}, []interface{}{1, 2, 3}, nil}, + + // unsupported value, def == expect + {int(123), []interface{}{"hello"}, []interface{}{"hello"}}, + {uint16(123), nil, nil}, + {func() {}, []interface{}{}, []interface{}{}}, + } + + 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.Slice(tt.input, tt.def) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestSlice_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect []interface{} + }{ + {int(123), nil}, + {uint16(123), nil}, + {func() {}, nil}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + + v := cvt.Slice(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestSliceE(t *testing.T) { + tests := []struct { + input interface{} + expect []interface{} + isErr bool + }{ + {"hello", []interface{}{'h', 'e', 'l', 'l', 'o'}, false}, + {[]byte("hey"), []interface{}{byte('h'), byte('e'), byte('y')}, false}, + {[]rune("我爱中国"), []interface{}{'我', '爱', '中', '国'}, false}, + {[]int{}, nil, false}, + {[]int{1, 2, 3}, []interface{}{1, 2, 3}, false}, + {[]string{}, nil, false}, + {[]string{"a", "b", "c"}, []interface{}{"a", "b", "c"}, false}, + {[]interface{}{1, "a", -1, nil}, []interface{}{1, "a", -1, nil}, false}, + {[...]string{}, nil, false}, + {[...]string{"a", "b", "c"}, []interface{}{"a", "b", "c"}, false}, + {map[int]string{}, nil, false}, + {map[int]string{1: "111", 2: "222"}, []interface{}{"111", "222"}, false}, + {map[int]TestStructC{}, nil, false}, + {map[int]TestStructC{1: {"c1"}, 2: {"c2"}}, []interface{}{TestStructC{"c1"}, TestStructC{"c2"}}, false}, + // map key convert to string, and sorted by key asc + {map[interface{}]string{ + "k": "k", + 1: "1", + 0: "0", + "b": "b", + -1: "-1", + "3c": "3c", + -0.1: "-0.1", + }, []interface{}{"-0.1", "-1", "0", "1", "3c", "b", "k"}, false}, + + {testing.T{}, nil, false}, + {&testing.T{}, nil, false}, + {TestStructA{}, []interface{}{0, "", 0, "", TestStructD{0}}, false}, + {&TestStructB{}, []interface{}{"", 0}, false}, + {&TestStructE{}, []interface{}{0, (*TestStructD)(nil)}, false}, + + // errors + {int(123), nil, true}, + {uint16(123), nil, true}, + {float64(12.3), nil, true}, + {func() {}, nil, true}, + {nil, nil, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.SliceE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.Slice(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestSliceIntE(t *testing.T) { + tests := []struct { + input interface{} + expect []int + isErr bool + }{ + {[]int{}, nil, false}, + {[]int{1, 2, 3}, []int{1, 2, 3}, false}, + {[]string{}, nil, false}, + {[]interface{}{1, "-1", -1, nil}, []int{1, -1, -1, 0}, false}, + {[...]string{}, nil, false}, + {[...]string{"1", "2", "3"}, []int{1, 2, 3}, false}, + + // sorted by key asc + {map[int]string{}, nil, false}, + {map[int]string{2: "222", 1: "111"}, []int{111, 222}, false}, + {map[int]TestStructC{}, nil, false}, + {map[interface{}]string{ + 1: "1", + 0: "0", + -1: "-1", + -0.1: "-0.1", + }, []int{0, -1, 0, 1}, false}, + + {testing.T{}, nil, false}, + {&testing.T{}, nil, false}, + + // errors + {int(123), nil, true}, + {uint16(123), nil, true}, + {float64(12.3), nil, true}, + {func() {}, nil, true}, + {nil, nil, true}, + {[]string{"a", "b", "c"}, nil, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.SliceIntE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestSliceInt64E(t *testing.T) { + tests := []struct { + input interface{} + expect []int64 + isErr bool + }{ + {[]int{}, nil, false}, + {[]int{1, 2, 3}, []int64{1, 2, 3}, false}, + {[]float64{1, 2, 3.3, 4.8}, []int64{1, 2, 3, 4}, false}, + {[]string{}, nil, false}, + {[]interface{}{1, "-1", -1, nil}, []int64{1, -1, -1, 0}, false}, + {[...]string{}, nil, false}, + {[...]string{"1", "2", "3"}, []int64{1, 2, 3}, false}, + + // sorted by key asc + {map[int]string{}, nil, false}, + {map[int]string{2: "222", 1: "111"}, []int64{111, 222}, false}, + {map[int]TestStructC{}, nil, false}, + {map[interface{}]string{ + 1: "1", + 0: "0", + -1: "-1", + -0.1: "-0.1", + }, []int64{0, -1, 0, 1}, false}, + + {testing.T{}, nil, false}, + {&testing.T{}, nil, false}, + + // errors + {int(123), nil, true}, + {uint16(123), nil, true}, + {float64(12.3), nil, true}, + {func() {}, nil, true}, + {nil, nil, true}, + {[]string{"a", "b", "c"}, nil, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.SliceInt64E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestSliceFloat64E(t *testing.T) { + tests := []struct { + input interface{} + expect []float64 + isErr bool + }{ + {[]int{}, nil, false}, + {[]int{1, 2, 3}, []float64{1, 2, 3}, false}, + {[]float64{1, 2, 3}, []float64{1, 2, 3}, false}, + {[]float64{1.1, 2.2, 3}, []float64{1.1, 2.2, 3}, false}, + {[]string{}, nil, false}, + {[]interface{}{1, "-1.1", -1.7, nil}, []float64{1, -1.1, -1.7, 0}, false}, + {[...]string{}, nil, false}, + {[...]string{"1.01", "2.22", "3.30", "-1"}, []float64{1.01, 2.22, 3.3, -1}, false}, + + // sorted by key asc + {map[int]string{}, nil, false}, + {map[int]string{2: "222", 1: "11.1"}, []float64{11.1, 222}, false}, + {map[int]TestStructC{}, nil, false}, + {map[interface{}]string{ + 1: "1", + 0.9: "0.9", + -1: "-1", + -0.1: "-0.1", + }, []float64{-0.1, -1, 0.9, 1}, false}, + + {testing.T{}, nil, false}, + {&testing.T{}, nil, false}, + + // errors + {int(123), nil, true}, + {uint16(123), nil, true}, + {float64(12.3), nil, true}, + {func() {}, nil, true}, + {nil, nil, true}, + {[]string{"a", "b", "c"}, nil, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.SliceFloat64E(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestSliceStringE(t *testing.T) { + tests := []struct { + input interface{} + expect []string + isErr bool + }{ + {[]int{}, nil, false}, + {[]int{1, 2, 3}, []string{"1", "2", "3"}, false}, + {[]float64{1, 2, 3}, []string{"1", "2", "3"}, false}, + {[]float64{1.1, 2.2, 3.0}, []string{"1.1", "2.2", "3"}, false}, + {[]string{}, nil, false}, + {[]interface{}{1, "-1.1", -1.7, nil}, []string{"1", "-1.1", "-1.7", ""}, false}, + {[...]string{}, nil, false}, + {[...]string{"1.01", "2.22", "3.30", "-1"}, []string{"1.01", "2.22", "3.30", "-1"}, false}, + {[]string{"a", "b", "c"}, []string{"a", "b", "c"}, false}, + + // sorted by key asc + {map[int]string{}, nil, false}, + {map[int]string{2: "222", 1: "11.1"}, []string{"11.1", "222"}, false}, + {map[int]TestStructC{1: {"C12"}}, []string{"C12"}, false}, + + {testing.T{}, nil, false}, + {&testing.T{}, nil, false}, + + // errors + {int(123), nil, true}, + {uint16(123), nil, true}, + {float64(12.3), nil, true}, + {func() {}, nil, true}, + {nil, nil, true}, + {[]interface{}{testing.T{}}, nil, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.SliceStringE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestColumnsE(t *testing.T) { + tests := []struct { + input interface{} + field interface{} + expect interface{} + isErr bool + }{ + {[]interface{}{TestStructE{D1: 1, DD: &TestStructD{D1: 2}}}, "D1", []interface{}{1}, false}, + {[]TestStructE{{D1: 1}, {D1: 2}}, "D1", []interface{}{1, 2}, false}, + {[]TestStructE{{DD: &TestStructD{}}, {D1: 2}}, "DD", []interface{}{&TestStructD{}, (*TestStructD)(nil)}, false}, + {[]interface{}{TestStructE{D1: 1, DD: &TestStructD{D1: 2}}}, "DD", []interface{}{&TestStructD{D1: 2}}, false}, + {[]map[string]interface{}{{"1": 111, "DDD": "D1"}, {"2": 222, "DDD": "D2"}, {"DDD": nil}}, "DDD", []interface{}{"D1", "D2", nil}, false}, + {map[int]map[string]interface{}{1: {"1": 111, "DDD": "D1"}, 2: {"2": 222, "DDD": "D2"}, 3: {"DDD": nil}}, "DDD", []interface{}{"D1", "D2", nil}, false}, + {map[int]TestStructD{1: {11}, 2: {22}}, "D1", []interface{}{11, 22}, false}, + + // errors + {TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "", nil, true}, + {TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "Age", nil, true}, + {int(123), "Name", nil, true}, + {uint16(123), "Name", nil, true}, + {float64(12.3), "Name", nil, true}, + {"Name", "Name", nil, true}, + {func() {}, "Name", nil, true}, + {nil, "Name", nil, true}, + {TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "D1", nil, true}, + {TestStructE{D1: 1, DD: &TestStructD{D1: 2}}, "DD", nil, true}, + {TestStructB{B1: 1, TestStructC: TestStructC{C1: "c1"}}, "C1", nil, true}, + {map[int]interface{}{123: "112233"}, "123", nil, true}, + {map[int]interface{}{123: "112233"}, 123, nil, true}, + {map[string]interface{}{"123": "112233"}, 123, nil, true}, + {map[string]interface{}{"c": "ccc"}, TestStructC{C1: "c"}, nil, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf( + "i = %d, input[%+v], field[%s], expect[%+v], isErr[%v]", + i, tt.input, tt.field, tt.expect, tt.isErr, + ) + + v, err := cvt.ColumnsE(tt.input, tt.field) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestKeysE(t *testing.T) { + tests := []struct { + input interface{} + expect []interface{} + isErr bool + }{ + {map[int]map[string]interface{}{1: {"1": 111, "DDD": 12.3}, 2: {"2": 222, "DDD": "321"}, 3: {"DDD": nil}}, []interface{}{1, 2, 3}, false}, + {map[string]interface{}{"A": 1, "2": 2}, []interface{}{"2", "A"}, false}, + {map[float64]TestStructD{1: {11}, 2: {22}}, []interface{}{float64(1), float64(2)}, false}, + {map[interface{}]interface{}{1: 1, 2.2: 2.22, "A": "A"}, []interface{}{1, 2.2, "A"}, false}, + + // errors + {nil, nil, true}, + {"Name", nil, true}, + {testing.T{}, nil, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf( + "i = %d, input[%+v], expect[%+v], isErr[%v]", + i, tt.input, tt.expect, tt.isErr, + ) + + v, err := cvt.KeysE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + } +} diff --git a/string.go b/string.go new file mode 100644 index 0000000..b75d9cc --- /dev/null +++ b/string.go @@ -0,0 +1,62 @@ +package cvt + +import ( + "encoding/json" + "fmt" + "strconv" +) + +// String convert an interface to a string type, with default value +func String(v interface{}, def ...string) string { + if v, err := StringE(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return "" +} + +// StringE convert an interface to a string type +func StringE(val interface{}) (string, error) { + v, _, rv := indirect(val) + + // interface implements + switch vv := val.(type) { + case fmt.Stringer: + return vv.String(), nil + case error: + return vv.Error(), nil + case json.Marshaler: + vvv, e := vv.MarshalJSON() + if e == nil { + return string(vvv), nil + } + } + + // source type + switch vv := v.(type) { + case nil: + return "", nil + case bool: + return strconv.FormatBool(vv), nil + case string: + return vv, nil + case []byte: + return string(vv), nil + case []rune: + return string(vv), nil + case uint, uint8, uint16, uint32, uint64, uintptr: + return strconv.FormatUint(rv.Uint(), 10), nil + case int, int8, int16, int32, int64: + return strconv.FormatInt(rv.Int(), 10), nil + case float64: + return strconv.FormatFloat(vv, 'f', -1, 64), nil + case float32: + return strconv.FormatFloat(float64(vv), 'f', -1, 32), nil + } + + return "", newErr(val, "string") +} diff --git a/string_test.go b/string_test.go new file mode 100644 index 0000000..444d35b --- /dev/null +++ b/string_test.go @@ -0,0 +1,159 @@ +package cvt_test + +import ( + "errors" + "fmt" + "html/template" + "math/big" + "testing" + "time" + + "github.com/shockerli/cvt" + "github.com/stretchr/testify/assert" +) + +func TestString_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def string + expect string + }{ + // supported value, def is not used, def != expect + {uint64(8), "xxx", "8"}, + {float32(8.31), "xxx", "8.31"}, + {float64(-8.31), "xxx", "-8.31"}, + {true, "xxx", "true"}, + {int64(-8), "xxx", "-8"}, + {[]byte("8.01"), "xxx", "8.01"}, + {[]rune("我❤️中国"), "xxx", "我❤️中国"}, + {nil, "xxx", ""}, + {aliasTypeInt_0, "xxx", "0"}, + {&aliasTypeString_8d15_minus, "xxx", "-8.15"}, + {aliasTypeBool_true, "xxx", "true"}, + {errors.New("errors"), "xxx", "errors"}, + {time.Friday, "xxx", "Friday"}, + {big.NewInt(123), "xxx", "123"}, + {TestMarshalJSON{}, "xxx", "MarshalJSON"}, + {&TestMarshalJSON{}, "xxx", "MarshalJSON"}, + + // unsupported value, def == expect + {testing.T{}, "xxx", "xxx"}, + {&testing.T{}, "xxx", "xxx"}, + {[]int{}, "xxx", "xxx"}, + {[]string{}, "xxx", "xxx"}, + {[...]string{}, "xxx", "xxx"}, + {map[int]string{}, "xxx", "xxx"}, + } + + 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.String(tt.input, tt.def) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestString_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect string + }{ + {testing.T{}, ""}, + {&testing.T{}, ""}, + {[]int{}, ""}, + {[]string{}, ""}, + {[...]string{}, ""}, + {map[int]string{}, ""}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + + v := cvt.String(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestStringE(t *testing.T) { + tests := []struct { + input interface{} + expect string + isErr bool + }{ + {int(8), "8", false}, + {int8(8), "8", false}, + {int16(8), "8", false}, + {int32(8), "8", false}, + {int64(8), "8", false}, + {uint(8), "8", false}, + {uint8(8), "8", false}, + {uint16(8), "8", false}, + {uint32(8), "8", false}, + {uint64(8), "8", false}, + {float32(8.31), "8.31", false}, + {float64(8.31), "8.31", false}, + {true, "true", false}, + {false, "false", false}, + {int(-8), "-8", false}, + {int8(-8), "-8", false}, + {int16(-8), "-8", false}, + {int32(-8), "-8", false}, + {int64(-8), "-8", false}, + {float32(-8.31), "-8.31", false}, + {float64(-8.31), "-8.31", false}, + {[]byte("-8"), "-8", false}, + {[]byte("-8.01"), "-8.01", false}, + {[]byte("8"), "8", false}, + {[]byte("8.00"), "8.00", false}, + {[]byte("8.01"), "8.01", false}, + {[]rune("我❤️中国"), "我❤️中国", false}, + {nil, "", false}, + {aliasTypeInt_0, "0", false}, + {&aliasTypeInt_0, "0", false}, + {aliasTypeInt_1, "1", false}, + {&aliasTypeInt_1, "1", false}, + {aliasTypeString_0, "0", false}, + {&aliasTypeString_0, "0", false}, + {aliasTypeString_1, "1", false}, + {&aliasTypeString_1, "1", false}, + {aliasTypeString_8d15, "8.15", false}, + {&aliasTypeString_8d15, "8.15", false}, + {aliasTypeString_8d15_minus, "-8.15", false}, + {&aliasTypeString_8d15_minus, "-8.15", false}, + {aliasTypeBool_true, "true", false}, + {&aliasTypeBool_true, "true", false}, + {aliasTypeBool_false, "false", false}, + {&aliasTypeBool_false, "false", false}, + {errors.New("errors"), "errors", false}, + {time.Friday, "Friday", false}, + {big.NewInt(123), "123", false}, + {TestMarshalJSON{}, "MarshalJSON", false}, + {&TestMarshalJSON{}, "MarshalJSON", false}, + {template.URL("http://host.foo"), "http://host.foo", false}, + + // errors + {testing.T{}, "", true}, + {&testing.T{}, "", true}, + {[]int{}, "", true}, + {[]string{}, "", true}, + {[...]string{}, "", true}, + {map[int]string{}, "", true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%v], isErr[%v]", i, tt.input, tt.expect, tt.isErr) + + v, err := cvt.StringE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v, msg) + + // Non-E test with no default value: + v = cvt.String(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} diff --git a/time.go b/time.go new file mode 100644 index 0000000..72b8feb --- /dev/null +++ b/time.go @@ -0,0 +1,86 @@ +package cvt + +import ( + "fmt" + "time" +) + +// Time convert an interface to a time.Time type, with default value +func Time(v interface{}, def ...time.Time) time.Time { + if v, err := TimeE(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return time.Time{} +} + +// TimeE convert an interface to a time.Time type +func TimeE(val interface{}) (t time.Time, err error) { + v, _, _ := indirect(val) + + // source type + switch vv := v.(type) { + case time.Time: + return vv, nil + case string: + return parseDate(vv) + case int, int32, int64, uint, uint32, uint64: + return time.Unix(Int64(vv), 0), nil + } + + // interface implements + switch vv := val.(type) { + case fmt.Stringer: + return parseDate(vv.String()) + } + + return time.Time{}, newErr(val, "time.Time") +} + +func parseDate(s string) (t time.Time, err error) { + fs := []string{ + time.RFC3339, + "2006-01-02T15:04:05", // iso8601 without timezone + time.RFC1123Z, + time.RFC1123, + time.RFC822Z, + time.RFC822, + time.RFC850, + time.ANSIC, + time.UnixDate, + time.RubyDate, + "2006-01-02 15:04:05.999999999 -0700 MST", // Time.String() + "2006-01-02", + "02 Jan 2006", + "2006-01-02T15:04:05-0700", // RFC3339 without timezone hh:mm colon + "2006-01-02 15:04:05 -07:00", + "2006-01-02 15:04:05 -0700", + "2006-01-02 15:04:05Z07:00", // RFC3339 without T + "2006-01-02 15:04:05Z0700", // RFC3339 without T or timezone hh:mm colon + "2006-01-02 15:04:05", + "2006.01.02", + "2006.01.02 15:04:05", + "2006/01/02", + "2006/01/02 15:04:05", + "2006年01月02日", + "2006年01月02日 15:04:05", + "2006年01月02日 15时04分05秒", + time.Kitchen, + time.Stamp, + time.StampMilli, + time.StampMicro, + time.StampNano, + } + + for _, dateType := range fs { + if t, err = time.Parse(dateType, s); err == nil { + return + } + } + + return t, fmt.Errorf("unable to parse date: %s", s) +} diff --git a/time_test.go b/time_test.go new file mode 100644 index 0000000..0323e28 --- /dev/null +++ b/time_test.go @@ -0,0 +1,125 @@ +package cvt_test + +import ( + "fmt" + "testing" + "time" + + "github.com/shockerli/cvt" + "github.com/stretchr/testify/assert" +) + +func TestTime_HasDefault(t *testing.T) { + loc := time.UTC + tests := []struct { + input interface{} + def time.Time + expect time.Time + }{ + // supported value, def is not used, def != expect + {"2018-10-21T23:21:29+0200", time.Date(2010, 4, 23, 11, 11, 11, 0, loc), time.Date(2018, 10, 21, 21, 21, 29, 0, loc)}, + + // unsupported value, def == expect + {"hello world", time.Date(2010, 4, 23, 11, 11, 11, 0, loc), time.Date(2010, 4, 23, 11, 11, 11, 0, loc)}, + } + + 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.Time(tt.input, tt.def) + assert.Equal(t, tt.expect, v.UTC(), msg) + } +} + +func TestTime_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect time.Time + }{ + {"hello world", time.Time{}}, + {testing.T{}, time.Time{}}, + } + + for i, tt := range tests { + msg := fmt.Sprintf("i = %d, input[%+v], expect[%+v]", i, tt.input, tt.expect) + + v := cvt.Time(tt.input) + assert.Equal(t, tt.expect, v, msg) + } +} + +func TestTimeE(t *testing.T) { + loc := time.UTC + + tests := []struct { + input interface{} + expect time.Time + isErr bool + }{ + {"2009-11-10 23:00:00 +0000 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // Time.String() + {"Tue Nov 10 23:00:00 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // ANSIC + {"Tue Nov 10 23:00:00 UTC 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // UnixDate + {"Tue Nov 10 23:00:00 +0000 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RubyDate + {"10 Nov 09 23:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC822 + {"10 Nov 09 23:00 +0000", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC822Z + {"Tuesday, 10-Nov-09 23:00:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC850 + {"Tue, 10 Nov 2009 23:00:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC1123 + {"Tue, 10 Nov 2009 23:00:00 +0000", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC1123Z + {"2009-11-10T23:00:00Z", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC3339 + {"2018-10-21T23:21:29+0200", time.Date(2018, 10, 21, 21, 21, 29, 0, loc), false}, // RFC3339 without timezone hh:mm colon + {"2009-11-10T23:00:00Z", time.Date(2009, 11, 10, 23, 0, 0, 0, loc), false}, // RFC3339Nano + {"11:00PM", time.Date(0, 1, 1, 23, 0, 0, 0, loc), false}, // Kitchen + {"Nov 10 23:00:00", time.Date(0, 11, 10, 23, 0, 0, 0, loc), false}, // Stamp + {"Nov 10 23:00:00.000", time.Date(0, 11, 10, 23, 0, 0, 0, loc), false}, // StampMilli + {"Nov 10 23:00:00.000000", time.Date(0, 11, 10, 23, 0, 0, 0, loc), false}, // StampMicro + {"Nov 10 23:00:00.000000000", time.Date(0, 11, 10, 23, 0, 0, 0, loc), false}, // StampNano + {"2016-03-06 15:28:01-00:00", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, // RFC3339 without T + {"2016-03-06 15:28:01-0000", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, // RFC3339 without T or timezone hh:mm colon + {"2016-03-06 15:28:01", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, + {"2016-03-06 15:28:01 -0000", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, + {"2016-03-06 15:28:01 -00:00", time.Date(2016, 3, 6, 15, 28, 1, 0, loc), false}, + {"2006-01-02", time.Date(2006, 1, 2, 0, 0, 0, 0, loc), false}, + {"02 Jan 2006", time.Date(2006, 1, 2, 0, 0, 0, 0, loc), false}, + {"2010.03.07", time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, + {"2010.03.07 18:08:18", time.Date(2010, 3, 7, 18, 8, 18, 0, loc), false}, + {"2010/03/07", time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, + {"2010/03/07 18:08:18", time.Date(2010, 3, 7, 18, 8, 18, 0, loc), false}, + {"2010年03月07日", time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, + {"2010年03月07日 18:08:18", time.Date(2010, 3, 7, 18, 8, 18, 0, loc), false}, + {"2010年03月07日 18时08分18秒", time.Date(2010, 3, 7, 18, 8, 18, 0, loc), false}, + {1472574600, time.Date(2016, 8, 30, 16, 30, 0, 0, loc), false}, + {int(1482597504), time.Date(2016, 12, 24, 16, 38, 24, 0, loc), false}, + {int64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, + {int32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, + {uint(1482597504), time.Date(2016, 12, 24, 16, 38, 24, 0, loc), false}, + {uint64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, + {uint32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, + {time.Date(2009, 2, 13, 23, 31, 30, 0, loc), time.Date(2009, 2, 13, 23, 31, 30, 0, loc), false}, + {TestTimeStringer{time.Date(2010, 3, 7, 0, 0, 0, 0, loc)}, time.Date(2010, 3, 7, 0, 0, 0, 0, loc), false}, + + // errors + {"2006", time.Time{}, true}, + {"hello world", time.Time{}, true}, + {testing.T{}, time.Time{}, true}, + } + + for i, tt := range tests { + msg := fmt.Sprintf( + "i = %d, input[%+v], expect[%+v], isErr[%v]", + i, tt.input, tt.expect, tt.isErr, + ) + + v, err := cvt.TimeE(tt.input) + if tt.isErr { + assert.Error(t, err, msg) + continue + } + + assert.NoError(t, err, msg) + assert.Equal(t, tt.expect, v.UTC(), v, msg) + + // Non-E test + v = cvt.Time(tt.input) + assert.Equal(t, tt.expect, v.UTC(), msg) + } +}