mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
4 Commits
5e3337a52e
...
7e85a0ed7d
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e85a0ed7d | |||
| 2268a0312f | |||
| da84d95aa3 | |||
| 48244d6711 |
@@ -411,3 +411,35 @@ func getCallerName() string {
|
|||||||
|
|
||||||
return fullName
|
return fullName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DaysBetween returns the number of days between two times.
|
||||||
|
// Play: todo
|
||||||
|
func DaysBetween(start, end time.Time) int {
|
||||||
|
duration := end.Sub(start)
|
||||||
|
days := int(duration.Hours() / 24)
|
||||||
|
|
||||||
|
return days
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateDatetimesBetween returns a slice of strings between two times.
|
||||||
|
// layout: the format of the datetime string
|
||||||
|
// interval: the interval between two datetimes
|
||||||
|
// Play: todo
|
||||||
|
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error) {
|
||||||
|
var result []string
|
||||||
|
|
||||||
|
if start.After(end) {
|
||||||
|
start, end = end, start
|
||||||
|
}
|
||||||
|
|
||||||
|
duration, err := time.ParseDuration(interval)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for current := start; !current.After(end); current = current.Add(duration) {
|
||||||
|
result = append(result, current.Format(layout))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -409,16 +409,31 @@ func ExampleIsWeekend() {
|
|||||||
// false
|
// false
|
||||||
}
|
}
|
||||||
|
|
||||||
// func ExampleTrackFuncTime() {
|
func ExampleDaysBetween() {
|
||||||
// defer TrackFuncTime(time.Now())()
|
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
// var n int
|
result := DaysBetween(start, end)
|
||||||
// for i := 0; i < 5000000; i++ {
|
|
||||||
// n++
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fmt.Println(1)
|
fmt.Println(result)
|
||||||
|
|
||||||
// // Output:
|
// Output:
|
||||||
// // 1
|
// 9
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
func ExampleGenerateDatetimesBetween() {
|
||||||
|
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
layout := "2006-01-02 15:04:05"
|
||||||
|
interval := "1h"
|
||||||
|
|
||||||
|
result, err := GenerateDatetimesBetween(start, end, layout, interval)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
|
||||||
|
// <nil>
|
||||||
|
}
|
||||||
|
|||||||
@@ -419,3 +419,105 @@ func TestTrackFuncTime(t *testing.T) {
|
|||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDaysBetween(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestDaysBetween")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
start time.Time
|
||||||
|
end time.Time
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
end: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
|
||||||
|
expected: 9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
|
||||||
|
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
expected: -9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
end: time.Date(2024, time.December, 31, 0, 0, 0, 0, time.UTC),
|
||||||
|
expected: 365,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
end: time.Date(2024, time.March, 31, 0, 0, 0, 0, time.UTC),
|
||||||
|
expected: 30,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
result := DaysBetween(tt.start, tt.end)
|
||||||
|
assert.Equal(tt.expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateDatetimesBetween(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestGenerateDatetimesBetween")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
start time.Time
|
||||||
|
end time.Time
|
||||||
|
layout string
|
||||||
|
interval string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
end: time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC),
|
||||||
|
layout: "2006-01-02 15:04:05",
|
||||||
|
interval: "30m",
|
||||||
|
expected: []string{
|
||||||
|
"2024-09-01 00:00:00",
|
||||||
|
"2024-09-01 00:30:00",
|
||||||
|
"2024-09-01 01:00:00",
|
||||||
|
"2024-09-01 01:30:00",
|
||||||
|
"2024-09-01 02:00:00",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
layout: "2006-01-02 15:04:05",
|
||||||
|
interval: "1h",
|
||||||
|
expected: []string{"2024-09-01 00:00:00"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
end: time.Date(2024, time.September, 1, 3, 0, 0, 0, time.UTC),
|
||||||
|
layout: "2006-01-02 15:04:05",
|
||||||
|
interval: "2h",
|
||||||
|
expected: []string{
|
||||||
|
"2024-09-01 00:00:00",
|
||||||
|
"2024-09-01 02:00:00",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
result, err := GenerateDatetimesBetween(tt.start, tt.end, tt.layout, tt.interval)
|
||||||
|
|
||||||
|
assert.Equal(tt.expected, result)
|
||||||
|
assert.IsNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Invalid interval", func(t *testing.T) {
|
||||||
|
_, err := GenerateDatetimesBetween(time.Now(), time.Now(), "2006-01-02 15:04:05", "invalid")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected error, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ import (
|
|||||||
- [TimestampMicro](#TimestampMicro)
|
- [TimestampMicro](#TimestampMicro)
|
||||||
- [TimestampNano](#TimestampNano)
|
- [TimestampNano](#TimestampNano)
|
||||||
- [TrackFuncTime](#TrackFuncTime)
|
- [TrackFuncTime](#TrackFuncTime)
|
||||||
|
- [DaysBetween](#DaysBetween)
|
||||||
|
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -1498,3 +1500,74 @@ func main() {
|
|||||||
fmt.Println(1) // Function main execution time: 1.460287ms
|
fmt.Println(1) // Function main execution time: 1.460287ms
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="DaysBetween">DaysBetween</span>
|
||||||
|
|
||||||
|
<p>返回两个日期之间的天数差。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func DaysBetween(start, end time.Time) int
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datetime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
result := datetime.DaysBetween(start, end)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 9
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
|
||||||
|
|
||||||
|
<p>生成从start到end的所有日期时间的字符串列表。layout参数表示时间格式,例如"2006-01-02 15:04:05",interval参数表示时间间隔,例如"1h"表示1小时,"30m"表示30分钟。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datetime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
layout := "2006-01-02 15:04:05"
|
||||||
|
interval := "1h"
|
||||||
|
|
||||||
|
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
|
||||||
|
// <nil>
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -7,6 +7,8 @@ maputil 包包括一些操作 map 的函数。
|
|||||||
## 源码:
|
## 源码:
|
||||||
|
|
||||||
- [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/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)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ import (
|
|||||||
- [TimestampMicro](#TimestampMicro)
|
- [TimestampMicro](#TimestampMicro)
|
||||||
- [TimestampNano](#TimestampNano)
|
- [TimestampNano](#TimestampNano)
|
||||||
- [TrackFuncTime](#TrackFuncTime)
|
- [TrackFuncTime](#TrackFuncTime)
|
||||||
|
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -1498,3 +1500,74 @@ func main() {
|
|||||||
fmt.Println(1) // Function main execution time: 1.460287ms
|
fmt.Println(1) // Function main execution time: 1.460287ms
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="DaysBetween">DaysBetween</span>
|
||||||
|
|
||||||
|
<p>Returns the number of days between two times.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func DaysBetween(start, end time.Time) int
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datetime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
result := datetime.DaysBetween(start, end)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 9
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
|
||||||
|
|
||||||
|
<p>Returns a slice of strings between two times. `layout`: the format of the datetime string.`interval`: the interval between two datetimes.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datetime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
layout := "2006-01-02 15:04:05"
|
||||||
|
interval := "1h"
|
||||||
|
|
||||||
|
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
|
||||||
|
// <nil>
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -7,6 +7,9 @@ Package maputil includes some functions to manipulate map.
|
|||||||
## Source:
|
## 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/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)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
|||||||
+184
@@ -473,3 +473,187 @@ func SortByKey[K constraints.Ordered, V any](m map[K]V, less func(a, b K) bool)
|
|||||||
|
|
||||||
return
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -751,3 +751,121 @@ func TestSortByKey(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(expected2, result2)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user