]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/hcl2/hcl/traversal.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hcl / traversal.go
CommitLineData
15c0b25d
AP
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) {
107c1cdb 258 return GetAttr(val, tn.Name, &tn.SrcRange)
15c0b25d
AP
259}
260
261func (tn TraverseAttr) SourceRange() Range {
262 return tn.SrcRange
263}
264
265// TraverseIndex applies the index operation to its initial value.
266type TraverseIndex struct {
267 isTraverser
268 Key cty.Value
269 SrcRange Range
270}
271
272func (tn TraverseIndex) TraversalStep(val cty.Value) (cty.Value, Diagnostics) {
273 return Index(val, tn.Key, &tn.SrcRange)
274}
275
276func (tn TraverseIndex) SourceRange() Range {
277 return tn.SrcRange
278}
279
280// TraverseSplat applies the splat operation to its initial value.
281type TraverseSplat struct {
282 isTraverser
283 Each Traversal
284 SrcRange Range
285}
286
287func (tn TraverseSplat) TraversalStep(val cty.Value) (cty.Value, Diagnostics) {
288 panic("TraverseSplat not yet implemented")
289}
290
291func (tn TraverseSplat) SourceRange() Range {
292 return tn.SrcRange
293}