diff options
author | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
---|---|---|
committer | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
commit | bae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch) | |
tree | ca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/hashicorp/hil/walk.go | |
parent | 254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff) | |
download | terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip |
Initial transfer of provider code
Diffstat (limited to 'vendor/github.com/hashicorp/hil/walk.go')
-rw-r--r-- | vendor/github.com/hashicorp/hil/walk.go | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hil/walk.go b/vendor/github.com/hashicorp/hil/walk.go new file mode 100644 index 0000000..0ace830 --- /dev/null +++ b/vendor/github.com/hashicorp/hil/walk.go | |||
@@ -0,0 +1,266 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "reflect" | ||
6 | "strings" | ||
7 | |||
8 | "github.com/hashicorp/hil/ast" | ||
9 | "github.com/mitchellh/reflectwalk" | ||
10 | ) | ||
11 | |||
12 | // WalkFn is the type of function to pass to Walk. Modify fields within | ||
13 | // WalkData to control whether replacement happens. | ||
14 | type WalkFn func(*WalkData) error | ||
15 | |||
16 | // WalkData is the structure passed to the callback of the Walk function. | ||
17 | // | ||
18 | // This structure contains data passed in as well as fields that are expected | ||
19 | // to be written by the caller as a result. Please see the documentation for | ||
20 | // each field for more information. | ||
21 | type WalkData struct { | ||
22 | // Root is the parsed root of this HIL program | ||
23 | Root ast.Node | ||
24 | |||
25 | // Location is the location within the structure where this | ||
26 | // value was found. This can be used to modify behavior within | ||
27 | // slices and so on. | ||
28 | Location reflectwalk.Location | ||
29 | |||
30 | // The below two values must be set by the callback to have any effect. | ||
31 | // | ||
32 | // Replace, if true, will replace the value in the structure with | ||
33 | // ReplaceValue. It is up to the caller to make sure this is a string. | ||
34 | Replace bool | ||
35 | ReplaceValue string | ||
36 | } | ||
37 | |||
38 | // Walk will walk an arbitrary Go structure and parse any string as an | ||
39 | // HIL program and call the callback cb to determine what to replace it | ||
40 | // with. | ||
41 | // | ||
42 | // This function is very useful for arbitrary HIL program interpolation | ||
43 | // across a complex configuration structure. Due to the heavy use of | ||
44 | // reflection in this function, it is recommend to write many unit tests | ||
45 | // with your typical configuration structures to hilp mitigate the risk | ||
46 | // of panics. | ||
47 | func Walk(v interface{}, cb WalkFn) error { | ||
48 | walker := &interpolationWalker{F: cb} | ||
49 | return reflectwalk.Walk(v, walker) | ||
50 | } | ||
51 | |||
52 | // interpolationWalker implements interfaces for the reflectwalk package | ||
53 | // (github.com/mitchellh/reflectwalk) that can be used to automatically | ||
54 | // execute a callback for an interpolation. | ||
55 | type interpolationWalker struct { | ||
56 | F WalkFn | ||
57 | |||
58 | key []string | ||
59 | lastValue reflect.Value | ||
60 | loc reflectwalk.Location | ||
61 | cs []reflect.Value | ||
62 | csKey []reflect.Value | ||
63 | csData interface{} | ||
64 | sliceIndex int | ||
65 | unknownKeys []string | ||
66 | } | ||
67 | |||
68 | func (w *interpolationWalker) Enter(loc reflectwalk.Location) error { | ||
69 | w.loc = loc | ||
70 | return nil | ||
71 | } | ||
72 | |||
73 | func (w *interpolationWalker) Exit(loc reflectwalk.Location) error { | ||
74 | w.loc = reflectwalk.None | ||
75 | |||
76 | switch loc { | ||
77 | case reflectwalk.Map: | ||
78 | w.cs = w.cs[:len(w.cs)-1] | ||
79 | case reflectwalk.MapValue: | ||
80 | w.key = w.key[:len(w.key)-1] | ||
81 | w.csKey = w.csKey[:len(w.csKey)-1] | ||
82 | case reflectwalk.Slice: | ||
83 | // Split any values that need to be split | ||
84 | w.splitSlice() | ||
85 | w.cs = w.cs[:len(w.cs)-1] | ||
86 | case reflectwalk.SliceElem: | ||
87 | w.csKey = w.csKey[:len(w.csKey)-1] | ||
88 | } | ||
89 | |||
90 | return nil | ||
91 | } | ||
92 | |||
93 | func (w *interpolationWalker) Map(m reflect.Value) error { | ||
94 | w.cs = append(w.cs, m) | ||
95 | return nil | ||
96 | } | ||
97 | |||
98 | func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error { | ||
99 | w.csData = k | ||
100 | w.csKey = append(w.csKey, k) | ||
101 | w.key = append(w.key, k.String()) | ||
102 | w.lastValue = v | ||
103 | return nil | ||
104 | } | ||
105 | |||
106 | func (w *interpolationWalker) Slice(s reflect.Value) error { | ||
107 | w.cs = append(w.cs, s) | ||
108 | return nil | ||
109 | } | ||
110 | |||
111 | func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error { | ||
112 | w.csKey = append(w.csKey, reflect.ValueOf(i)) | ||
113 | w.sliceIndex = i | ||
114 | return nil | ||
115 | } | ||
116 | |||
117 | func (w *interpolationWalker) Primitive(v reflect.Value) error { | ||
118 | setV := v | ||
119 | |||
120 | // We only care about strings | ||
121 | if v.Kind() == reflect.Interface { | ||
122 | setV = v | ||
123 | v = v.Elem() | ||
124 | } | ||
125 | if v.Kind() != reflect.String { | ||
126 | return nil | ||
127 | } | ||
128 | |||
129 | astRoot, err := Parse(v.String()) | ||
130 | if err != nil { | ||
131 | return err | ||
132 | } | ||
133 | |||
134 | // If the AST we got is just a literal string value with the same | ||
135 | // value then we ignore it. We have to check if its the same value | ||
136 | // because it is possible to input a string, get out a string, and | ||
137 | // have it be different. For example: "foo-$${bar}" turns into | ||
138 | // "foo-${bar}" | ||
139 | if n, ok := astRoot.(*ast.LiteralNode); ok { | ||
140 | if s, ok := n.Value.(string); ok && s == v.String() { | ||
141 | return nil | ||
142 | } | ||
143 | } | ||
144 | |||
145 | if w.F == nil { | ||
146 | return nil | ||
147 | } | ||
148 | |||
149 | data := WalkData{Root: astRoot, Location: w.loc} | ||
150 | if err := w.F(&data); err != nil { | ||
151 | return fmt.Errorf( | ||
152 | "%s in:\n\n%s", | ||
153 | err, v.String()) | ||
154 | } | ||
155 | |||
156 | if data.Replace { | ||
157 | /* | ||
158 | if remove { | ||
159 | w.removeCurrent() | ||
160 | return nil | ||
161 | } | ||
162 | */ | ||
163 | |||
164 | resultVal := reflect.ValueOf(data.ReplaceValue) | ||
165 | switch w.loc { | ||
166 | case reflectwalk.MapKey: | ||
167 | m := w.cs[len(w.cs)-1] | ||
168 | |||
169 | // Delete the old value | ||
170 | var zero reflect.Value | ||
171 | m.SetMapIndex(w.csData.(reflect.Value), zero) | ||
172 | |||
173 | // Set the new key with the existing value | ||
174 | m.SetMapIndex(resultVal, w.lastValue) | ||
175 | |||
176 | // Set the key to be the new key | ||
177 | w.csData = resultVal | ||
178 | case reflectwalk.MapValue: | ||
179 | // If we're in a map, then the only way to set a map value is | ||
180 | // to set it directly. | ||
181 | m := w.cs[len(w.cs)-1] | ||
182 | mk := w.csData.(reflect.Value) | ||
183 | m.SetMapIndex(mk, resultVal) | ||
184 | default: | ||
185 | // Otherwise, we should be addressable | ||
186 | setV.Set(resultVal) | ||
187 | } | ||
188 | } | ||
189 | |||
190 | return nil | ||
191 | } | ||
192 | |||
193 | func (w *interpolationWalker) removeCurrent() { | ||
194 | // Append the key to the unknown keys | ||
195 | w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, ".")) | ||
196 | |||
197 | for i := 1; i <= len(w.cs); i++ { | ||
198 | c := w.cs[len(w.cs)-i] | ||
199 | switch c.Kind() { | ||
200 | case reflect.Map: | ||
201 | // Zero value so that we delete the map key | ||
202 | var val reflect.Value | ||
203 | |||
204 | // Get the key and delete it | ||
205 | k := w.csData.(reflect.Value) | ||
206 | c.SetMapIndex(k, val) | ||
207 | return | ||
208 | } | ||
209 | } | ||
210 | |||
211 | panic("No container found for removeCurrent") | ||
212 | } | ||
213 | |||
214 | func (w *interpolationWalker) replaceCurrent(v reflect.Value) { | ||
215 | c := w.cs[len(w.cs)-2] | ||
216 | switch c.Kind() { | ||
217 | case reflect.Map: | ||
218 | // Get the key and delete it | ||
219 | k := w.csKey[len(w.csKey)-1] | ||
220 | c.SetMapIndex(k, v) | ||
221 | } | ||
222 | } | ||
223 | |||
224 | func (w *interpolationWalker) splitSlice() { | ||
225 | // Get the []interface{} slice so we can do some operations on | ||
226 | // it without dealing with reflection. We'll document each step | ||
227 | // here to be clear. | ||
228 | var s []interface{} | ||
229 | raw := w.cs[len(w.cs)-1] | ||
230 | switch v := raw.Interface().(type) { | ||
231 | case []interface{}: | ||
232 | s = v | ||
233 | case []map[string]interface{}: | ||
234 | return | ||
235 | default: | ||
236 | panic("Unknown kind: " + raw.Kind().String()) | ||
237 | } | ||
238 | |||
239 | // Check if we have any elements that we need to split. If not, then | ||
240 | // just return since we're done. | ||
241 | split := false | ||
242 | if !split { | ||
243 | return | ||
244 | } | ||
245 | |||
246 | // Make a new result slice that is twice the capacity to fit our growth. | ||
247 | result := make([]interface{}, 0, len(s)*2) | ||
248 | |||
249 | // Go over each element of the original slice and start building up | ||
250 | // the resulting slice by splitting where we have to. | ||
251 | for _, v := range s { | ||
252 | sv, ok := v.(string) | ||
253 | if !ok { | ||
254 | // Not a string, so just set it | ||
255 | result = append(result, v) | ||
256 | continue | ||
257 | } | ||
258 | |||
259 | // Not a string list, so just set it | ||
260 | result = append(result, sv) | ||
261 | } | ||
262 | |||
263 | // Our slice is now done, we have to replace the slice now | ||
264 | // with this new one that we have. | ||
265 | w.replaceCurrent(reflect.ValueOf(result)) | ||
266 | } | ||