]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/hcl2/hcl/traversal.go
deps: github.com/hashicorp/terraform@sdk-v0.11-with-go-modules
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hcl / traversal.go
1 package hcl
2
3 import (
4 "fmt"
5
6 "github.com/zclconf/go-cty/cty"
7 )
8
9 // A Traversal is a description of traversing through a value through a
10 // series of operations such as attribute lookup, index lookup, etc.
11 //
12 // It is used to look up values in scopes, for example.
13 //
14 // The traversal operations are implementations of interface Traverser.
15 // This is a closed set of implementations, so the interface cannot be
16 // implemented from outside this package.
17 //
18 // A traversal can be absolute (its first value is a symbol name) or relative
19 // (starts from an existing value).
20 type Traversal []Traverser
21
22 // TraversalJoin appends a relative traversal to an absolute traversal to
23 // produce a new absolute traversal.
24 func TraversalJoin(abs Traversal, rel Traversal) Traversal {
25 if abs.IsRelative() {
26 panic("first argument to TraversalJoin must be absolute")
27 }
28 if !rel.IsRelative() {
29 panic("second argument to TraversalJoin must be relative")
30 }
31
32 ret := make(Traversal, len(abs)+len(rel))
33 copy(ret, abs)
34 copy(ret[len(abs):], rel)
35 return ret
36 }
37
38 // TraverseRel applies the receiving traversal to the given value, returning
39 // the resulting value. This is supported only for relative traversals,
40 // and will panic if applied to an absolute traversal.
41 func (t Traversal) TraverseRel(val cty.Value) (cty.Value, Diagnostics) {
42 if !t.IsRelative() {
43 panic("can't use TraverseRel on an absolute traversal")
44 }
45
46 current := val
47 var diags Diagnostics
48 for _, tr := range t {
49 var newDiags Diagnostics
50 current, newDiags = tr.TraversalStep(current)
51 diags = append(diags, newDiags...)
52 if newDiags.HasErrors() {
53 return cty.DynamicVal, diags
54 }
55 }
56 return current, diags
57 }
58
59 // TraverseAbs applies the receiving traversal to the given eval context,
60 // returning the resulting value. This is supported only for absolute
61 // traversals, and will panic if applied to a relative traversal.
62 func (t Traversal) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) {
63 if t.IsRelative() {
64 panic("can't use TraverseAbs on a relative traversal")
65 }
66
67 split := t.SimpleSplit()
68 root := split.Abs[0].(TraverseRoot)
69 name := root.Name
70
71 thisCtx := ctx
72 hasNonNil := false
73 for thisCtx != nil {
74 if thisCtx.Variables == nil {
75 thisCtx = thisCtx.parent
76 continue
77 }
78 hasNonNil = true
79 val, exists := thisCtx.Variables[name]
80 if exists {
81 return split.Rel.TraverseRel(val)
82 }
83 thisCtx = thisCtx.parent
84 }
85
86 if !hasNonNil {
87 return cty.DynamicVal, Diagnostics{
88 {
89 Severity: DiagError,
90 Summary: "Variables not allowed",
91 Detail: "Variables may not be used here.",
92 Subject: &root.SrcRange,
93 },
94 }
95 }
96
97 suggestions := make([]string, 0, len(ctx.Variables))
98 thisCtx = ctx
99 for thisCtx != nil {
100 for k := range thisCtx.Variables {
101 suggestions = append(suggestions, k)
102 }
103 thisCtx = thisCtx.parent
104 }
105 suggestion := nameSuggestion(name, suggestions)
106 if suggestion != "" {
107 suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
108 }
109
110 return cty.DynamicVal, Diagnostics{
111 {
112 Severity: DiagError,
113 Summary: "Unknown variable",
114 Detail: fmt.Sprintf("There is no variable named %q.%s", name, suggestion),
115 Subject: &root.SrcRange,
116 },
117 }
118 }
119
120 // IsRelative returns true if the receiver is a relative traversal, or false
121 // otherwise.
122 func (t Traversal) IsRelative() bool {
123 if len(t) == 0 {
124 return true
125 }
126 if _, firstIsRoot := t[0].(TraverseRoot); firstIsRoot {
127 return false
128 }
129 return true
130 }
131
132 // SimpleSplit returns a TraversalSplit where the name lookup is the absolute
133 // part and the remainder is the relative part. Supported only for
134 // absolute traversals, and will panic if applied to a relative traversal.
135 //
136 // This can be used by applications that have a relatively-simple variable
137 // namespace where only the top-level is directly populated in the scope, with
138 // everything else handled by relative lookups from those initial values.
139 func (t Traversal) SimpleSplit() TraversalSplit {
140 if t.IsRelative() {
141 panic("can't use SimpleSplit on a relative traversal")
142 }
143 return TraversalSplit{
144 Abs: t[0:1],
145 Rel: t[1:],
146 }
147 }
148
149 // RootName returns the root name for a absolute traversal. Will panic if
150 // called on a relative traversal.
151 func (t Traversal) RootName() string {
152 if t.IsRelative() {
153 panic("can't use RootName on a relative traversal")
154
155 }
156 return t[0].(TraverseRoot).Name
157 }
158
159 // SourceRange returns the source range for the traversal.
160 func (t Traversal) SourceRange() Range {
161 if len(t) == 0 {
162 // Nothing useful to return here, but we'll return something
163 // that's correctly-typed at least.
164 return Range{}
165 }
166
167 return RangeBetween(t[0].SourceRange(), t[len(t)-1].SourceRange())
168 }
169
170 // TraversalSplit represents a pair of traversals, the first of which is
171 // an absolute traversal and the second of which is relative to the first.
172 //
173 // This is used by calling applications that only populate prefixes of the
174 // traversals in the scope, with Abs representing the part coming from the
175 // scope and Rel representing the remaining steps once that part is
176 // retrieved.
177 type TraversalSplit struct {
178 Abs Traversal
179 Rel Traversal
180 }
181
182 // TraverseAbs traverses from a scope to the value resulting from the
183 // absolute traversal.
184 func (t TraversalSplit) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) {
185 return t.Abs.TraverseAbs(ctx)
186 }
187
188 // TraverseRel traverses from a given value, assumed to be the result of
189 // TraverseAbs on some scope, to a final result for the entire split traversal.
190 func (t TraversalSplit) TraverseRel(val cty.Value) (cty.Value, Diagnostics) {
191 return t.Rel.TraverseRel(val)
192 }
193
194 // Traverse is a convenience function to apply TraverseAbs followed by
195 // TraverseRel.
196 func (t TraversalSplit) Traverse(ctx *EvalContext) (cty.Value, Diagnostics) {
197 v1, diags := t.TraverseAbs(ctx)
198 if diags.HasErrors() {
199 return cty.DynamicVal, diags
200 }
201 v2, newDiags := t.TraverseRel(v1)
202 diags = append(diags, newDiags...)
203 return v2, diags
204 }
205
206 // Join concatenates together the Abs and Rel parts to produce a single
207 // absolute traversal.
208 func (t TraversalSplit) Join() Traversal {
209 return TraversalJoin(t.Abs, t.Rel)
210 }
211
212 // RootName returns the root name for the absolute part of the split.
213 func (t TraversalSplit) RootName() string {
214 return t.Abs.RootName()
215 }
216
217 // A Traverser is a step within a Traversal.
218 type Traverser interface {
219 TraversalStep(cty.Value) (cty.Value, Diagnostics)
220 SourceRange() Range
221 isTraverserSigil() isTraverser
222 }
223
224 // Embed this in a struct to declare it as a Traverser
225 type isTraverser struct {
226 }
227
228 func (tr isTraverser) isTraverserSigil() isTraverser {
229 return isTraverser{}
230 }
231
232 // TraverseRoot looks up a root name in a scope. It is used as the first step
233 // of an absolute Traversal, and cannot itself be traversed directly.
234 type TraverseRoot struct {
235 isTraverser
236 Name string
237 SrcRange Range
238 }
239
240 // TraversalStep on a TraverseName immediately panics, because absolute
241 // traversals cannot be directly traversed.
242 func (tn TraverseRoot) TraversalStep(cty.Value) (cty.Value, Diagnostics) {
243 panic("Cannot traverse an absolute traversal")
244 }
245
246 func (tn TraverseRoot) SourceRange() Range {
247 return tn.SrcRange
248 }
249
250 // TraverseAttr looks up an attribute in its initial value.
251 type TraverseAttr struct {
252 isTraverser
253 Name string
254 SrcRange Range
255 }
256
257 func (tn TraverseAttr) TraversalStep(val cty.Value) (cty.Value, Diagnostics) {
258 if val.IsNull() {
259 return cty.DynamicVal, Diagnostics{
260 {
261 Severity: DiagError,
262 Summary: "Attempt to get attribute from null value",
263 Detail: "This value is null, so it does not have any attributes.",
264 Subject: &tn.SrcRange,
265 },
266 }
267 }
268
269 ty := val.Type()
270 switch {
271 case ty.IsObjectType():
272 if !ty.HasAttribute(tn.Name) {
273 return cty.DynamicVal, Diagnostics{
274 {
275 Severity: DiagError,
276 Summary: "Unsupported attribute",
277 Detail: fmt.Sprintf("This object does not have an attribute named %q.", tn.Name),
278 Subject: &tn.SrcRange,
279 },
280 }
281 }
282
283 if !val.IsKnown() {
284 return cty.UnknownVal(ty.AttributeType(tn.Name)), nil
285 }
286
287 return val.GetAttr(tn.Name), nil
288 case ty.IsMapType():
289 if !val.IsKnown() {
290 return cty.UnknownVal(ty.ElementType()), nil
291 }
292
293 idx := cty.StringVal(tn.Name)
294 if val.HasIndex(idx).False() {
295 return cty.DynamicVal, Diagnostics{
296 {
297 Severity: DiagError,
298 Summary: "Missing map element",
299 Detail: fmt.Sprintf("This map does not have an element with the key %q.", tn.Name),
300 Subject: &tn.SrcRange,
301 },
302 }
303 }
304
305 return val.Index(idx), nil
306 case ty == cty.DynamicPseudoType:
307 return cty.DynamicVal, nil
308 default:
309 return cty.DynamicVal, Diagnostics{
310 {
311 Severity: DiagError,
312 Summary: "Unsupported attribute",
313 Detail: "This value does not have any attributes.",
314 Subject: &tn.SrcRange,
315 },
316 }
317 }
318 }
319
320 func (tn TraverseAttr) SourceRange() Range {
321 return tn.SrcRange
322 }
323
324 // TraverseIndex applies the index operation to its initial value.
325 type TraverseIndex struct {
326 isTraverser
327 Key cty.Value
328 SrcRange Range
329 }
330
331 func (tn TraverseIndex) TraversalStep(val cty.Value) (cty.Value, Diagnostics) {
332 return Index(val, tn.Key, &tn.SrcRange)
333 }
334
335 func (tn TraverseIndex) SourceRange() Range {
336 return tn.SrcRange
337 }
338
339 // TraverseSplat applies the splat operation to its initial value.
340 type TraverseSplat struct {
341 isTraverser
342 Each Traversal
343 SrcRange Range
344 }
345
346 func (tn TraverseSplat) TraversalStep(val cty.Value) (cty.Value, Diagnostics) {
347 panic("TraverseSplat not yet implemented")
348 }
349
350 func (tn TraverseSplat) SourceRange() Range {
351 return tn.SrcRange
352 }