forked from jinzhu/copier
-
Notifications
You must be signed in to change notification settings - Fork 0
/
copier.go
120 lines (105 loc) · 2.44 KB
/
copier.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package copier
import "reflect"
func Copy(toValue interface{}, fromValue interface{}) (err error) {
var (
isSlice bool
fromType reflect.Type
isFromPtr bool
toType reflect.Type
isToPtr bool
amount int
)
from := reflect.Indirect(reflect.ValueOf(fromValue))
to := reflect.Indirect(reflect.ValueOf(toValue))
if to.Kind() == reflect.Slice {
isSlice = true
if from.Kind() == reflect.Slice {
fromType = from.Type().Elem()
if fromType.Kind() == reflect.Ptr {
fromType = fromType.Elem()
isFromPtr = true
}
amount = from.Len()
} else {
fromType = from.Type()
amount = 1
}
toType = to.Type().Elem()
if toType.Kind() == reflect.Ptr {
toType = toType.Elem()
isToPtr = true
}
} else {
fromType = from.Type()
toType = to.Type()
amount = 1
}
for e := 0; e < amount; e++ {
var dest, source reflect.Value
if isSlice {
if from.Kind() == reflect.Slice {
source = from.Index(e)
if isFromPtr {
source = source.Elem()
}
} else {
source = from
}
} else {
source = from
}
if isSlice {
dest = reflect.New(toType).Elem()
} else {
dest = to
}
for _, field := range deepFields(fromType) {
if !field.Anonymous {
name := field.Name
fromField := source.FieldByName(name)
toField := dest.FieldByName(name)
toMethod := dest.Addr().MethodByName(name)
if fromField.IsValid() && toField.IsValid() && toField.CanSet() {
toField.Set(fromField)
}
if fromField.IsValid() && toMethod.IsValid() {
toMethod.Call([]reflect.Value{fromField})
}
}
}
for i := 0; i < toType.NumField(); i++ {
field := toType.Field(i)
if !field.Anonymous {
name := field.Name
fromMethod := source.Addr().MethodByName(name)
toField := dest.FieldByName(name)
if fromMethod.IsValid() && toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
toField.Set(values[0])
}
}
}
}
if isSlice {
if isToPtr {
to.Set(reflect.Append(to, dest.Addr()))
} else {
to.Set(reflect.Append(to, dest))
}
}
}
return
}
func deepFields(ifaceType reflect.Type) []reflect.StructField {
var fields []reflect.StructField
for i := 0; i < ifaceType.NumField(); i++ {
v := ifaceType.Field(i)
if v.Anonymous && v.Type.Kind() == reflect.Struct {
fields = append(fields, deepFields(v.Type)...)
} else {
fields = append(fields, v)
}
}
return fields
}