-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmerge.go
112 lines (98 loc) · 2.9 KB
/
merge.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
package conflate
import (
"reflect"
)
func mergeTo(toData interface{}, fromData ...interface{}) error {
for _, fromDatum := range fromData {
err := merge(toData, fromDatum)
if err != nil {
return err
}
}
return nil
}
func merge(pToData interface{}, fromData interface{}) error {
return mergeRecursive(rootContext(), pToData, fromData)
}
func mergeRecursive(ctx context, pToData interface{}, fromData interface{}) error {
if pToData == nil {
return makeContextError(ctx, "The destination variable must not be nil")
}
pToVal := reflect.ValueOf(pToData)
if pToVal.Kind() != reflect.Ptr {
return makeContextError(ctx, "The destination variable must be a pointer")
}
if fromData == nil {
return nil
}
toVal := pToVal.Elem()
fromVal := reflect.ValueOf(fromData)
toData := toVal.Interface()
if toVal.Interface() == nil {
toVal.Set(fromVal)
return nil
}
var err error
switch fromVal.Kind() {
case reflect.Map:
err = mergeMapRecursive(ctx, toVal, fromVal, toData, fromData)
case reflect.Slice:
err = mergeSliceRecursive(ctx, toVal, fromVal, toData, fromData)
default:
err = mergeDefaultRecursive(ctx, toVal, fromVal, toData, fromData)
}
return err
}
func mergeMapRecursive(ctx context, toVal reflect.Value, fromVal reflect.Value,
toData interface{}, fromData interface{}) error {
fromProps, ok := fromData.(map[string]interface{})
if !ok {
return makeContextError(ctx, "The source value must be a map[string]interface{}")
}
toProps, _ := toData.(map[string]interface{})
if toProps == nil {
return makeContextError(ctx, "The destination value must be a map[string]interface{}")
}
for name, fromProp := range fromProps {
if val := toProps[name]; val == nil {
toProps[name] = fromProp
} else {
err := merge(&val, fromProp)
if err != nil {
return makeContextError(ctx.add(name), "Failed to merge object property : %v : %v", name, err)
}
toProps[name] = val
}
}
return nil
}
func mergeSliceRecursive(ctx context, toVal reflect.Value, fromVal reflect.Value,
toData interface{}, fromData interface{}) error {
fromItems, ok := fromData.([]interface{})
if !ok {
return makeContextError(ctx, "The source value must be a []interface{}")
}
toItems, _ := toData.([]interface{})
if toItems == nil {
return makeContextError(ctx, "The destination value must be a []interface{}")
}
toItems = append(toItems, fromItems...)
toVal.Set(reflect.ValueOf(toItems))
return nil
}
func mergeDefaultRecursive(ctx context, toVal reflect.Value, fromVal reflect.Value,
toData interface{}, fromData interface{}) error {
if reflect.DeepEqual(toData, fromData) {
return nil
}
fromType := fromVal.Type()
toType := toVal.Type()
if toType.Kind() == reflect.Interface {
toType = toVal.Elem().Type()
}
if !fromType.AssignableTo(toType) {
return makeContextError(ctx, "The destination type (%v) must be the same as the source type (%v)", toType, fromType)
}
toVal.Set(fromVal)
return nil
}