diff --git a/docs/api/packages/datetime.md b/docs/api/packages/datetime.md index c224fac..e808f7b 100644 --- a/docs/api/packages/datetime.md +++ b/docs/api/packages/datetime.md @@ -66,6 +66,7 @@ import ( - [TimestampNano](#TimestampNano) - [TrackFuncTime](#TrackFuncTime) - [DaysBetween](#DaysBetween) +- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
diff --git a/docs/en/api/packages/datetime.md b/docs/en/api/packages/datetime.md index 9222db1..cf6066c 100644 --- a/docs/en/api/packages/datetime.md +++ b/docs/en/api/packages/datetime.md @@ -66,6 +66,8 @@ import ( - [TimestampMicro](#TimestampMicro) - [TimestampNano](#TimestampNano) - [TrackFuncTime](#TrackFuncTime) +- [GenerateDatetimesBetween](#GenerateDatetimesBetween) +
diff --git a/docs/en/api/packages/maputil.md b/docs/en/api/packages/maputil.md index 243e55a..69abc7c 100644 --- a/docs/en/api/packages/maputil.md +++ b/docs/en/api/packages/maputil.md @@ -7,6 +7,9 @@ Package maputil includes some functions to manipulate map. ## Source: - [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go) +- [https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go](https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go) +- [https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go](https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go) +- [https://github.com/duke-git/lancet/blob/main/maputil/typemap.go](https://github.com/duke-git/lancet/blob/main/maputil/typemap.go)
diff --git a/maputil/map.go b/maputil/map.go index e60222e..b70edbc 100644 --- a/maputil/map.go +++ b/maputil/map.go @@ -473,3 +473,187 @@ func SortByKey[K constraints.Ordered, V any](m map[K]V, less func(a, b K) bool) return } + +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 := maputil.MapTo(v,&dist); err != nil { + log.Println(err) + return + } + + log.Println(dist) + +*/ +// Play: https://go.dev/play/p/4K7KBEPgS5M +func MapTo(src any, dst any) error { + dstRef := reflect.ValueOf(dst) + + if dstRef.Kind() != reflect.Ptr { + return fmt.Errorf("dst is not ptr") + } + + dstElem := dstRef.Type().Elem() + if dstElem.Kind() == reflect.Struct { + srcMap := src.(map[string]interface{}) + return MapToStruct(srcMap, dst) + } + + 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 && dst.IsValid() { + 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/map_test.go b/maputil/map_test.go index 90b33bb..a78b818 100644 --- a/maputil/map_test.go +++ b/maputil/map_test.go @@ -751,3 +751,121 @@ func TestSortByKey(t *testing.T) { assert.Equal(expected2, result2) } + +type ( + Person struct { + Name string `json:"name"` + Age int `json:"age"` + Phone string `json:"phone"` + Address *Address `json:"address"` + } + + Address struct { + Street string `json:"street"` + Number int `json:"number"` + } +) + +func TestStructType(t *testing.T) { + t.Parallel() + + 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.Address.Street) + assert.Equal(1, p.Address.Number) +} + +func TestBaseType(t *testing.T) { + t.Parallel() + + 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) +} diff --git a/maputil/typemap.go b/maputil/typemap.go deleted file mode 100644 index cb20961..0000000 --- a/maputil/typemap.go +++ /dev/null @@ -1,191 +0,0 @@ -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 := maputil.MapTo(v,&dist); err != nil { - log.Println(err) - return - } - - log.Println(dist) - -*/ -// Play: https://go.dev/play/p/4K7KBEPgS5M -func MapTo(src any, dst any) error { - dstRef := reflect.ValueOf(dst) - - if dstRef.Kind() != reflect.Ptr { - return fmt.Errorf("dst is not ptr") - } - - dstElem := dstRef.Type().Elem() - if dstElem.Kind() == reflect.Struct { - srcMap := src.(map[string]interface{}) - return MapToStruct(srcMap, dst) - } - - 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 && dst.IsValid() { - 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 deleted file mode 100644 index cba9218..0000000 --- a/maputil/typemap_test.go +++ /dev/null @@ -1,125 +0,0 @@ -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"` - Address *Address `json:"address"` - } - - Address struct { - Street string `json:"street"` - Number int `json:"number"` - } -) - -func TestStructType(t *testing.T) { - t.Parallel() - - 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.Address.Street) - assert.Equal(1, p.Address.Number) -} - -func TestBaseType(t *testing.T) { - t.Parallel() - - 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) -}