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