Skip to content

Commit 0d793f8

Browse files
committed
feat: 添加映射和字段设置功能,支持结构体之间的转换
1 parent bafcb20 commit 0d793f8

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

database/mapper/render.go

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package mapper
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"golang.org/x/exp/constraints"
7+
"reflect"
8+
"strings"
9+
)
10+
11+
func Map[S any, T any](source []S, mapper func(S) T) []T {
12+
result := make([]T, len(source))
13+
for i, s := range source {
14+
result[i] = mapper(s)
15+
}
16+
return result
17+
}
18+
19+
func MapStruct[S any, T any](source *S) *T {
20+
bytes, err := json.Marshal(source)
21+
if err != nil {
22+
return nil
23+
}
24+
var target T
25+
err = json.Unmarshal(bytes, &target)
26+
if err != nil {
27+
return nil
28+
}
29+
return &target
30+
}
31+
32+
func SetFields[S any, T any](source S, target T, ignoreZero bool) (err error) {
33+
defer func() {
34+
if r := recover(); r != nil {
35+
err = fmt.Errorf("panic: %v", r)
36+
}
37+
}()
38+
sourceValue := reflect.ValueOf(source)
39+
targetValue := reflect.ValueOf(target)
40+
if targetValue.Kind() != reflect.Ptr {
41+
err = fmt.Errorf("target must be a pointer")
42+
return
43+
}
44+
targetValue = targetValue.Elem()
45+
sourceType := sourceValue.Type()
46+
for i := 0; i < sourceValue.NumField(); i++ {
47+
field := sourceType.Field(i)
48+
fieldValue := sourceValue.Field(i)
49+
setterName := "Set" + strings.Title(field.Name)
50+
method := targetValue.Addr().MethodByName(setterName)
51+
if method.IsValid() {
52+
args := []reflect.Value{fieldValue}
53+
if ignoreZero && fieldValue.IsZero() {
54+
continue
55+
}
56+
method.Call(args)
57+
}
58+
}
59+
return
60+
}
61+
62+
func Page[P constraints.Integer](total, pageSize, defaultSize P) P {
63+
if total == 0 {
64+
return 0
65+
}
66+
if pageSize == 0 {
67+
pageSize = defaultSize
68+
}
69+
if pageSize == 0 {
70+
return total
71+
}
72+
page := total / pageSize
73+
if total%pageSize != 0 {
74+
page++
75+
}
76+
return page
77+
}

database/mapper/render_test.go

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package mapper
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
type Source struct {
9+
Name string
10+
Age int
11+
IsActive bool
12+
}
13+
14+
type Target struct {
15+
name *string
16+
age int
17+
isActive bool
18+
}
19+
20+
func (t *Target) SetName(name string) { t.name = &name }
21+
func (t *Target) SetAge(age int) { t.age = age }
22+
func (t *Target) SetIsActive(isActive bool) { t.isActive = isActive }
23+
24+
func TestSetFields(t *testing.T) {
25+
names := []string{"Alice", "", "Charlie"}
26+
tests := []struct {
27+
name string
28+
source Source
29+
ignoreZero bool
30+
want Target
31+
}{
32+
{
33+
name: "Basic Test",
34+
source: Source{
35+
Name: names[0],
36+
Age: 25,
37+
IsActive: true,
38+
},
39+
ignoreZero: false,
40+
want: Target{
41+
name: &names[0],
42+
age: 25,
43+
isActive: true,
44+
},
45+
},
46+
{
47+
name: "Ignore zero value test",
48+
source: Source{
49+
Name: names[1],
50+
Age: 0,
51+
IsActive: false,
52+
},
53+
ignoreZero: true,
54+
want: Target{
55+
name: nil,
56+
age: 0,
57+
isActive: false,
58+
},
59+
},
60+
{
61+
name: "Partial field test",
62+
source: Source{
63+
Name: names[2],
64+
Age: 0,
65+
IsActive: true,
66+
},
67+
ignoreZero: true,
68+
want: Target{
69+
name: &names[2],
70+
age: 0,
71+
isActive: true,
72+
},
73+
},
74+
}
75+
76+
for _, tt := range tests {
77+
t.Run(tt.name, func(t *testing.T) {
78+
target := &Target{}
79+
err := SetFields(tt.source, target, tt.ignoreZero)
80+
if err != nil {
81+
t.Errorf("SetFields() error = %v", err)
82+
return
83+
}
84+
85+
if !reflect.DeepEqual(*target, tt.want) {
86+
t.Errorf("SetFields() = %v, want %v", *target, tt.want)
87+
}
88+
})
89+
}
90+
}

0 commit comments

Comments
 (0)