-
Notifications
You must be signed in to change notification settings - Fork 4
/
auto.go
105 lines (88 loc) · 2.44 KB
/
auto.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package gomapper
import (
"reflect"
"github.com/insei/fmap/v3"
)
var (
manualFieldRoutes = map[reflect.Type]map[string]string{}
)
func AutoRoute[TSource, TDest any | []any](opts ...Option) error {
s := new(TSource)
d := new(TDest)
sourceStorage, _ := fmap.GetFrom(s)
destStorage, _ := fmap.GetFrom(d)
sourceType := reflect.TypeOf(s)
opt := &options{}
for _, o := range opts {
o.apply(opt)
}
mapFunc := func(source TSource, dest *TDest) error {
for _, sourcePath := range sourceStorage.GetAllPaths() {
destFld, ok := destStorage.Find(getDestFieldName(sourceType, sourcePath))
if !ok {
continue
}
srcFld := sourceStorage.MustFind(sourcePath)
if destFld.GetType() != srcFld.GetType() {
continue
}
if err := setFieldRecursive(srcFld, destFld, source, dest); err != nil {
return err
}
}
for _, o := range opt.Fns {
fn, ok := o.(func(TSource, *TDest))
if !ok {
continue
}
fn(source, dest)
}
return nil
}
return AddRoute[TSource, TDest](mapFunc)
}
func setFieldRecursive(sourceFld, destFld fmap.Field, source, dest any) error {
if r, ok := getRouteIfExists(sourceFld, destFld); ok {
return r(sourceFld.Get(source), destFld.GetPtr(dest))
}
if sourceFld.GetType().Kind() != reflect.Struct {
sourceVal := sourceFld.Get(source)
if sourceVal != nil {
destFld.Set(dest, sourceVal)
}
return nil
}
sourceStructField := sourceFld.GetPtr(source)
sourceStorage, _ := fmap.GetFrom(sourceStructField)
destStructField := destFld.GetPtr(dest)
destStorage, _ := fmap.GetFrom(destStructField)
for _, sPath := range sourceStorage.GetAllPaths() {
sField := sourceStorage.MustFind(sPath)
dPath := getDestFieldName(sField.GetType(), sPath)
dField, ok := destStorage.Find(dPath)
if !ok {
continue
}
err := setFieldRecursive(sField, dField, sourceStructField, destStructField)
if err != nil {
return err
}
}
return nil
}
func getRouteIfExists(sourceFld, destFld fmap.Field) (func(source interface{}, dest interface{}) error, bool) {
destType := destFld.GetType()
sourceType := sourceFld.GetType()
for sourceType.Kind() == reflect.Ptr {
sourceType = sourceType.Elem()
}
destType = reflect.PointerTo(destType)
r, ok := routes[sourceType][destType]
return r, ok
}
func getDestFieldName(sourceFieldType reflect.Type, sourceFieldName string) string {
if destFieldName, ok := manualFieldRoutes[sourceFieldType][sourceFieldName]; ok {
return destFieldName
}
return sourceFieldName
}