From 21a699cfff10905fdce7eabd5b6626772138a0ff Mon Sep 17 00:00:00 2001 From: shockerli Date: Tue, 6 Sep 2022 17:20:24 +0800 Subject: [PATCH] Support functions: Field/SliceInt/SliceInt64/SliceFloat64/SliceString --- cvte.go | 15 +- doc/content/en/type/others.md | 33 ++++ doc/content/en/type/slice.md | 33 ++-- doc/content/en/usage/getting-started.md | 6 + doc/content/zh-cn/develop/testing.md | 12 ++ doc/content/zh-cn/type/others.md | 34 +++++ doc/content/zh-cn/type/slice.md | 33 ++-- doc/content/zh-cn/usage/getting-started.md | 7 + slice.go | 76 ++++++++-- slice_test.go | 168 +++++++++++++++++++++ 10 files changed, 366 insertions(+), 51 deletions(-) create mode 100644 doc/content/en/type/others.md create mode 100644 doc/content/zh-cn/develop/testing.md create mode 100644 doc/content/zh-cn/type/others.md diff --git a/cvte.go b/cvte.go index fe3a853..996dd1c 100644 --- a/cvte.go +++ b/cvte.go @@ -15,7 +15,20 @@ var formatOutOfLimitInt = "%w, out of max limit value(%d)" var formatOutOfLimitFloat = "%w, out of max limit value(%f)" var formatExtend = "%v, %w" -// FieldE return the field value from map/struct, ignore the filed type +// Field return the field value from map/struct, with default value +func Field(v interface{}, field interface{}, def ...interface{}) interface{} { + if v, err := FieldE(v, field); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return nil +} + +// FieldE return the field value from map/struct, ignore the field type func FieldE(val interface{}, field interface{}) (interface{}, error) { if val == nil { return nil, errUnsupportedTypeNil diff --git a/doc/content/en/type/others.md b/doc/content/en/type/others.md new file mode 100644 index 0000000..4ac92ac --- /dev/null +++ b/doc/content/en/type/others.md @@ -0,0 +1,33 @@ +--- +title: Others +weight: 80 +--- + + +{{< toc >}} + +## Field +Reference method `FieldE`. + +## FieldE +Return the field value from map/struct, `interface{}`. + +```go +// map +cvt.FieldE(map[int]interface{}{123: "112233"}, "123") // "112233" +cvt.FieldE(map[string]interface{}{"123": "112233"}, "123") // "112233" + +// struct +cvt.FieldE(struct{ + A string + B int +}{"Hello", 18}, "A") // "Hello" +cvt.FieldE(struct{ + A string + B int +}{"Hello", 18}, "B") // 18 +``` + + +> 更多示例请看单元测试:`cvte_test.go` + diff --git a/doc/content/en/type/slice.md b/doc/content/en/type/slice.md index 71dfe1a..6bc77bf 100644 --- a/doc/content/en/type/slice.md +++ b/doc/content/en/type/slice.md @@ -35,25 +35,6 @@ cvt.ColumnsE(map[int]TestStructD{1: {11}, 2: {22}}, "D1") cvt.ColumnsE([]TestStructE{{D1: 1}, {D1: 2}}, "D1") ``` -## FieldE -Return the field value from map/struct, `interface{}`. - -```go -// map -cvt.FieldE(map[int]interface{}{123: "112233"}, "123") // "112233" -cvt.FieldE(map[string]interface{}{"123": "112233"}, "123") // "112233" - -// struct -cvt.FieldE(struct{ - A string - B int -}{"Hello", 18}, "A") // "Hello" -cvt.FieldE(struct{ - A string - B int -}{"Hello", 18}, "B") // 18 -``` - ## KeysE Return the keys of map, or fields of struct, `[]interface{}`. @@ -83,7 +64,7 @@ cvt.KeysE(struct{ ## Slice -Convert an interface to a `[]interface{}` type. +Reference method `SliceE`. ## SliceE Convert an interface to a `[]interface{}` type. @@ -104,6 +85,9 @@ cvt.SliceE(TestStruct{18,"jhon"}) // []interface{}{18, "jhon"} ``` +## SliceInt +Reference method `SliceIntE`. + ## SliceIntE Convert an interface to a `[]int` type. @@ -112,6 +96,9 @@ cvt.SliceIntE([]string{"1", "2", "3"}) // []int{1, 2, 3} cvt.SliceIntE(map[int]string{2: "222", 1: "111"}) // []int{111, 222} ``` +## SliceInt64 +Reference method `SliceInt64E`. + ## SliceInt64E Convert an interface to a `[]int64` type. @@ -121,6 +108,9 @@ cvt.SliceInt64E(map[int]string{2: "222", 1: "111"}) // []int64{111, 222} ``` +## SliceFloat64 +Reference method `SliceFloat64E`. + ## SliceFloat64E Convert an interface to a `[]float64` type. @@ -129,6 +119,9 @@ cvt.SliceFloat64E([]string{"1", "2", "3"}) // []float64{1, 2, 3} cvt.SliceFloat64E(map[int]string{2: "222", 1: "111"}) // []float64{111, 222} ``` +## SliceString +Reference method `SliceStringE`. + ## SliceStringE Convert an interface to a `[]string` type. diff --git a/doc/content/en/usage/getting-started.md b/doc/content/en/usage/getting-started.md index ba3afc3..e1d09d8 100644 --- a/doc/content/en/usage/getting-started.md +++ b/doc/content/en/usage/getting-started.md @@ -5,6 +5,12 @@ weight: -20 Simple, safe conversion of any type, including indirect/custom types. + +{{< hint type=warning >}} +**Document statement**\ +`__()` and `__P()` are convenient method is based on `__E()`, so the method of use and the type of support please reference ` __E () ` document. +{{< /hint >}} + {{< toc >}} diff --git a/doc/content/zh-cn/develop/testing.md b/doc/content/zh-cn/develop/testing.md new file mode 100644 index 0000000..d5167d3 --- /dev/null +++ b/doc/content/zh-cn/develop/testing.md @@ -0,0 +1,12 @@ +--- +title: 测试 +weight: 20 +--- + +## 命名约定 + +- `TestXXX_HasDefault`:测试带有默认值的方法 +- `TestXXX_BaseLine`:测试没有带默认值的无错误方法 +- `TestXXXE`:测试返回错误信息的方法 +- `TestXXXP`:测试返回数据指针的方法 + diff --git a/doc/content/zh-cn/type/others.md b/doc/content/zh-cn/type/others.md new file mode 100644 index 0000000..348c08b --- /dev/null +++ b/doc/content/zh-cn/type/others.md @@ -0,0 +1,34 @@ +--- +title: Others +weight: 80 +--- + + +{{< toc >}} + + +## Field +参考 `FieldE` 方法。 + +## FieldE +取 `map` 或 `struct` 的字段值,返回 `interface{}`。 + +```go +// map +cvt.FieldE(map[int]interface{}{123: "112233"}, "123") // "112233" +cvt.FieldE(map[string]interface{}{"123": "112233"}, "123") // "112233" + +// struct +cvt.FieldE(struct{ + A string + B int +}{"Hello", 18}, "A") // "Hello" +cvt.FieldE(struct{ + A string + B int +}{"Hello", 18}, "B") // 18 +``` + + +> 更多示例请看单元测试:`cvte_test.go` + diff --git a/doc/content/zh-cn/type/slice.md b/doc/content/zh-cn/type/slice.md index da2ba1e..9e629a7 100644 --- a/doc/content/zh-cn/type/slice.md +++ b/doc/content/zh-cn/type/slice.md @@ -38,25 +38,6 @@ cvt.ColumnsE(map[int]TestStructD{1: {11}, 2: {22}}, "D1") cvt.ColumnsE([]TestStructE{{D1: 1}, {D1: 2}}, "D1") ``` -## FieldE -取 `map` 或 `struct` 的字段值,返回 `interface{}`。 - -```go -// map -cvt.FieldE(map[int]interface{}{123: "112233"}, "123") // "112233" -cvt.FieldE(map[string]interface{}{"123": "112233"}, "123") // "112233" - -// struct -cvt.FieldE(struct{ - A string - B int -}{"Hello", 18}, "A") // "Hello" -cvt.FieldE(struct{ - A string - B int -}{"Hello", 18}, "B") // 18 -``` - ## KeysE 取 `map` 的键名,或结构体的字段名,返回 `[]interface{}`。 @@ -86,7 +67,7 @@ cvt.KeysE(struct{ ## Slice -`SliceE` 方法的默认值版本 +参考 `SliceE` 方法。 ## SliceE 转换成 `[]interface{}` @@ -107,6 +88,9 @@ cvt.SliceE(TestStruct{18,"jhon"}) // []interface{}{18, "jhon"} ``` +## SliceInt +参考 `SliceIntE` 方法。 + ## SliceIntE 转换成 `[]int` @@ -115,6 +99,9 @@ cvt.SliceIntE([]string{"1", "2", "3"}) // []int{1, 2, 3} cvt.SliceIntE(map[int]string{2: "222", 1: "111"}) // []int{111, 222} ``` +## SliceInt64 +参考 `SliceInt64E` 方法。 + ## SliceInt64E 转换成 `[]int64` @@ -124,6 +111,9 @@ cvt.SliceInt64E(map[int]string{2: "222", 1: "111"}) // []int64{111, 222} ``` +## SliceFloat64 +参考 `SliceFloat64E` 方法。 + ## SliceFloat64E 转换成 `[]float64` @@ -132,6 +122,9 @@ cvt.SliceFloat64E([]string{"1", "2", "3"}) // []float64{1, 2, 3} cvt.SliceFloat64E(map[int]string{2: "222", 1: "111"}) // []float64{111, 222} ``` +## SliceString +参考 `SliceStringE` 方法。 + ## SliceStringE 转换成 `[]string` diff --git a/doc/content/zh-cn/usage/getting-started.md b/doc/content/zh-cn/usage/getting-started.md index 2ca05cf..f3f1515 100644 --- a/doc/content/zh-cn/usage/getting-started.md +++ b/doc/content/zh-cn/usage/getting-started.md @@ -6,6 +6,12 @@ weight: -20 简单、安全、高效的转换任意数据类型的 Go 语言工具包,支持自定义类型、提取结构体字段和值。 +{{< hint type=warning >}} +**文档说明**\ +`__()`、`__P()` 均是以其对应的 `__E()` 为基础的便捷方法,故其使用方法及支持的类型请参考 `__E()` 的文档,大部分此类便利方法均不再详细阐述。 +{{< /hint >}} + + {{< toc >}} @@ -75,6 +81,7 @@ cvt.Float("hello", 12.34) // 12.34 cvt.BoolP("true") // (*bool)(0x14000126180)(true) ``` + ### 更多示例 > 上千个单元测试用例,覆盖率近100%,所有示例可通过单元测试了解:`*_test.go` diff --git a/slice.go b/slice.go index 7424136..506ded1 100644 --- a/slice.go +++ b/slice.go @@ -21,7 +21,7 @@ func Slice(v interface{}, def ...[]interface{}) []interface{} { // SliceE convert an interface to a []interface{} type func SliceE(val interface{}) (sl []interface{}, err error) { if val == nil { - return nil, errUnsupportedTypeNil + return sl, errUnsupportedTypeNil } _, rv := indirect(val) @@ -47,7 +47,20 @@ func SliceE(val interface{}) (sl []interface{}, err error) { return } - return nil, newErr(val, "slice") + return sl, newErr(val, "slice") +} + +// SliceInt convert an interface to a []int type, with default value +func SliceInt(v interface{}, def ...[]int) []int { + if v, err := SliceIntE(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return nil } // SliceIntE convert an interface to a []int type @@ -57,10 +70,11 @@ func SliceIntE(val interface{}) (sl []int, err error) { return } + var vv int for _, v := range list { - vv, err := IntE(v) + vv, err = IntE(v) if err != nil { - return nil, err + return } sl = append(sl, vv) } @@ -68,6 +82,19 @@ func SliceIntE(val interface{}) (sl []int, err error) { return } +// SliceInt64 convert an interface to a []int64 type, with default value +func SliceInt64(v interface{}, def ...[]int64) []int64 { + if v, err := SliceInt64E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return nil +} + // SliceInt64E convert an interface to a []int64 type func SliceInt64E(val interface{}) (sl []int64, err error) { list, err := SliceE(val) @@ -75,10 +102,11 @@ func SliceInt64E(val interface{}) (sl []int64, err error) { return } + var vv int64 for _, v := range list { - vv, err := Int64E(v) + vv, err = Int64E(v) if err != nil { - return nil, err + return } sl = append(sl, vv) } @@ -86,6 +114,19 @@ func SliceInt64E(val interface{}) (sl []int64, err error) { return } +// SliceFloat64 convert an interface to a []float64 type, with default value +func SliceFloat64(v interface{}, def ...[]float64) []float64 { + if v, err := SliceFloat64E(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return nil +} + // SliceFloat64E convert an interface to a []float64 type func SliceFloat64E(val interface{}) (sl []float64, err error) { list, err := SliceE(val) @@ -93,10 +134,11 @@ func SliceFloat64E(val interface{}) (sl []float64, err error) { return } + var vv float64 for _, v := range list { - vv, err := Float64E(v) + vv, err = Float64E(v) if err != nil { - return nil, err + return } sl = append(sl, vv) } @@ -104,6 +146,19 @@ func SliceFloat64E(val interface{}) (sl []float64, err error) { return } +// SliceString convert an interface to a []string type, with default value +func SliceString(v interface{}, def ...[]string) []string { + if v, err := SliceStringE(v); err == nil { + return v + } + + if len(def) > 0 { + return def[0] + } + + return nil +} + // SliceStringE convert an interface to a []string type func SliceStringE(val interface{}) (sl []string, err error) { list, err := SliceE(val) @@ -111,10 +166,11 @@ func SliceStringE(val interface{}) (sl []string, err error) { return } + var vv string for _, v := range list { - vv, err := StringE(v) + vv, err = StringE(v) if err != nil { - return nil, err + return } sl = append(sl, vv) } diff --git a/slice_test.go b/slice_test.go index b7c78be..4b14416 100644 --- a/slice_test.go +++ b/slice_test.go @@ -112,6 +112,48 @@ func TestSliceE(t *testing.T) { } } +func TestSliceInt_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def []int + expect []int + }{ + // supported value, def is not used, def != expect + {[]int{1, 2, 3}, []int{1, 2}, []int{1, 2, 3}}, + {testing.T{}, []int{1, 2, 3}, nil}, + + // unsupported value, def == expect + {int(123), []int{1}, []int{1}}, + {uint16(123), nil, nil}, + {func() {}, []int{}, []int{}}, + } + + 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.SliceInt(tt.input, tt.def) + assertEqual(t, tt.expect, v, "[NonE] "+msg) + } +} + +func TestSliceInt_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect []int + }{ + {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.SliceInt(tt.input) + assertEqual(t, tt.expect, v, "[NonE] "+msg) + } +} + func TestSliceIntE(t *testing.T) { tests := []struct { input interface{} @@ -162,6 +204,48 @@ func TestSliceIntE(t *testing.T) { } } +func TestSliceInt64_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def []int64 + expect []int64 + }{ + // supported value, def is not used, def != expect + {[]int{1, 2, 3}, []int64{1, 2}, []int64{1, 2, 3}}, + {testing.T{}, []int64{1, 2, 3}, nil}, + + // unsupported value, def == expect + {int(123), []int64{1}, []int64{1}}, + {uint16(123), nil, nil}, + {func() {}, []int64{}, []int64{}}, + } + + 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.SliceInt64(tt.input, tt.def) + assertEqual(t, tt.expect, v, "[NonE] "+msg) + } +} + +func TestSliceInt64_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect []int64 + }{ + {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.SliceInt64(tt.input) + assertEqual(t, tt.expect, v, "[NonE] "+msg) + } +} + func TestSliceInt64E(t *testing.T) { tests := []struct { input interface{} @@ -213,6 +297,48 @@ func TestSliceInt64E(t *testing.T) { } } +func TestSliceFloat64_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def []float64 + expect []float64 + }{ + // supported value, def is not used, def != expect + {[]int{1, 2, 3}, []float64{1, 2}, []float64{1, 2, 3}}, + {testing.T{}, []float64{1, 2, 3}, nil}, + + // unsupported value, def == expect + {int(123), []float64{1}, []float64{1}}, + {uint16(123), nil, nil}, + {func() {}, []float64{}, []float64{}}, + } + + 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.SliceFloat64(tt.input, tt.def) + assertEqual(t, tt.expect, v, "[NonE] "+msg) + } +} + +func TestSliceFloat64_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect []int + }{ + {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.SliceInt(tt.input) + assertEqual(t, tt.expect, v, "[NonE] "+msg) + } +} + func TestSliceFloat64E(t *testing.T) { tests := []struct { input interface{} @@ -265,6 +391,48 @@ func TestSliceFloat64E(t *testing.T) { } } +func TestSliceString_HasDefault(t *testing.T) { + tests := []struct { + input interface{} + def []string + expect []string + }{ + // supported value, def is not used, def != expect + {[]int{1, 2, 3}, []string{"1", "2"}, []string{"1", "2", "3"}}, + {testing.T{}, []string{"1", "2", "3"}, nil}, + + // unsupported value, def == expect + {int(123), []string{"1"}, []string{"1"}}, + {uint16(123), nil, nil}, + {func() {}, []string{}, []string{}}, + } + + 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.SliceString(tt.input, tt.def) + assertEqual(t, tt.expect, v, "[NonE] "+msg) + } +} + +func TestSliceString_BaseLine(t *testing.T) { + tests := []struct { + input interface{} + expect []string + }{ + {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.SliceString(tt.input) + assertEqual(t, tt.expect, v, "[NonE] "+msg) + } +} + func TestSliceStringE(t *testing.T) { tests := []struct { input interface{}