]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/config/interpolate_walk.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / config / interpolate_walk.go
1 package config
2
3 import (
4 "fmt"
5 "reflect"
6 "strings"
7
8 "github.com/hashicorp/hil"
9 "github.com/hashicorp/hil/ast"
10 "github.com/mitchellh/reflectwalk"
11 )
12
13 // interpolationWalker implements interfaces for the reflectwalk package
14 // (github.com/mitchellh/reflectwalk) that can be used to automatically
15 // execute a callback for an interpolation.
16 type interpolationWalker struct {
17 // F is the function to call for every interpolation. It can be nil.
18 //
19 // If Replace is true, then the return value of F will be used to
20 // replace the interpolation.
21 F interpolationWalkerFunc
22 Replace bool
23
24 // ContextF is an advanced version of F that also receives the
25 // location of where it is in the structure. This lets you do
26 // context-aware validation.
27 ContextF interpolationWalkerContextFunc
28
29 key []string
30 lastValue reflect.Value
31 loc reflectwalk.Location
32 cs []reflect.Value
33 csKey []reflect.Value
34 csData interface{}
35 sliceIndex []int
36 unknownKeys []string
37 }
38
39 // interpolationWalkerFunc is the callback called by interpolationWalk.
40 // It is called with any interpolation found. It should return a value
41 // to replace the interpolation with, along with any errors.
42 //
43 // If Replace is set to false in interpolationWalker, then the replace
44 // value can be anything as it will have no effect.
45 type interpolationWalkerFunc func(ast.Node) (interface{}, error)
46
47 // interpolationWalkerContextFunc is called by interpolationWalk if
48 // ContextF is set. This receives both the interpolation and the location
49 // where the interpolation is.
50 //
51 // This callback can be used to validate the location of the interpolation
52 // within the configuration.
53 type interpolationWalkerContextFunc func(reflectwalk.Location, ast.Node)
54
55 func (w *interpolationWalker) Enter(loc reflectwalk.Location) error {
56 w.loc = loc
57 return nil
58 }
59
60 func (w *interpolationWalker) Exit(loc reflectwalk.Location) error {
61 w.loc = reflectwalk.None
62
63 switch loc {
64 case reflectwalk.Map:
65 w.cs = w.cs[:len(w.cs)-1]
66 case reflectwalk.MapValue:
67 w.key = w.key[:len(w.key)-1]
68 w.csKey = w.csKey[:len(w.csKey)-1]
69 case reflectwalk.Slice:
70 // Split any values that need to be split
71 w.splitSlice()
72 w.cs = w.cs[:len(w.cs)-1]
73 case reflectwalk.SliceElem:
74 w.csKey = w.csKey[:len(w.csKey)-1]
75 w.sliceIndex = w.sliceIndex[:len(w.sliceIndex)-1]
76 }
77
78 return nil
79 }
80
81 func (w *interpolationWalker) Map(m reflect.Value) error {
82 w.cs = append(w.cs, m)
83 return nil
84 }
85
86 func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error {
87 w.csData = k
88 w.csKey = append(w.csKey, k)
89
90 if l := len(w.sliceIndex); l > 0 {
91 w.key = append(w.key, fmt.Sprintf("%d.%s", w.sliceIndex[l-1], k.String()))
92 } else {
93 w.key = append(w.key, k.String())
94 }
95
96 w.lastValue = v
97 return nil
98 }
99
100 func (w *interpolationWalker) Slice(s reflect.Value) error {
101 w.cs = append(w.cs, s)
102 return nil
103 }
104
105 func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error {
106 w.csKey = append(w.csKey, reflect.ValueOf(i))
107 w.sliceIndex = append(w.sliceIndex, i)
108 return nil
109 }
110
111 func (w *interpolationWalker) Primitive(v reflect.Value) error {
112 setV := v
113
114 // We only care about strings
115 if v.Kind() == reflect.Interface {
116 setV = v
117 v = v.Elem()
118 }
119 if v.Kind() != reflect.String {
120 return nil
121 }
122
123 astRoot, err := hil.Parse(v.String())
124 if err != nil {
125 return err
126 }
127
128 // If the AST we got is just a literal string value with the same
129 // value then we ignore it. We have to check if its the same value
130 // because it is possible to input a string, get out a string, and
131 // have it be different. For example: "foo-$${bar}" turns into
132 // "foo-${bar}"
133 if n, ok := astRoot.(*ast.LiteralNode); ok {
134 if s, ok := n.Value.(string); ok && s == v.String() {
135 return nil
136 }
137 }
138
139 if w.ContextF != nil {
140 w.ContextF(w.loc, astRoot)
141 }
142
143 if w.F == nil {
144 return nil
145 }
146
147 replaceVal, err := w.F(astRoot)
148 if err != nil {
149 return fmt.Errorf(
150 "%s in:\n\n%s",
151 err, v.String())
152 }
153
154 if w.Replace {
155 // We need to determine if we need to remove this element
156 // if the result contains any "UnknownVariableValue" which is
157 // set if it is computed. This behavior is different if we're
158 // splitting (in a SliceElem) or not.
159 remove := false
160 if w.loc == reflectwalk.SliceElem {
161 switch typedReplaceVal := replaceVal.(type) {
162 case string:
163 if typedReplaceVal == UnknownVariableValue {
164 remove = true
165 }
166 case []interface{}:
167 if hasUnknownValue(typedReplaceVal) {
168 remove = true
169 }
170 }
171 } else if replaceVal == UnknownVariableValue {
172 remove = true
173 }
174
175 if remove {
176 w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))
177 }
178
179 resultVal := reflect.ValueOf(replaceVal)
180 switch w.loc {
181 case reflectwalk.MapKey:
182 m := w.cs[len(w.cs)-1]
183
184 // Delete the old value
185 var zero reflect.Value
186 m.SetMapIndex(w.csData.(reflect.Value), zero)
187
188 // Set the new key with the existing value
189 m.SetMapIndex(resultVal, w.lastValue)
190
191 // Set the key to be the new key
192 w.csData = resultVal
193 case reflectwalk.MapValue:
194 // If we're in a map, then the only way to set a map value is
195 // to set it directly.
196 m := w.cs[len(w.cs)-1]
197 mk := w.csData.(reflect.Value)
198 m.SetMapIndex(mk, resultVal)
199 default:
200 // Otherwise, we should be addressable
201 setV.Set(resultVal)
202 }
203 }
204
205 return nil
206 }
207
208 func (w *interpolationWalker) replaceCurrent(v reflect.Value) {
209 // if we don't have at least 2 values, we're not going to find a map, but
210 // we could panic.
211 if len(w.cs) < 2 {
212 return
213 }
214
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 hasUnknownValue(variable []interface{}) bool {
225 for _, value := range variable {
226 if strVal, ok := value.(string); ok {
227 if strVal == UnknownVariableValue {
228 return true
229 }
230 }
231 }
232 return false
233 }
234
235 func (w *interpolationWalker) splitSlice() {
236 raw := w.cs[len(w.cs)-1]
237
238 var s []interface{}
239 switch v := raw.Interface().(type) {
240 case []interface{}:
241 s = v
242 case []map[string]interface{}:
243 return
244 }
245
246 split := false
247 for _, val := range s {
248 if varVal, ok := val.(ast.Variable); ok && varVal.Type == ast.TypeList {
249 split = true
250 }
251 if _, ok := val.([]interface{}); ok {
252 split = true
253 }
254 }
255
256 if !split {
257 return
258 }
259
260 result := make([]interface{}, 0)
261 for _, v := range s {
262 switch val := v.(type) {
263 case ast.Variable:
264 switch val.Type {
265 case ast.TypeList:
266 elements := val.Value.([]ast.Variable)
267 for _, element := range elements {
268 result = append(result, element.Value)
269 }
270 default:
271 result = append(result, val.Value)
272 }
273 case []interface{}:
274 for _, element := range val {
275 result = append(result, element)
276 }
277 default:
278 result = append(result, v)
279 }
280 }
281
282 w.replaceCurrent(reflect.ValueOf(result))
283 }