1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 12:52:28 +08:00

Merge branch 'main' into v2

This commit is contained in:
dudaodong
2023-04-17 10:38:28 +08:00
9 changed files with 509 additions and 25 deletions

3
.gitignore vendored
View File

@@ -7,4 +7,5 @@ fileutil/*.zip
fileutil/*.link
fileutil/unzip/*
slice/testdata/*
cryptor/*.pem
cryptor/*.pem
test

View File

@@ -598,6 +598,8 @@ import "github.com/duke-git/lancet/v2/maputil"
#### Function list:
- **<big>MapTo</big>** : quick map any value to struct or any base type.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#MapTo)]
- **<big>ForEach</big>** : 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,8 +1342,13 @@ 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)]
## 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.

View File

@@ -597,6 +597,8 @@ import "github.com/duke-git/lancet/v2/maputil"
#### 函数列表:
- **<big>MapTo</big>** : 快速将map或者其他类型映射到结构体或者指定类型。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#MapTo)]
- **<big>ForEach</big>** : 对 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)]

View File

@@ -22,6 +22,7 @@ import (
## Index
- [MapTo](#MapTo)
- [ForEach](#ForEach)
- [Filter](#Filter)
- [FilterByKeys](#FilterByKeys)
@@ -47,6 +48,64 @@ import (
## Documentation
### <span id="MapTo">MapTo</span>
<p>Rry to map any interface to struct or base type.</p>
<b>Signature:</b>
```go
func MapTo(src any, dst any) error
```
<b>Example:</b>
```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:
// <nil>
// {Nothin 28 123456789 {test 1}}
}
```
### <span id="ForEach">ForEach</span>
<p>Executes iteratee funcation for every key and value pair in map.</p>
@@ -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() {
}
```
### <span id="FilterByKeys">FilterByKeys</span>
<p>Iterates over map, return a new map whose keys are all given keys.</p>
@@ -172,7 +230,6 @@ func main() {
}
```
### <span id="FilterByValues">FilterByValues</span>
<p>Iterates over map, return a new map whose values are all given values.</p>
@@ -211,7 +268,6 @@ func main() {
}
```
### <span id="OmitBy">OmitBy</span>
<p>OmitBy is the opposite of Filter, removes all the map elements for which the predicate function returns true.</p>
@@ -253,7 +309,6 @@ func main() {
}
```
### <span id="OmitByKeys">OmitByKeys</span>
<p>The opposite of FilterByKeys, extracts all the map elements which keys are not omitted.</p>
@@ -292,7 +347,6 @@ func main() {
}
```
### <span id="OmitByValues">OmitByValues</span>
<p>The opposite of FilterByValues. remov all elements whose value are in the give slice.</p>
@@ -331,7 +385,6 @@ func main() {
}
```
### <span id="Intersect">Intersect</span>
<p>Iterates over maps, return a new map of key and value pairs in all given maps.</p>
@@ -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() {
}
```
### <span id="MapKeys">MapKeys</span>
<p>Transforms a map to other type map by manipulating it's keys.</p>
@@ -717,7 +769,6 @@ func main() {
}
```
### <span id="Entry">Entry</span>
<p>Transforms a map into array of key/value pairs.</p>
@@ -763,7 +814,6 @@ func main() {
}
```
### <span id="FromEntries">FromEntries</span>
<p>Creates a map based on a slice of key/value pairs.</p>
@@ -841,7 +891,6 @@ func main() {
}
```
### <span id="IsDisjoint">IsDisjoint</span>
<p>Checks two maps are disjoint if they have no keys in common</p>

View File

@@ -22,6 +22,7 @@ import (
## 目录:
- [MapTo](#MapTo)
- [ForEach](#ForEach)
- [Filter](#Filter)
- [FilterByKeys](#FilterByKeys)
@@ -47,6 +48,63 @@ import (
## API 文档:
### <span id="MapTo">MapTo</span>
<p>快速将map或者其他类型映射到结构体或者指定类型。</p>
<b>函数签名:</b>
```go
func MapTo(src any, dst any) error
```
<b>示例:</b>
```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:
// <nil>
// {Nothin 28 123456789 {test 1}}
}
```
### <span id="ForEach">ForEach</span>
<p>对map中的每对key和value执行iteratee函数</p>
@@ -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() {
}
```
### <span id="FilterByValues">FilterByValues</span>
<p>迭代map, 返回一个新map其value都是给定的value值。</p>
@@ -210,7 +267,6 @@ func main() {
}
```
### <span id="OmitBy">OmitBy</span>
<p>Filter的反向操作, 迭代map中的每对key和value, 删除符合predicate函数的key, value, 返回新map。</p>
@@ -252,7 +308,6 @@ func main() {
}
```
### <span id="OmitByKeys">OmitByKeys</span>
<p>FilterByKeys的反向操作, 迭代map, 返回一个新map其key不包括给定的key值。</p>
@@ -291,7 +346,6 @@ func main() {
}
```
### <span id="OmitByValues">OmitByValues</span>
<p>FilterByValues的反向操作, 迭代map, 返回一个新map其value不包括给定的value值。</p>
@@ -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() {
}
```
### <span id="MapKeys">MapKeys</span>
<p>操作map的每个key然后转为新的map。</p>
@@ -711,7 +764,6 @@ func main() {
}
```
### <span id="Entry">Entry</span>
<p>将map转换为键/值对切片。</p>
@@ -757,7 +809,6 @@ func main() {
}
```
### <span id="FromEntries">FromEntries</span>
<p>基于键/值对的切片创建map。</p>

View File

@@ -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 {

View File

@@ -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:
// <nil>
// {Nothin 28 123456789 {test 1}}
}

180
maputil/typemap.go Normal file
View File

@@ -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
}

111
maputil/typemap_test.go Normal file
View File

@@ -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)
}