forked from hofstadter-io/structural
-
Notifications
You must be signed in to change notification settings - Fork 0
/
merge.go
124 lines (103 loc) · 2.61 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
113
114
115
116
117
118
119
120
121
122
123
124
package structural
import (
"fmt"
"reflect"
"cuelang.org/go/cue"
)
func CueMerge(sorig, snew string) (string, error) {
out := NewpvStruct()
vorigi, err := r.Compile("", sorig)
if err != nil {
return "", err
}
vorig := vorigi.Value()
if vorig.Err() != nil {
return "", vorig.Err()
}
vnewi, err := r.Compile("", snew)
if err != nil {
return "", err
}
vnew := vnewi.Value()
if vnew.Err() != nil {
return "", vnew.Err()
}
err = cueMerge(out, vorig, vnew)
if err != nil {
return "", err
}
return out.ToString()
}
func cueMerge(out *pvStruct, vorig, vnew cue.Value) error {
// Loop over keys in orig
vorigStruct, err := vorig.Struct()
if err != nil {
return err
}
vorigIter := vorigStruct.Fields()
for vorigIter.Next() {
origVal := vorigIter.Value()
// Add anything that doesn't exist in new
newVal, err := vnew.LookupField(vorigIter.Label())
if err != nil {
out.Set(vorigIter.Label(), *ExprFromValue(origVal))
continue
}
// If the values are not the same overall shape, then fail
if (isBuiltin(origVal) && !isBuiltin(newVal.Value)) ||
(isStruct(origVal) && !isStruct(newVal.Value)) ||
(isList(origVal) && !isList(newVal.Value)) {
return fmt.Errorf("invalid merge: %s has different type", vorigIter.Label())
}
// Always use new for builtins and lists
// TODO handle lists better
if isBuiltin(origVal) || isList(origVal) {
out.Set(vorigIter.Label(), *ExprFromValue(newVal.Value))
continue
}
// Recurse for structs
if !isStruct(origVal) || !isStruct(newVal.Value) {
panic("should not reach")
}
rval := NewpvStruct()
err = cueMerge(rval, origVal, newVal.Value)
if err != nil {
return err
}
out.Set(vorigIter.Label(), *rval.ToExpr())
}
// Loop over keys in new
vnewStruct, err := vnew.Struct()
if err != nil {
return err
}
vnewIter := vnewStruct.Fields()
for vnewIter.Next() {
// Add any new keys not in orig
_, err := vorig.LookupField(vnewIter.Label())
if err != nil {
out.Set(vnewIter.Label(), *ExprFromValue(vnewIter.Value()))
}
}
return nil
}
///////////
func Merge(orig, last interface{}) (interface{}, error) {
O, ook := orig.(cue.Value)
L, lok := last.(cue.Value)
if ook && lok {
return MergeCue(O, L)
}
if !(ook || lok) {
return MergeGo(orig, last)
}
return nil, fmt.Errorf("structural.Merge - Incompatible types %v and %v", reflect.TypeOf(orig), reflect.TypeOf(last))
}
func MergeCue(orig, last cue.Value) (cue.Value, error) {
fmt.Println("MergeCue - no implemented")
return cue.Value{}, nil
}
func MergeGo(orig, last interface{}) (interface{}, error) {
fmt.Println("MergeGo - no implemented")
return nil, nil
}