1
2
3
4
5
6
7
8
9 package mergo
10
11 import (
12 "fmt"
13 "reflect"
14 "unicode"
15 "unicode/utf8"
16 )
17
18 func changeInitialCase(s string, mapper func(rune) rune) string {
19 if s == "" {
20 return s
21 }
22 r, n := utf8.DecodeRuneInString(s)
23 return string(mapper(r)) + s[n:]
24 }
25
26 func isExported(field reflect.StructField) bool {
27 r, _ := utf8.DecodeRuneInString(field.Name)
28 return r >= 'A' && r <= 'Z'
29 }
30
31
32
33
34 func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
35 overwrite := config.Overwrite
36 if dst.CanAddr() {
37 addr := dst.UnsafeAddr()
38 h := 17 * addr
39 seen := visited[h]
40 typ := dst.Type()
41 for p := seen; p != nil; p = p.next {
42 if p.ptr == addr && p.typ == typ {
43 return nil
44 }
45 }
46
47 visited[h] = &visit{typ, seen, addr}
48 }
49 zeroValue := reflect.Value{}
50 switch dst.Kind() {
51 case reflect.Map:
52 dstMap := dst.Interface().(map[string]interface{})
53 for i, n := 0, src.NumField(); i < n; i++ {
54 srcType := src.Type()
55 field := srcType.Field(i)
56 if !isExported(field) {
57 continue
58 }
59 fieldName := field.Name
60 fieldName = changeInitialCase(fieldName, unicode.ToLower)
61 if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v), !config.ShouldNotDereference) || overwrite) {
62 dstMap[fieldName] = src.Field(i).Interface()
63 }
64 }
65 case reflect.Ptr:
66 if dst.IsNil() {
67 v := reflect.New(dst.Type().Elem())
68 dst.Set(v)
69 }
70 dst = dst.Elem()
71 fallthrough
72 case reflect.Struct:
73 srcMap := src.Interface().(map[string]interface{})
74 for key := range srcMap {
75 config.overwriteWithEmptyValue = true
76 srcValue := srcMap[key]
77 fieldName := changeInitialCase(key, unicode.ToUpper)
78 dstElement := dst.FieldByName(fieldName)
79 if dstElement == zeroValue {
80
81 continue
82 }
83 srcElement := reflect.ValueOf(srcValue)
84 dstKind := dstElement.Kind()
85 srcKind := srcElement.Kind()
86 if srcKind == reflect.Ptr && dstKind != reflect.Ptr {
87 srcElement = srcElement.Elem()
88 srcKind = reflect.TypeOf(srcElement.Interface()).Kind()
89 } else if dstKind == reflect.Ptr {
90
91 if srcKind != reflect.Ptr && srcElement.CanAddr() {
92 srcPtr := srcElement.Addr()
93 srcElement = reflect.ValueOf(srcPtr)
94 srcKind = reflect.Ptr
95 }
96 }
97
98 if !srcElement.IsValid() {
99 continue
100 }
101 if srcKind == dstKind {
102 if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
103 return
104 }
105 } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
106 if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
107 return
108 }
109 } else if srcKind == reflect.Map {
110 if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
111 return
112 }
113 } else {
114 return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
115 }
116 }
117 }
118 return
119 }
120
121
122
123
124
125
126
127
128
129
130
131
132 func Map(dst, src interface{}, opts ...func(*Config)) error {
133 return _map(dst, src, opts...)
134 }
135
136
137
138
139 func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
140 return _map(dst, src, append(opts, WithOverride)...)
141 }
142
143 func _map(dst, src interface{}, opts ...func(*Config)) error {
144 if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
145 return ErrNonPointerArgument
146 }
147 var (
148 vDst, vSrc reflect.Value
149 err error
150 )
151 config := &Config{}
152
153 for _, opt := range opts {
154 opt(config)
155 }
156
157 if vDst, vSrc, err = resolveValues(dst, src); err != nil {
158 return err
159 }
160
161
162 if vSrc.Kind() == vDst.Kind() {
163 return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
164 }
165 switch vSrc.Kind() {
166 case reflect.Struct:
167 if vDst.Kind() != reflect.Map {
168 return ErrExpectedMapAsDestination
169 }
170 case reflect.Map:
171 if vDst.Kind() != reflect.Struct {
172 return ErrExpectedStructAsDestination
173 }
174 default:
175 return ErrNotSupported
176 }
177 return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
178 }
179
View as plain text