forked from wundergraph/astjson
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmergevalues.go
125 lines (121 loc) · 2.77 KB
/
mergevalues.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
121
122
123
124
125
package astjson
import (
"bytes"
"errors"
)
var (
ErrMergeDifferentTypes = errors.New("cannot merge different types")
ErrMergeDifferingArrayLengths = errors.New("cannot merge arrays of differing lengths")
ErrMergeUnknownType = errors.New("cannot merge unknown type")
)
func MergeValues(a, b *Value) (v *Value, changed bool, err error) {
if a == nil {
return b, true, nil
}
if b == nil {
return a, false, nil
}
if b.Type() == TypeNull && a.Type() == TypeObject {
// we assume that null was returned in an error case for resolving a nested object field
// as we've got an object on the left side, we don't override the whole object with null
// instead, we keep the left object and discard the null on the right side
return a, false, nil
}
aBool, bBool := a.Type() == TypeTrue || a.Type() == TypeFalse, b.Type() == TypeTrue || b.Type() == TypeFalse
booleans := aBool && bBool
if a.Type() != b.Type() && !booleans {
return nil, false, ErrMergeDifferentTypes
}
switch a.Type() {
case TypeObject:
ao, _ := a.Object()
bo, _ := b.Object()
ao.unescapeKeys()
bo.unescapeKeys()
for i := range bo.kvs {
k := bo.kvs[i].k
r := bo.kvs[i].v
l := ao.Get(k)
if l == nil {
ao.Set(k, r)
continue
}
n, changed, err := MergeValues(l, r)
if err != nil {
return nil, false, err
}
if changed {
ao.Set(k, n)
}
}
return a, false, nil
case TypeArray:
aa, _ := a.Array()
ba, _ := b.Array()
if len(aa) == 0 {
return b, true, nil
}
if len(ba) == 0 {
return a, false, nil
}
if len(aa) != len(ba) {
return nil, false, ErrMergeDifferingArrayLengths
}
for i := range aa {
n, changed, err := MergeValues(aa[i], ba[i])
if err != nil {
return nil, false, err
}
if changed {
aa[i] = n
}
}
return a, false, nil
case TypeFalse:
if b.Type() == TypeTrue {
return b, true, nil
}
return a, false, nil
case TypeTrue:
if b.Type() == TypeFalse {
return b, true, nil
}
return a, false, nil
case TypeNull:
if b.Type() != TypeNull {
return b, true, nil
}
return a, false, nil
case TypeNumber:
af, _ := a.Float64()
bf, _ := b.Float64()
if af != bf {
return b, true, nil
}
return a, false, nil
case TypeString:
as, _ := a.StringBytes()
bs, _ := b.StringBytes()
if !bytes.Equal(as, bs) {
return b, true, nil
}
return a, false, nil
default:
return nil, false, ErrMergeUnknownType
}
}
func MergeValuesWithPath(a, b *Value, path ...string) (v *Value, changed bool, err error) {
if len(path) == 0 {
return MergeValues(a, b)
}
root := &Value{
t: TypeObject,
}
current := root
for i := 0; i < len(path)-1; i++ {
current.Set(path[i], &Value{t: TypeObject})
current = current.Get(path[i])
}
current.Set(path[len(path)-1], b)
return MergeValues(a, root)
}