aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/hcl/traversal.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hcl/traversal.go')
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcl/traversal.go352
1 files changed, 352 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/hcl/traversal.go b/vendor/github.com/hashicorp/hcl2/hcl/traversal.go
new file mode 100644
index 0000000..24f4c91
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcl/traversal.go
@@ -0,0 +1,352 @@
1package hcl
2
3import (
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).
20type Traversal []Traverser
21
22// TraversalJoin appends a relative traversal to an absolute traversal to
23// produce a new absolute traversal.
24func 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.
41func (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.
62func (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.
122func (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.
139func (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.
151func (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.
160func (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.
177type 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.
184func (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.
190func (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.
196func (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.
208func (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.
213func (t TraversalSplit) RootName() string {
214 return t.Abs.RootName()
215}
216
217// A Traverser is a step within a Traversal.
218type 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
225type isTraverser struct {
226}
227
228func (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.
234type 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.
242func (tn TraverseRoot) TraversalStep(cty.Value) (cty.Value, Diagnostics) {
243 panic("Cannot traverse an absolute traversal")
244}
245
246func (tn TraverseRoot) SourceRange() Range {
247 return tn.SrcRange
248}
249
250// TraverseAttr looks up an attribute in its initial value.
251type TraverseAttr struct {
252 isTraverser
253 Name string
254 SrcRange Range
255}
256
257func (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
320func (tn TraverseAttr) SourceRange() Range {
321 return tn.SrcRange
322}
323
324// TraverseIndex applies the index operation to its initial value.
325type TraverseIndex struct {
326 isTraverser
327 Key cty.Value
328 SrcRange Range
329}
330
331func (tn TraverseIndex) TraversalStep(val cty.Value) (cty.Value, Diagnostics) {
332 return Index(val, tn.Key, &tn.SrcRange)
333}
334
335func (tn TraverseIndex) SourceRange() Range {
336 return tn.SrcRange
337}
338
339// TraverseSplat applies the splat operation to its initial value.
340type TraverseSplat struct {
341 isTraverser
342 Each Traversal
343 SrcRange Range
344}
345
346func (tn TraverseSplat) TraversalStep(val cty.Value) (cty.Value, Diagnostics) {
347 panic("TraverseSplat not yet implemented")
348}
349
350func (tn TraverseSplat) SourceRange() Range {
351 return tn.SrcRange
352}