1 package tree
2
3 import (
4 "fmt"
5
6 "sigs.k8s.io/yaml"
7 )
8
9
10
11 type Tree map[string]interface{}
12
13
14 func (t Tree) ToYAML() (string, error) {
15 bytes, err := yaml.Marshal(t)
16 if err != nil {
17 return "", err
18 }
19 return string(bytes), nil
20 }
21
22
23
24 func (t Tree) String() string {
25 s, err := t.ToYAML()
26 if err != nil {
27 return err.Error()
28 }
29 return s
30 }
31
32
33 func (t Tree) GetString(path ...string) (string, error) {
34 if len(path) == 1 {
35
36 if val, ok := t[path[0]]; ok {
37
38 if s, ok := val.(string); ok {
39 return s, nil
40 }
41 return "", fmt.Errorf("expected string at node %s but found a different type", path[0])
42 }
43 return "", fmt.Errorf("could not find node %s", path[0])
44 }
45
46
47 if val, ok := t[path[0]]; ok {
48
49 if valTree, ok := val.(Tree); ok {
50 return valTree.GetString(path[1:]...)
51 }
52 return "", fmt.Errorf("expected Tree at node %s but found a different type", path[0])
53 }
54 return "", fmt.Errorf("could not find node %s", path[0])
55 }
56
57
58 func (t Tree) Diff(other Tree) (Tree, error) {
59 diff := make(Tree)
60 for k, v := range other {
61 tv, ok := t[k]
62 if ok {
63 tvt, tvIsTree := tv.(Tree)
64 vt, vIsTree := v.(Tree)
65 if tvIsTree && vIsTree {
66 subdiff, err := tvt.Diff(vt)
67 if err != nil {
68 return nil, err
69 }
70 diff[k] = subdiff
71 } else if !tvIsTree && !vIsTree {
72 if !equal(v, tv) {
73 diff[k] = v
74 }
75 } else {
76 diff[k] = v
77 }
78 } else {
79 diff[k] = v
80 }
81 }
82 diff.Prune()
83 return diff, nil
84 }
85
86 func equal(x interface{}, y interface{}) bool {
87 xt, xIsTree := x.(Tree)
88 yt, yIsTree := y.(Tree)
89 if xIsTree && yIsTree {
90 if len(xt) != len(yt) {
91 return false
92 }
93 for k := range xt {
94 if !equal(xt[k], yt[k]) {
95 return false
96 }
97 }
98 return true
99 }
100 if xIsTree || yIsTree {
101 return false
102 }
103 xs, xIsSlice := x.([]interface{})
104 ys, yIsSlice := x.([]interface{})
105 if xIsSlice && yIsSlice {
106 if len(xs) != len(ys) {
107 return false
108 }
109 for i := range xs {
110 if !equal(xs[i], ys[i]) {
111 return false
112 }
113 }
114 return true
115 }
116 if xIsSlice || yIsSlice {
117 return false
118 }
119 return x == y
120 }
121
122
123
124 func (t Tree) Prune() {
125 for k, v := range t {
126 child, isTree := v.(Tree)
127 if isTree {
128 if child.Empty() {
129 delete(t, k)
130 } else {
131 child.Prune()
132 }
133 }
134 }
135 }
136
137
138 func (t Tree) Empty() bool {
139 for _, v := range t {
140 child, isTree := v.(Tree)
141 if !isTree {
142 return false
143 }
144 if !child.Empty() {
145 return false
146 }
147 }
148 return true
149 }
150
151
152
153 func MarshalToTree(obj interface{}) (Tree, error) {
154 bytes, err := yaml.Marshal(obj)
155 if err != nil {
156 return nil, err
157 }
158 return BytesToTree(bytes)
159 }
160
161
162 func BytesToTree(bytes []byte) (Tree, error) {
163 tree := make(Tree)
164 err := yaml.Unmarshal(bytes, &tree)
165 if err != nil {
166 return nil, err
167 }
168 tree.coerceToTree()
169 return tree, nil
170 }
171
172
173
174
175 func Diff(x interface{}, y interface{}) (Tree, error) {
176 xTree, err := MarshalToTree(x)
177 if err != nil {
178 return nil, err
179 }
180 yTree, err := MarshalToTree(y)
181 if err != nil {
182 return nil, err
183 }
184 return xTree.Diff(yTree)
185 }
186
187
188
189 func coerceTreeValue(v interface{}) interface{} {
190 if vt, ok := v.(Tree); ok {
191 vt.coerceToTree()
192 } else if vm, ok := v.(map[string]interface{}); ok {
193 tree := Tree(vm)
194 tree.coerceToTree()
195 return tree
196 } else if va, ok := v.([]interface{}); ok {
197 for i, v := range va {
198 va[i] = coerceTreeValue(v)
199 }
200 }
201 return v
202 }
203
204
205
206
207
208
209 func (t Tree) coerceToTree() {
210 for k, v := range t {
211 t[k] = coerceTreeValue(v)
212 }
213 }
214
View as plain text