From d4a16534f280ae0077439c94287bc3ca676280de Mon Sep 17 00:00:00 2001 From: Nothin <55579792+Nothing-no@users.noreply.github.com> Date: Mon, 17 Apr 2023 10:04:31 +0800 Subject: [PATCH 1/3] add typemap (#85) * [FEATURE] typemap, quick map any type to specified type * [DOC] add more test case --------- Co-authored-by: zhijian.chen --- .gitignore | 3 +- README.md | 50 +++++++++++ README_zh-CN.md | 46 ++++++++++ go.mod | 7 ++ go.sum | 16 ++++ typemap/typemap.go | 180 ++++++++++++++++++++++++++++++++++++++++ typemap/typemap_test.go | 121 +++++++++++++++++++++++++++ 7 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 typemap/typemap.go create mode 100644 typemap/typemap_test.go diff --git a/.gitignore b/.gitignore index d1f189b..5c8a8c5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ fileutil/*.zip fileutil/*.link fileutil/unzip/* slice/testdata/* -cryptor/*.pem \ No newline at end of file +cryptor/*.pem +test \ No newline at end of file diff --git a/README.md b/README.md index 527b438..221eb5d 100644 --- a/README.md +++ b/README.md @@ -1340,8 +1340,58 @@ import "github.com/duke-git/lancet/v2/xerror" [[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#TryUnwrap)] [[play](https://go.dev/play/p/acyZVkNZEeW)] +### 22. quick map any to struct or any base type +```go +import "github.com/duke-git/lancet/v2/typemap" +``` +#### Example +```go +type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } +) + +func main() { + v := map[string]interface{}{ + "person":map[string]interface{}{ + "name":"Nothin", + "age":123, + "phone":"123421312", + "address":map[string]interface{}{ + "street":"test", + "number":1, + }, + }, + "other":1234, + } + + var person Person + err :=typemap.MapTo(v["person"],&person) + if err != nil { + //error handler ... + } + + log.Println(person) +} + + +``` + + ## How to Contribute + + + I really appreciate any code commits which make lancet lib powerful. Please follow the rules below to create your pull request. 1. Fork the repository. diff --git a/README_zh-CN.md b/README_zh-CN.md index f07e1b2..2af0bf9 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -1341,6 +1341,52 @@ import "github.com/duke-git/lancet/v2/xerror" [[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#TryUnwrap)] [[play](https://go.dev/play/p/acyZVkNZEeW)] + + +### 22. [typemap] 快速将map或者其他类型映射到结构体或者指定类型 +```go +import "github.com/duke-git/lancet/v2/typemap" +``` +#### Example +```go +type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } +) + +func main() { + v := map[string]interface{}{ + "person":map[string]interface{}{ + "name":"Nothin", + "age":123, + "phone":"123421312", + "address":map[string]interface{}{ + "street":"test", + "number":1, + }, + }, + "other":1234, + } + + var person Person + err :=typemap.MapTo(v["person"],&person) + if err != nil { + //error handler ... + } + + log.Println(person) +} + + ## 如何贡献代码 非常感激任何的代码提交以使 lancet 的功能越来越强大。创建 pull request 时请遵守以下规则。 diff --git a/go.mod b/go.mod index 9e6c3ae..1958f5a 100644 --- a/go.mod +++ b/go.mod @@ -6,3 +6,10 @@ require ( golang.org/x/exp v0.0.0-20221208152030-732eee02a75a golang.org/x/text v0.5.0 ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index a12ea5d..c2121dd 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,20 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw= golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/typemap/typemap.go b/typemap/typemap.go new file mode 100644 index 0000000..9dc87a4 --- /dev/null +++ b/typemap/typemap.go @@ -0,0 +1,180 @@ +package typemap + +import ( + "fmt" + "reflect" + "strings" +) + +var mapUtils = map[reflect.Kind]func(reflect.Value, reflect.Value) error{ + reflect.String: convertNormal, + reflect.Int: convertNormal, + reflect.Int16: convertNormal, + reflect.Int32: convertNormal, + reflect.Int64: convertNormal, + reflect.Uint: convertNormal, + reflect.Uint16: convertNormal, + reflect.Uint32: convertNormal, + reflect.Uint64: convertNormal, + reflect.Float32: convertNormal, + reflect.Float64: convertNormal, + reflect.Uint8: convertNormal, + reflect.Int8: convertNormal, + reflect.Struct: convertNormal, + reflect.Complex64: convertNormal, + reflect.Complex128: convertNormal, +} + +var _ = func() struct{} { + mapUtils[reflect.Map] = convertMap + mapUtils[reflect.Array] = convertSlice + mapUtils[reflect.Slice] = convertSlice + return struct{}{} +}() + +// MapTo try to map any interface to struct or base type +/* + Eg: + v := map[string]interface{}{ + "service":map[string]interface{}{ + "ip":"127.0.0.1", + "port":1234, + }, + version:"v1.0.01" + } + + type Target struct { + Service struct { + Ip string `json:"ip"` + Port int `json:"port"` + } `json:"service"` + Ver string `json:"version"` + } + + var dist Target + if err := typemap.MapTo(v,&dist); err != nil { + log.Println(err) + return + } + + log.Println(dist) + +*/ +func MapTo(src any, dst any) error { + + dstRef := reflect.ValueOf(dst) + if dstRef.Kind() != reflect.Ptr { + return fmt.Errorf("dst is not ptr") + } + + dstRef = reflect.Indirect(dstRef) + + srcRef := reflect.ValueOf(src) + if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface { + srcRef = srcRef.Elem() + } + if f, ok := mapUtils[srcRef.Kind()]; ok { + return f(srcRef, dstRef) + } + + return fmt.Errorf("no implemented:%s", srcRef.Type()) +} + +func convertNormal(src reflect.Value, dst reflect.Value) error { + if dst.CanSet() { + if src.Type() == dst.Type() { + dst.Set(src) + } else if src.CanConvert(dst.Type()) { + dst.Set(src.Convert(dst.Type())) + } else { + return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String()) + } + } + return nil +} + +func convertSlice(src reflect.Value, dst reflect.Value) error { + if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice { + return fmt.Errorf("error type:%s", dst.Type().String()) + } + l := src.Len() + target := reflect.MakeSlice(dst.Type(), l, l) + if dst.CanSet() { + dst.Set(target) + } + for i := 0; i < l; i++ { + srcValue := src.Index(i) + if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface { + srcValue = srcValue.Elem() + } + if f, ok := mapUtils[srcValue.Kind()]; ok { + err := f(srcValue, dst.Index(i)) + if err != nil { + return err + } + } + } + + return nil +} + +func convertMap(src reflect.Value, dst reflect.Value) error { + if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct { + if src.Kind() == reflect.Interface { + return convertMap(src.Elem(), dst) + } else { + return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String()) + } + } + dstType := dst.Type() + num := dstType.NumField() + exist := map[string]int{} + for i := 0; i < num; i++ { + k := dstType.Field(i).Tag.Get("json") + if k == "" { + k = dstType.Field(i).Name + } + if strings.Contains(k, ",") { + taglist := strings.Split(k, ",") + if taglist[0] == "" { + + k = dstType.Field(i).Name + } else { + k = taglist[0] + + } + + } + exist[k] = i + } + + keys := src.MapKeys() + for _, key := range keys { + if index, ok := exist[key.String()]; ok { + v := dst.Field(index) + if v.Kind() == reflect.Struct { + err := convertMap(src.MapIndex(key), v) + if err != nil { + return err + } + } else { + if v.CanSet() { + if v.Type() == src.MapIndex(key).Elem().Type() { + v.Set(src.MapIndex(key).Elem()) + } else if src.MapIndex(key).Elem().CanConvert(v.Type()) { + v.Set(src.MapIndex(key).Elem().Convert(v.Type())) + } else if f, ok := mapUtils[src.MapIndex(key).Elem().Kind()]; ok && f != nil { + err := f(src.MapIndex(key).Elem(), v) + if err != nil { + return err + } + } else { + return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type()) + } + } + } + } + } + + return nil +} diff --git a/typemap/typemap_test.go b/typemap/typemap_test.go new file mode 100644 index 0000000..a817f9d --- /dev/null +++ b/typemap/typemap_test.go @@ -0,0 +1,121 @@ +package typemap + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } +) + +func TestTransform(t *testing.T) { + src := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(src, &p) + assert.NoError(t, err) + assert.Equal(t, src["name"], p.Name) + assert.Equal(t, src["age"], p.Age) + assert.Equal(t, src["phone"], p.Phone) + assert.Equal(t, "test", p.Addr.Street) + assert.Equal(t, 1, p.Addr.Number) +} + +func TestBaseType(t *testing.T) { + tc := map[string]interface{}{ + "i32": -32, + "i8": -8, + "i16": -16, + "i64": -64, + "i": -1, + "u32": 32, + "u8": 8, + "u16": 16, + "u64": 64, + "u": 1, + "tf": true, + "f32": 1.32, + "f64": 1.64, + "str": "hello mapto", + "complex": 1 + 3i, + } + + type BaseType struct { + I int `json:"i"` + I8 int8 `json:"i8"` + I16 int16 `json:"i16"` + I32 int32 `json:"i32"` + I64 int64 `json:"i64"` + U uint `json:"u"` + U8 uint8 `json:"u8"` + U16 uint16 `json:"u16"` + U32 uint32 `json:"u32"` + U64 uint64 `json:"u64"` + F32 float32 `json:"f32"` + F64 float64 `json:"f64"` + Tf bool `json:"tf"` + Str string `json:"str"` + Comp complex128 `json:"complex"` + } + var dist BaseType + err := MapTo(tc, &dist) + assert.NoError(t, err) + assert.EqualValues(t, -1, dist.I) + assert.EqualValues(t, -8, dist.I8) + assert.EqualValues(t, -16, dist.I16) + assert.EqualValues(t, -32, dist.I32) + assert.EqualValues(t, -64, dist.I64) + assert.EqualValues(t, 1, dist.U) + assert.EqualValues(t, 8, dist.U8) + assert.EqualValues(t, 16, dist.U16) + assert.EqualValues(t, 32, dist.U32) + assert.EqualValues(t, 64, dist.U64) + assert.EqualValues(t, tc["f32"], dist.F32) + assert.EqualValues(t, tc["f64"], dist.F64) + assert.EqualValues(t, tc["str"], dist.Str) + assert.EqualValues(t, tc["tf"], dist.Tf) + assert.EqualValues(t, tc["complex"], dist.Comp) + + var number float64 + + MapTo(tc["i"], &number) + assert.EqualValues(t, -1, number) + MapTo(tc["i8"], &number) + assert.EqualValues(t, -8, number) + MapTo(tc["i16"], &number) + assert.EqualValues(t, -16, number) + MapTo(tc["i32"], &number) + assert.EqualValues(t, -32, number) + MapTo(tc["i64"], &number) + assert.EqualValues(t, -64, number) + MapTo(tc["u"], &number) + assert.EqualValues(t, 1, number) + MapTo(tc["u8"], &number) + assert.EqualValues(t, 8, number) + MapTo(tc["u16"], &number) + assert.EqualValues(t, 16, number) + MapTo(tc["u32"], &number) + assert.EqualValues(t, 32, number) + MapTo(tc["u64"], &number) + assert.EqualValues(t, 64, number) +} From 5b11a8b45725e627343b7cd5db88cc68a650eb61 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Mon, 17 Apr 2023 10:36:59 +0800 Subject: [PATCH 2/3] refactor: move typemap.go to maputil package and add document for it --- README.md | 47 +--------- README_zh-CN.md | 48 +--------- docs/maputil.md | 75 ++++++++++++--- docs/maputil_zh-CN.md | 73 ++++++++++++--- go.mod | 7 -- go.sum | 16 ---- internal/assert.go | 47 ++++++++++ maputil/map_example_test.go | 36 ++++++++ typemap/typemap.go | 180 ------------------------------------ typemap/typemap_test.go | 121 ------------------------ 10 files changed, 211 insertions(+), 439 deletions(-) delete mode 100644 typemap/typemap.go delete mode 100644 typemap/typemap_test.go diff --git a/README.md b/README.md index 221eb5d..3d427aa 100644 --- a/README.md +++ b/README.md @@ -598,6 +598,8 @@ import "github.com/duke-git/lancet/v2/maputil" #### Function list: +- **MapTo** : quick map any value to struct or any base type. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#MapTo)] - **ForEach** : executes iteratee funcation for every key and value pair in map. [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#ForEach)] [[play](https://go.dev/play/p/OaThj6iNVXK)] @@ -1340,51 +1342,6 @@ import "github.com/duke-git/lancet/v2/xerror" [[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#TryUnwrap)] [[play](https://go.dev/play/p/acyZVkNZEeW)] -### 22. quick map any to struct or any base type -```go -import "github.com/duke-git/lancet/v2/typemap" -``` -#### Example -```go -type ( - Person struct { - Name string `json:"name"` - Age int `json:"age"` - Phone string `json:"phone"` - Addr Address `json:"address"` - } - - Address struct { - Street string `json:"street"` - Number int `json:"number"` - } -) - -func main() { - v := map[string]interface{}{ - "person":map[string]interface{}{ - "name":"Nothin", - "age":123, - "phone":"123421312", - "address":map[string]interface{}{ - "street":"test", - "number":1, - }, - }, - "other":1234, - } - - var person Person - err :=typemap.MapTo(v["person"],&person) - if err != nil { - //error handler ... - } - - log.Println(person) -} - - -``` ## How to Contribute diff --git a/README_zh-CN.md b/README_zh-CN.md index 2af0bf9..9b0cda2 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -597,6 +597,8 @@ import "github.com/duke-git/lancet/v2/maputil" #### 函数列表: +- **MapTo** : 快速将map或者其他类型映射到结构体或者指定类型。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#MapTo)] - **ForEach** : 对 map 中的每对 key 和 value 执行 iteratee 函数。 [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#ForEach)] [[play](https://go.dev/play/p/OaThj6iNVXK)] @@ -1341,52 +1343,6 @@ import "github.com/duke-git/lancet/v2/xerror" [[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#TryUnwrap)] [[play](https://go.dev/play/p/acyZVkNZEeW)] - - -### 22. [typemap] 快速将map或者其他类型映射到结构体或者指定类型 -```go -import "github.com/duke-git/lancet/v2/typemap" -``` -#### Example -```go -type ( - Person struct { - Name string `json:"name"` - Age int `json:"age"` - Phone string `json:"phone"` - Addr Address `json:"address"` - } - - Address struct { - Street string `json:"street"` - Number int `json:"number"` - } -) - -func main() { - v := map[string]interface{}{ - "person":map[string]interface{}{ - "name":"Nothin", - "age":123, - "phone":"123421312", - "address":map[string]interface{}{ - "street":"test", - "number":1, - }, - }, - "other":1234, - } - - var person Person - err :=typemap.MapTo(v["person"],&person) - if err != nil { - //error handler ... - } - - log.Println(person) -} - - ## 如何贡献代码 非常感激任何的代码提交以使 lancet 的功能越来越强大。创建 pull request 时请遵守以下规则。 diff --git a/docs/maputil.md b/docs/maputil.md index e70733e..fbcc6f5 100644 --- a/docs/maputil.md +++ b/docs/maputil.md @@ -22,6 +22,7 @@ import ( ## Index +- [MapTo](#MapTo) - [ForEach](#ForEach) - [Filter](#Filter) - [FilterByKeys](#FilterByKeys) @@ -47,6 +48,64 @@ import ( ## Documentation + +### MapTo + +

Rry to map any interface to struct or base type.

+ +Signature: + +```go +func MapTo(src any, dst any) error +``` + +Example: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } + ) + + personInfo := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(personInfo, &p) + + fmt.Println(err) + fmt.Println(p) + + // Output: + // + // {Nothin 28 123456789 {test 1}} +} +``` + ### ForEach

Executes iteratee funcation for every key and value pair in map.

@@ -123,7 +182,7 @@ func main() { maputil.Filter(m, func(_ string, value int) { sum += value }) - + result := maputil.Filter(m, isEven) fmt.Println(result) @@ -133,7 +192,6 @@ func main() { } ``` - ### FilterByKeys

Iterates over map, return a new map whose keys are all given keys.

@@ -172,7 +230,6 @@ func main() { } ``` - ### FilterByValues

Iterates over map, return a new map whose values are all given values.

@@ -211,7 +268,6 @@ func main() { } ``` - ### OmitBy

OmitBy is the opposite of Filter, removes all the map elements for which the predicate function returns true.

@@ -253,7 +309,6 @@ func main() { } ``` - ### OmitByKeys

The opposite of FilterByKeys, extracts all the map elements which keys are not omitted.

@@ -292,7 +347,6 @@ func main() { } ``` - ### OmitByValues

The opposite of FilterByValues. remov all elements whose value are in the give slice.

@@ -331,7 +385,6 @@ func main() { } ``` - ### Intersect

Iterates over maps, return a new map of key and value pairs in all given maps.

@@ -419,7 +472,7 @@ func main() { keys := maputil.Keys(m) sort.Ints(keys) - + fmt.Println(keys) // Output: @@ -498,7 +551,7 @@ func main() { "b": 22, "d": 33, } - + result := maputil.Minus(m1, m2) fmt.Println(result) @@ -638,7 +691,6 @@ func main() { } ``` - ### MapKeys

Transforms a map to other type map by manipulating it's keys.

@@ -717,7 +769,6 @@ func main() { } ``` - ### Entry

Transforms a map into array of key/value pairs.

@@ -763,7 +814,6 @@ func main() { } ``` - ### FromEntries

Creates a map based on a slice of key/value pairs.

@@ -841,7 +891,6 @@ func main() { } ``` - ### IsDisjoint

Checks two maps are disjoint if they have no keys in common

diff --git a/docs/maputil_zh-CN.md b/docs/maputil_zh-CN.md index 84a713d..dc012c5 100644 --- a/docs/maputil_zh-CN.md +++ b/docs/maputil_zh-CN.md @@ -22,6 +22,7 @@ import ( ## 目录: +- [MapTo](#MapTo) - [ForEach](#ForEach) - [Filter](#Filter) - [FilterByKeys](#FilterByKeys) @@ -47,6 +48,63 @@ import ( ## API 文档: +### MapTo + +

快速将map或者其他类型映射到结构体或者指定类型。

+ +函数签名: + +```go +func MapTo(src any, dst any) error +``` + +示例: + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } + ) + + personInfo := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(personInfo, &p) + + fmt.Println(err) + fmt.Println(p) + + // Output: + // + // {Nothin 28 123456789 {test 1}} +} +``` + ### ForEach

对map中的每对key和value执行iteratee函数

@@ -80,7 +138,7 @@ func main() { maputil.ForEach(m, func(_ string, value int) { sum += value }) - + fmt.Println(sum) // Output: @@ -123,7 +181,7 @@ func main() { maputil.Filter(m, func(_ string, value int) { sum += value }) - + result := Filter(m, isEven) fmt.Println(result) @@ -171,7 +229,6 @@ func main() { } ``` - ### FilterByValues

迭代map, 返回一个新map,其value都是给定的value值。

@@ -210,7 +267,6 @@ func main() { } ``` - ### OmitBy

Filter的反向操作, 迭代map中的每对key和value, 删除符合predicate函数的key, value, 返回新map。

@@ -252,7 +308,6 @@ func main() { } ``` - ### OmitByKeys

FilterByKeys的反向操作, 迭代map, 返回一个新map,其key不包括给定的key值。

@@ -291,7 +346,6 @@ func main() { } ``` - ### OmitByValues

FilterByValues的反向操作, 迭代map, 返回一个新map,其value不包括给定的value值。

@@ -416,7 +470,7 @@ func main() { keys := maputil.Keys(m) sort.Ints(keys) - + fmt.Println(keys) // Output: @@ -453,7 +507,7 @@ func main() { 1: "1", 3: "2", } - + result := maputil.Merge(m1, m2) fmt.Println(result) @@ -632,7 +686,6 @@ func main() { } ``` - ### MapKeys

操作map的每个key,然后转为新的map。

@@ -711,7 +764,6 @@ func main() { } ``` - ### Entry

将map转换为键/值对切片。

@@ -757,7 +809,6 @@ func main() { } ``` - ### FromEntries

基于键/值对的切片创建map。

diff --git a/go.mod b/go.mod index 1958f5a..9e6c3ae 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,3 @@ require ( golang.org/x/exp v0.0.0-20221208152030-732eee02a75a golang.org/x/text v0.5.0 ) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.8.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/go.sum b/go.sum index c2121dd..a12ea5d 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,4 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw= golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/assert.go b/internal/assert.go index 5f4cd16..d51af3c 100644 --- a/internal/assert.go +++ b/internal/assert.go @@ -5,6 +5,7 @@ package internal import ( + "bytes" "fmt" "reflect" "runtime" @@ -44,6 +45,52 @@ func (a *Assert) NotEqual(expected, actual any) { } } +// EqualValues asserts that two objects are equal or convertable to the same types and equal. +// https://github.com/stretchr/testify/assert/assertions.go +func (a *Assert) EqualValues(expected, actual any) { + if !objectsAreEqualValues(expected, actual) { + makeTestFailed(a.T, a.CaseName, expected, actual) + } +} + +func objectsAreEqualValues(expected, actual interface{}) bool { + if objectsAreEqual(expected, actual) { + return true + } + + actualType := reflect.TypeOf(actual) + if actualType == nil { + return false + } + expectedValue := reflect.ValueOf(expected) + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + // Attempt comparison after type conversion + return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) + } + + return false +} + +func objectsAreEqual(expected, actual interface{}) bool { + if expected == nil || actual == nil { + return expected == actual + } + + exp, ok := expected.([]byte) + if !ok { + return reflect.DeepEqual(expected, actual) + } + + act, ok := actual.([]byte) + if !ok { + return false + } + if exp == nil || act == nil { + return exp == nil && act == nil + } + return bytes.Equal(exp, act) +} + // Greater check if expected is greate than actual func (a *Assert) Greater(expected, actual any) { if compare(expected, actual) != compareGreater { diff --git a/maputil/map_example_test.go b/maputil/map_example_test.go index 3909a14..3d9b784 100644 --- a/maputil/map_example_test.go +++ b/maputil/map_example_test.go @@ -397,3 +397,39 @@ func ExampleOmitByValues() { // Output: // map[a:1 b:2 c:3] } + +func ExampleMapTo() { + type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } + ) + + personInfo := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(personInfo, &p) + + fmt.Println(err) + fmt.Println(p) + + // Output: + // + // {Nothin 28 123456789 {test 1}} +} diff --git a/typemap/typemap.go b/typemap/typemap.go deleted file mode 100644 index 9dc87a4..0000000 --- a/typemap/typemap.go +++ /dev/null @@ -1,180 +0,0 @@ -package typemap - -import ( - "fmt" - "reflect" - "strings" -) - -var mapUtils = map[reflect.Kind]func(reflect.Value, reflect.Value) error{ - reflect.String: convertNormal, - reflect.Int: convertNormal, - reflect.Int16: convertNormal, - reflect.Int32: convertNormal, - reflect.Int64: convertNormal, - reflect.Uint: convertNormal, - reflect.Uint16: convertNormal, - reflect.Uint32: convertNormal, - reflect.Uint64: convertNormal, - reflect.Float32: convertNormal, - reflect.Float64: convertNormal, - reflect.Uint8: convertNormal, - reflect.Int8: convertNormal, - reflect.Struct: convertNormal, - reflect.Complex64: convertNormal, - reflect.Complex128: convertNormal, -} - -var _ = func() struct{} { - mapUtils[reflect.Map] = convertMap - mapUtils[reflect.Array] = convertSlice - mapUtils[reflect.Slice] = convertSlice - return struct{}{} -}() - -// MapTo try to map any interface to struct or base type -/* - Eg: - v := map[string]interface{}{ - "service":map[string]interface{}{ - "ip":"127.0.0.1", - "port":1234, - }, - version:"v1.0.01" - } - - type Target struct { - Service struct { - Ip string `json:"ip"` - Port int `json:"port"` - } `json:"service"` - Ver string `json:"version"` - } - - var dist Target - if err := typemap.MapTo(v,&dist); err != nil { - log.Println(err) - return - } - - log.Println(dist) - -*/ -func MapTo(src any, dst any) error { - - dstRef := reflect.ValueOf(dst) - if dstRef.Kind() != reflect.Ptr { - return fmt.Errorf("dst is not ptr") - } - - dstRef = reflect.Indirect(dstRef) - - srcRef := reflect.ValueOf(src) - if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface { - srcRef = srcRef.Elem() - } - if f, ok := mapUtils[srcRef.Kind()]; ok { - return f(srcRef, dstRef) - } - - return fmt.Errorf("no implemented:%s", srcRef.Type()) -} - -func convertNormal(src reflect.Value, dst reflect.Value) error { - if dst.CanSet() { - if src.Type() == dst.Type() { - dst.Set(src) - } else if src.CanConvert(dst.Type()) { - dst.Set(src.Convert(dst.Type())) - } else { - return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String()) - } - } - return nil -} - -func convertSlice(src reflect.Value, dst reflect.Value) error { - if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice { - return fmt.Errorf("error type:%s", dst.Type().String()) - } - l := src.Len() - target := reflect.MakeSlice(dst.Type(), l, l) - if dst.CanSet() { - dst.Set(target) - } - for i := 0; i < l; i++ { - srcValue := src.Index(i) - if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface { - srcValue = srcValue.Elem() - } - if f, ok := mapUtils[srcValue.Kind()]; ok { - err := f(srcValue, dst.Index(i)) - if err != nil { - return err - } - } - } - - return nil -} - -func convertMap(src reflect.Value, dst reflect.Value) error { - if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct { - if src.Kind() == reflect.Interface { - return convertMap(src.Elem(), dst) - } else { - return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String()) - } - } - dstType := dst.Type() - num := dstType.NumField() - exist := map[string]int{} - for i := 0; i < num; i++ { - k := dstType.Field(i).Tag.Get("json") - if k == "" { - k = dstType.Field(i).Name - } - if strings.Contains(k, ",") { - taglist := strings.Split(k, ",") - if taglist[0] == "" { - - k = dstType.Field(i).Name - } else { - k = taglist[0] - - } - - } - exist[k] = i - } - - keys := src.MapKeys() - for _, key := range keys { - if index, ok := exist[key.String()]; ok { - v := dst.Field(index) - if v.Kind() == reflect.Struct { - err := convertMap(src.MapIndex(key), v) - if err != nil { - return err - } - } else { - if v.CanSet() { - if v.Type() == src.MapIndex(key).Elem().Type() { - v.Set(src.MapIndex(key).Elem()) - } else if src.MapIndex(key).Elem().CanConvert(v.Type()) { - v.Set(src.MapIndex(key).Elem().Convert(v.Type())) - } else if f, ok := mapUtils[src.MapIndex(key).Elem().Kind()]; ok && f != nil { - err := f(src.MapIndex(key).Elem(), v) - if err != nil { - return err - } - } else { - return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type()) - } - } - } - } - } - - return nil -} diff --git a/typemap/typemap_test.go b/typemap/typemap_test.go deleted file mode 100644 index a817f9d..0000000 --- a/typemap/typemap_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package typemap - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -type ( - Person struct { - Name string `json:"name"` - Age int `json:"age"` - Phone string `json:"phone"` - Addr Address `json:"address"` - } - - Address struct { - Street string `json:"street"` - Number int `json:"number"` - } -) - -func TestTransform(t *testing.T) { - src := map[string]interface{}{ - "name": "Nothin", - "age": 28, - "phone": "123456789", - "address": map[string]interface{}{ - "street": "test", - "number": 1, - }, - } - - var p Person - err := MapTo(src, &p) - assert.NoError(t, err) - assert.Equal(t, src["name"], p.Name) - assert.Equal(t, src["age"], p.Age) - assert.Equal(t, src["phone"], p.Phone) - assert.Equal(t, "test", p.Addr.Street) - assert.Equal(t, 1, p.Addr.Number) -} - -func TestBaseType(t *testing.T) { - tc := map[string]interface{}{ - "i32": -32, - "i8": -8, - "i16": -16, - "i64": -64, - "i": -1, - "u32": 32, - "u8": 8, - "u16": 16, - "u64": 64, - "u": 1, - "tf": true, - "f32": 1.32, - "f64": 1.64, - "str": "hello mapto", - "complex": 1 + 3i, - } - - type BaseType struct { - I int `json:"i"` - I8 int8 `json:"i8"` - I16 int16 `json:"i16"` - I32 int32 `json:"i32"` - I64 int64 `json:"i64"` - U uint `json:"u"` - U8 uint8 `json:"u8"` - U16 uint16 `json:"u16"` - U32 uint32 `json:"u32"` - U64 uint64 `json:"u64"` - F32 float32 `json:"f32"` - F64 float64 `json:"f64"` - Tf bool `json:"tf"` - Str string `json:"str"` - Comp complex128 `json:"complex"` - } - var dist BaseType - err := MapTo(tc, &dist) - assert.NoError(t, err) - assert.EqualValues(t, -1, dist.I) - assert.EqualValues(t, -8, dist.I8) - assert.EqualValues(t, -16, dist.I16) - assert.EqualValues(t, -32, dist.I32) - assert.EqualValues(t, -64, dist.I64) - assert.EqualValues(t, 1, dist.U) - assert.EqualValues(t, 8, dist.U8) - assert.EqualValues(t, 16, dist.U16) - assert.EqualValues(t, 32, dist.U32) - assert.EqualValues(t, 64, dist.U64) - assert.EqualValues(t, tc["f32"], dist.F32) - assert.EqualValues(t, tc["f64"], dist.F64) - assert.EqualValues(t, tc["str"], dist.Str) - assert.EqualValues(t, tc["tf"], dist.Tf) - assert.EqualValues(t, tc["complex"], dist.Comp) - - var number float64 - - MapTo(tc["i"], &number) - assert.EqualValues(t, -1, number) - MapTo(tc["i8"], &number) - assert.EqualValues(t, -8, number) - MapTo(tc["i16"], &number) - assert.EqualValues(t, -16, number) - MapTo(tc["i32"], &number) - assert.EqualValues(t, -32, number) - MapTo(tc["i64"], &number) - assert.EqualValues(t, -64, number) - MapTo(tc["u"], &number) - assert.EqualValues(t, 1, number) - MapTo(tc["u8"], &number) - assert.EqualValues(t, 8, number) - MapTo(tc["u16"], &number) - assert.EqualValues(t, 16, number) - MapTo(tc["u32"], &number) - assert.EqualValues(t, 32, number) - MapTo(tc["u64"], &number) - assert.EqualValues(t, 64, number) -} From d7976e31a40c7565a39e7804c4483f1c4155117a Mon Sep 17 00:00:00 2001 From: dudaodong Date: Mon, 17 Apr 2023 10:37:56 +0800 Subject: [PATCH 3/3] refactor: move typemap.go to maputil package and add document for it --- maputil/typemap.go | 180 ++++++++++++++++++++++++++++++++++++++++ maputil/typemap_test.go | 111 +++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 maputil/typemap.go create mode 100644 maputil/typemap_test.go diff --git a/maputil/typemap.go b/maputil/typemap.go new file mode 100644 index 0000000..ab3c731 --- /dev/null +++ b/maputil/typemap.go @@ -0,0 +1,180 @@ +package maputil + +import ( + "fmt" + "reflect" + "strings" +) + +var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{ + reflect.String: convertNormal, + reflect.Int: convertNormal, + reflect.Int16: convertNormal, + reflect.Int32: convertNormal, + reflect.Int64: convertNormal, + reflect.Uint: convertNormal, + reflect.Uint16: convertNormal, + reflect.Uint32: convertNormal, + reflect.Uint64: convertNormal, + reflect.Float32: convertNormal, + reflect.Float64: convertNormal, + reflect.Uint8: convertNormal, + reflect.Int8: convertNormal, + reflect.Struct: convertNormal, + reflect.Complex64: convertNormal, + reflect.Complex128: convertNormal, +} + +var _ = func() struct{} { + mapHandlers[reflect.Map] = convertMap + mapHandlers[reflect.Array] = convertSlice + mapHandlers[reflect.Slice] = convertSlice + return struct{}{} +}() + +// MapTo try to map any interface to struct or base type +/* + Eg: + v := map[string]interface{}{ + "service":map[string]interface{}{ + "ip":"127.0.0.1", + "port":1234, + }, + version:"v1.0.01" + } + + type Target struct { + Service struct { + Ip string `json:"ip"` + Port int `json:"port"` + } `json:"service"` + Ver string `json:"version"` + } + + var dist Target + if err := typemap.MapTo(v,&dist); err != nil { + log.Println(err) + return + } + + log.Println(dist) + +*/ +func MapTo(src any, dst any) error { + + dstRef := reflect.ValueOf(dst) + if dstRef.Kind() != reflect.Ptr { + return fmt.Errorf("dst is not ptr") + } + + dstRef = reflect.Indirect(dstRef) + + srcRef := reflect.ValueOf(src) + if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface { + srcRef = srcRef.Elem() + } + if f, ok := mapHandlers[srcRef.Kind()]; ok { + return f(srcRef, dstRef) + } + + return fmt.Errorf("no implemented:%s", srcRef.Type()) +} + +func convertNormal(src reflect.Value, dst reflect.Value) error { + if dst.CanSet() { + if src.Type() == dst.Type() { + dst.Set(src) + } else if src.CanConvert(dst.Type()) { + dst.Set(src.Convert(dst.Type())) + } else { + return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String()) + } + } + return nil +} + +func convertSlice(src reflect.Value, dst reflect.Value) error { + if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice { + return fmt.Errorf("error type:%s", dst.Type().String()) + } + l := src.Len() + target := reflect.MakeSlice(dst.Type(), l, l) + if dst.CanSet() { + dst.Set(target) + } + for i := 0; i < l; i++ { + srcValue := src.Index(i) + if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface { + srcValue = srcValue.Elem() + } + if f, ok := mapHandlers[srcValue.Kind()]; ok { + err := f(srcValue, dst.Index(i)) + if err != nil { + return err + } + } + } + + return nil +} + +func convertMap(src reflect.Value, dst reflect.Value) error { + if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct { + if src.Kind() == reflect.Interface { + return convertMap(src.Elem(), dst) + } else { + return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String()) + } + } + dstType := dst.Type() + num := dstType.NumField() + exist := map[string]int{} + for i := 0; i < num; i++ { + k := dstType.Field(i).Tag.Get("json") + if k == "" { + k = dstType.Field(i).Name + } + if strings.Contains(k, ",") { + taglist := strings.Split(k, ",") + if taglist[0] == "" { + + k = dstType.Field(i).Name + } else { + k = taglist[0] + + } + + } + exist[k] = i + } + + keys := src.MapKeys() + for _, key := range keys { + if index, ok := exist[key.String()]; ok { + v := dst.Field(index) + if v.Kind() == reflect.Struct { + err := convertMap(src.MapIndex(key), v) + if err != nil { + return err + } + } else { + if v.CanSet() { + if v.Type() == src.MapIndex(key).Elem().Type() { + v.Set(src.MapIndex(key).Elem()) + } else if src.MapIndex(key).Elem().CanConvert(v.Type()) { + v.Set(src.MapIndex(key).Elem().Convert(v.Type())) + } else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil { + err := f(src.MapIndex(key).Elem(), v) + if err != nil { + return err + } + } else { + return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type()) + } + } + } + } + } + + return nil +} diff --git a/maputil/typemap_test.go b/maputil/typemap_test.go new file mode 100644 index 0000000..356453a --- /dev/null +++ b/maputil/typemap_test.go @@ -0,0 +1,111 @@ +package maputil + +import ( + "testing" + + "github.com/duke-git/lancet/v2/internal" +) + +type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Addr Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } +) + +func TestStructType(t *testing.T) { + assert := internal.NewAssert(t, "TestStructType") + + src := map[string]interface{}{ + "name": "Nothin", + "age": 28, + "phone": "123456789", + "address": map[string]interface{}{ + "street": "test", + "number": 1, + }, + } + + var p Person + err := MapTo(src, &p) + assert.IsNil(err) + assert.Equal(src["name"], p.Name) + assert.Equal(src["age"], p.Age) + assert.Equal(src["phone"], p.Phone) + assert.Equal("test", p.Addr.Street) + assert.Equal(1, p.Addr.Number) +} + +func TestBaseType(t *testing.T) { + assert := internal.NewAssert(t, "TestBaseType") + + tc := map[string]interface{}{ + "i32": -32, + "i8": -8, + "i16": -16, + "i64": -64, + "i": -1, + "u32": 32, + "u8": 8, + "u16": 16, + "u64": 64, + "u": 1, + "tf": true, + "f32": 1.32, + "f64": 1.64, + "str": "hello mapto", + "complex": 1 + 3i, + } + + type BaseType struct { + I int `json:"i"` + I8 int8 `json:"i8"` + I16 int16 `json:"i16"` + I32 int32 `json:"i32"` + I64 int64 `json:"i64"` + U uint `json:"u"` + U8 uint8 `json:"u8"` + U16 uint16 `json:"u16"` + U32 uint32 `json:"u32"` + U64 uint64 `json:"u64"` + F32 float32 `json:"f32"` + F64 float64 `json:"f64"` + Tf bool `json:"tf"` + Str string `json:"str"` + Comp complex128 `json:"complex"` + } + + var dist BaseType + err := MapTo(tc, &dist) + assert.IsNil(err) + + var number float64 + + MapTo(tc["i"], &number) + assert.EqualValues(-1, number) + MapTo(tc["i8"], &number) + assert.EqualValues(-8, number) + MapTo(tc["i16"], &number) + assert.EqualValues(-16, number) + MapTo(tc["i32"], &number) + assert.EqualValues(-32, number) + MapTo(tc["i64"], &number) + assert.EqualValues(-64, number) + MapTo(tc["u"], &number) + assert.EqualValues(1, number) + MapTo(tc["u8"], &number) + assert.EqualValues(8, number) + MapTo(tc["u16"], &number) + assert.EqualValues(16, number) + MapTo(tc["u32"], &number) + assert.EqualValues(32, number) + MapTo(tc["u64"], &number) + assert.EqualValues(64, number) +}