aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/hcldec
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hcldec')
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcldec/block_labels.go21
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcldec/decode.go36
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcldec/doc.go12
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcldec/gob.go23
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcldec/public.go78
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcldec/schema.go36
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcldec/spec.go998
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcldec/variables.go34
8 files changed, 1238 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/hcldec/block_labels.go b/vendor/github.com/hashicorp/hcl2/hcldec/block_labels.go
new file mode 100644
index 0000000..7e652e9
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcldec/block_labels.go
@@ -0,0 +1,21 @@
1package hcldec
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5)
6
7type blockLabel struct {
8 Value string
9 Range hcl.Range
10}
11
12func labelsForBlock(block *hcl.Block) []blockLabel {
13 ret := make([]blockLabel, len(block.Labels))
14 for i := range block.Labels {
15 ret[i] = blockLabel{
16 Value: block.Labels[i],
17 Range: block.LabelRanges[i],
18 }
19 }
20 return ret
21}
diff --git a/vendor/github.com/hashicorp/hcl2/hcldec/decode.go b/vendor/github.com/hashicorp/hcl2/hcldec/decode.go
new file mode 100644
index 0000000..6cf93fe
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcldec/decode.go
@@ -0,0 +1,36 @@
1package hcldec
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5 "github.com/zclconf/go-cty/cty"
6)
7
8func decode(body hcl.Body, blockLabels []blockLabel, ctx *hcl.EvalContext, spec Spec, partial bool) (cty.Value, hcl.Body, hcl.Diagnostics) {
9 schema := ImpliedSchema(spec)
10
11 var content *hcl.BodyContent
12 var diags hcl.Diagnostics
13 var leftovers hcl.Body
14
15 if partial {
16 content, leftovers, diags = body.PartialContent(schema)
17 } else {
18 content, diags = body.Content(schema)
19 }
20
21 val, valDiags := spec.decode(content, blockLabels, ctx)
22 diags = append(diags, valDiags...)
23
24 return val, leftovers, diags
25}
26
27func impliedType(spec Spec) cty.Type {
28 return spec.impliedType()
29}
30
31func sourceRange(body hcl.Body, blockLabels []blockLabel, spec Spec) hcl.Range {
32 schema := ImpliedSchema(spec)
33 content, _, _ := body.PartialContent(schema)
34
35 return spec.sourceRange(content, blockLabels)
36}
diff --git a/vendor/github.com/hashicorp/hcl2/hcldec/doc.go b/vendor/github.com/hashicorp/hcl2/hcldec/doc.go
new file mode 100644
index 0000000..23bfe54
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcldec/doc.go
@@ -0,0 +1,12 @@
1// Package hcldec provides a higher-level API for unpacking the content of
2// HCL bodies, implemented in terms of the low-level "Content" API exposed
3// by the bodies themselves.
4//
5// It allows decoding an entire nested configuration in a single operation
6// by providing a description of the intended structure.
7//
8// For some applications it may be more convenient to use the "gohcl"
9// package, which has a similar purpose but decodes directly into native
10// Go data types. hcldec instead targets the cty type system, and thus allows
11// a cty-driven application to remain within that type system.
12package hcldec
diff --git a/vendor/github.com/hashicorp/hcl2/hcldec/gob.go b/vendor/github.com/hashicorp/hcl2/hcldec/gob.go
new file mode 100644
index 0000000..e2027cf
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcldec/gob.go
@@ -0,0 +1,23 @@
1package hcldec
2
3import (
4 "encoding/gob"
5)
6
7func init() {
8 // Every Spec implementation should be registered with gob, so that
9 // specs can be sent over gob channels, such as using
10 // github.com/hashicorp/go-plugin with plugins that need to describe
11 // what shape of configuration they are expecting.
12 gob.Register(ObjectSpec(nil))
13 gob.Register(TupleSpec(nil))
14 gob.Register((*AttrSpec)(nil))
15 gob.Register((*LiteralSpec)(nil))
16 gob.Register((*ExprSpec)(nil))
17 gob.Register((*BlockSpec)(nil))
18 gob.Register((*BlockListSpec)(nil))
19 gob.Register((*BlockSetSpec)(nil))
20 gob.Register((*BlockMapSpec)(nil))
21 gob.Register((*BlockLabelSpec)(nil))
22 gob.Register((*DefaultSpec)(nil))
23}
diff --git a/vendor/github.com/hashicorp/hcl2/hcldec/public.go b/vendor/github.com/hashicorp/hcl2/hcldec/public.go
new file mode 100644
index 0000000..5d1f10a
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcldec/public.go
@@ -0,0 +1,78 @@
1package hcldec
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5 "github.com/zclconf/go-cty/cty"
6)
7
8// Decode interprets the given body using the given specification and returns
9// the resulting value. If the given body is not valid per the spec, error
10// diagnostics are returned and the returned value is likely to be incomplete.
11//
12// The ctx argument may be nil, in which case any references to variables or
13// functions will produce error diagnostics.
14func Decode(body hcl.Body, spec Spec, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
15 val, _, diags := decode(body, nil, ctx, spec, false)
16 return val, diags
17}
18
19// PartialDecode is like Decode except that it permits "leftover" items in
20// the top-level body, which are returned as a new body to allow for
21// further processing.
22//
23// Any descendent block bodies are _not_ decoded partially and thus must
24// be fully described by the given specification.
25func PartialDecode(body hcl.Body, spec Spec, ctx *hcl.EvalContext) (cty.Value, hcl.Body, hcl.Diagnostics) {
26 return decode(body, nil, ctx, spec, true)
27}
28
29// ImpliedType returns the value type that should result from decoding the
30// given spec.
31func ImpliedType(spec Spec) cty.Type {
32 return impliedType(spec)
33}
34
35// SourceRange interprets the given body using the given specification and
36// then returns the source range of the value that would be used to
37// fulfill the spec.
38//
39// This can be used if application-level validation detects value errors, to
40// obtain a reasonable SourceRange to use for generated diagnostics. It works
41// best when applied to specific body items (e.g. using AttrSpec, BlockSpec, ...)
42// as opposed to entire bodies using ObjectSpec, TupleSpec. The result will
43// be less useful the broader the specification, so e.g. a spec that returns
44// the entirety of all of the blocks of a given type is likely to be
45// _particularly_ arbitrary and useless.
46//
47// If the given body is not valid per the given spec, the result is best-effort
48// and may not actually be something ideal. It's expected that an application
49// will already have used Decode or PartialDecode earlier and thus had an
50// opportunity to detect and report spec violations.
51func SourceRange(body hcl.Body, spec Spec) hcl.Range {
52 return sourceRange(body, nil, spec)
53}
54
55// ChildBlockTypes returns a map of all of the child block types declared
56// by the given spec, with block type names as keys and the associated
57// nested body specs as values.
58func ChildBlockTypes(spec Spec) map[string]Spec {
59 ret := map[string]Spec{}
60
61 // visitSameBodyChildren walks through the spec structure, calling
62 // the given callback for each descendent spec encountered. We are
63 // interested in the specs that reference attributes and blocks.
64 var visit visitFunc
65 visit = func(s Spec) {
66 if bs, ok := s.(blockSpec); ok {
67 for _, blockS := range bs.blockHeaderSchemata() {
68 ret[blockS.Type] = bs.nestedSpec()
69 }
70 }
71
72 s.visitSameBodyChildren(visit)
73 }
74
75 visit(spec)
76
77 return ret
78}
diff --git a/vendor/github.com/hashicorp/hcl2/hcldec/schema.go b/vendor/github.com/hashicorp/hcl2/hcldec/schema.go
new file mode 100644
index 0000000..b57bd96
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcldec/schema.go
@@ -0,0 +1,36 @@
1package hcldec
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5)
6
7// ImpliedSchema returns the *hcl.BodySchema implied by the given specification.
8// This is the schema that the Decode function will use internally to
9// access the content of a given body.
10func ImpliedSchema(spec Spec) *hcl.BodySchema {
11 var attrs []hcl.AttributeSchema
12 var blocks []hcl.BlockHeaderSchema
13
14 // visitSameBodyChildren walks through the spec structure, calling
15 // the given callback for each descendent spec encountered. We are
16 // interested in the specs that reference attributes and blocks.
17 var visit visitFunc
18 visit = func(s Spec) {
19 if as, ok := s.(attrSpec); ok {
20 attrs = append(attrs, as.attrSchemata()...)
21 }
22
23 if bs, ok := s.(blockSpec); ok {
24 blocks = append(blocks, bs.blockHeaderSchemata()...)
25 }
26
27 s.visitSameBodyChildren(visit)
28 }
29
30 visit(spec)
31
32 return &hcl.BodySchema{
33 Attributes: attrs,
34 Blocks: blocks,
35 }
36}
diff --git a/vendor/github.com/hashicorp/hcl2/hcldec/spec.go b/vendor/github.com/hashicorp/hcl2/hcldec/spec.go
new file mode 100644
index 0000000..25cafcd
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcldec/spec.go
@@ -0,0 +1,998 @@
1package hcldec
2
3import (
4 "bytes"
5 "fmt"
6
7 "github.com/hashicorp/hcl2/hcl"
8 "github.com/zclconf/go-cty/cty"
9 "github.com/zclconf/go-cty/cty/convert"
10 "github.com/zclconf/go-cty/cty/function"
11)
12
13// A Spec is a description of how to decode a hcl.Body to a cty.Value.
14//
15// The various other types in this package whose names end in "Spec" are
16// the spec implementations. The most common top-level spec is ObjectSpec,
17// which decodes body content into a cty.Value of an object type.
18type Spec interface {
19 // Perform the decode operation on the given body, in the context of
20 // the given block (which might be null), using the given eval context.
21 //
22 // "block" is provided only by the nested calls performed by the spec
23 // types that work on block bodies.
24 decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
25
26 // Return the cty.Type that should be returned when decoding a body with
27 // this spec.
28 impliedType() cty.Type
29
30 // Call the given callback once for each of the nested specs that would
31 // get decoded with the same body and block as the receiver. This should
32 // not descend into the nested specs used when decoding blocks.
33 visitSameBodyChildren(cb visitFunc)
34
35 // Determine the source range of the value that would be returned for the
36 // spec in the given content, in the context of the given block
37 // (which might be null). If the corresponding item is missing, return
38 // a place where it might be inserted.
39 sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range
40}
41
42type visitFunc func(spec Spec)
43
44// An ObjectSpec is a Spec that produces a cty.Value of an object type whose
45// attributes correspond to the keys of the spec map.
46type ObjectSpec map[string]Spec
47
48// attrSpec is implemented by specs that require attributes from the body.
49type attrSpec interface {
50 attrSchemata() []hcl.AttributeSchema
51}
52
53// blockSpec is implemented by specs that require blocks from the body.
54type blockSpec interface {
55 blockHeaderSchemata() []hcl.BlockHeaderSchema
56 nestedSpec() Spec
57}
58
59// specNeedingVariables is implemented by specs that can use variables
60// from the EvalContext, to declare which variables they need.
61type specNeedingVariables interface {
62 variablesNeeded(content *hcl.BodyContent) []hcl.Traversal
63}
64
65func (s ObjectSpec) visitSameBodyChildren(cb visitFunc) {
66 for _, c := range s {
67 cb(c)
68 }
69}
70
71func (s ObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
72 vals := make(map[string]cty.Value, len(s))
73 var diags hcl.Diagnostics
74
75 for k, spec := range s {
76 var kd hcl.Diagnostics
77 vals[k], kd = spec.decode(content, blockLabels, ctx)
78 diags = append(diags, kd...)
79 }
80
81 return cty.ObjectVal(vals), diags
82}
83
84func (s ObjectSpec) impliedType() cty.Type {
85 if len(s) == 0 {
86 return cty.EmptyObject
87 }
88
89 attrTypes := make(map[string]cty.Type)
90 for k, childSpec := range s {
91 attrTypes[k] = childSpec.impliedType()
92 }
93 return cty.Object(attrTypes)
94}
95
96func (s ObjectSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
97 // This is not great, but the best we can do. In practice, it's rather
98 // strange to ask for the source range of an entire top-level body, since
99 // that's already readily available to the caller.
100 return content.MissingItemRange
101}
102
103// A TupleSpec is a Spec that produces a cty.Value of a tuple type whose
104// elements correspond to the elements of the spec slice.
105type TupleSpec []Spec
106
107func (s TupleSpec) visitSameBodyChildren(cb visitFunc) {
108 for _, c := range s {
109 cb(c)
110 }
111}
112
113func (s TupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
114 vals := make([]cty.Value, len(s))
115 var diags hcl.Diagnostics
116
117 for i, spec := range s {
118 var ed hcl.Diagnostics
119 vals[i], ed = spec.decode(content, blockLabels, ctx)
120 diags = append(diags, ed...)
121 }
122
123 return cty.TupleVal(vals), diags
124}
125
126func (s TupleSpec) impliedType() cty.Type {
127 if len(s) == 0 {
128 return cty.EmptyTuple
129 }
130
131 attrTypes := make([]cty.Type, len(s))
132 for i, childSpec := range s {
133 attrTypes[i] = childSpec.impliedType()
134 }
135 return cty.Tuple(attrTypes)
136}
137
138func (s TupleSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
139 // This is not great, but the best we can do. In practice, it's rather
140 // strange to ask for the source range of an entire top-level body, since
141 // that's already readily available to the caller.
142 return content.MissingItemRange
143}
144
145// An AttrSpec is a Spec that evaluates a particular attribute expression in
146// the body and returns its resulting value converted to the requested type,
147// or produces a diagnostic if the type is incorrect.
148type AttrSpec struct {
149 Name string
150 Type cty.Type
151 Required bool
152}
153
154func (s *AttrSpec) visitSameBodyChildren(cb visitFunc) {
155 // leaf node
156}
157
158// specNeedingVariables implementation
159func (s *AttrSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
160 attr, exists := content.Attributes[s.Name]
161 if !exists {
162 return nil
163 }
164
165 return attr.Expr.Variables()
166}
167
168// attrSpec implementation
169func (s *AttrSpec) attrSchemata() []hcl.AttributeSchema {
170 return []hcl.AttributeSchema{
171 {
172 Name: s.Name,
173 Required: s.Required,
174 },
175 }
176}
177
178func (s *AttrSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
179 attr, exists := content.Attributes[s.Name]
180 if !exists {
181 return content.MissingItemRange
182 }
183
184 return attr.Expr.Range()
185}
186
187func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
188 attr, exists := content.Attributes[s.Name]
189 if !exists {
190 // We don't need to check required and emit a diagnostic here, because
191 // that would already have happened when building "content".
192 return cty.NullVal(s.Type), nil
193 }
194
195 val, diags := attr.Expr.Value(ctx)
196
197 convVal, err := convert.Convert(val, s.Type)
198 if err != nil {
199 diags = append(diags, &hcl.Diagnostic{
200 Severity: hcl.DiagError,
201 Summary: "Incorrect attribute value type",
202 Detail: fmt.Sprintf(
203 "Inappropriate value for attribute %q: %s.",
204 s.Name, err.Error(),
205 ),
206 Subject: attr.Expr.StartRange().Ptr(),
207 Context: hcl.RangeBetween(attr.NameRange, attr.Expr.StartRange()).Ptr(),
208 })
209 // We'll return an unknown value of the _correct_ type so that the
210 // incomplete result can still be used for some analysis use-cases.
211 val = cty.UnknownVal(s.Type)
212 } else {
213 val = convVal
214 }
215
216 return val, diags
217}
218
219func (s *AttrSpec) impliedType() cty.Type {
220 return s.Type
221}
222
223// A LiteralSpec is a Spec that produces the given literal value, ignoring
224// the given body.
225type LiteralSpec struct {
226 Value cty.Value
227}
228
229func (s *LiteralSpec) visitSameBodyChildren(cb visitFunc) {
230 // leaf node
231}
232
233func (s *LiteralSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
234 return s.Value, nil
235}
236
237func (s *LiteralSpec) impliedType() cty.Type {
238 return s.Value.Type()
239}
240
241func (s *LiteralSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
242 // No sensible range to return for a literal, so the caller had better
243 // ensure it doesn't cause any diagnostics.
244 return hcl.Range{
245 Filename: "<unknown>",
246 }
247}
248
249// An ExprSpec is a Spec that evaluates the given expression, ignoring the
250// given body.
251type ExprSpec struct {
252 Expr hcl.Expression
253}
254
255func (s *ExprSpec) visitSameBodyChildren(cb visitFunc) {
256 // leaf node
257}
258
259// specNeedingVariables implementation
260func (s *ExprSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
261 return s.Expr.Variables()
262}
263
264func (s *ExprSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
265 return s.Expr.Value(ctx)
266}
267
268func (s *ExprSpec) impliedType() cty.Type {
269 // We can't know the type of our expression until we evaluate it
270 return cty.DynamicPseudoType
271}
272
273func (s *ExprSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
274 return s.Expr.Range()
275}
276
277// A BlockSpec is a Spec that produces a cty.Value by decoding the contents
278// of a single nested block of a given type, using a nested spec.
279//
280// If the Required flag is not set, the nested block may be omitted, in which
281// case a null value is produced. If it _is_ set, an error diagnostic is
282// produced if there are no nested blocks of the given type.
283type BlockSpec struct {
284 TypeName string
285 Nested Spec
286 Required bool
287}
288
289func (s *BlockSpec) visitSameBodyChildren(cb visitFunc) {
290 // leaf node ("Nested" does not use the same body)
291}
292
293// blockSpec implementation
294func (s *BlockSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
295 return []hcl.BlockHeaderSchema{
296 {
297 Type: s.TypeName,
298 LabelNames: findLabelSpecs(s.Nested),
299 },
300 }
301}
302
303// blockSpec implementation
304func (s *BlockSpec) nestedSpec() Spec {
305 return s.Nested
306}
307
308// specNeedingVariables implementation
309func (s *BlockSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
310 var childBlock *hcl.Block
311 for _, candidate := range content.Blocks {
312 if candidate.Type != s.TypeName {
313 continue
314 }
315
316 childBlock = candidate
317 break
318 }
319
320 if childBlock == nil {
321 return nil
322 }
323
324 return Variables(childBlock.Body, s.Nested)
325}
326
327func (s *BlockSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
328 var diags hcl.Diagnostics
329
330 var childBlock *hcl.Block
331 for _, candidate := range content.Blocks {
332 if candidate.Type != s.TypeName {
333 continue
334 }
335
336 if childBlock != nil {
337 diags = append(diags, &hcl.Diagnostic{
338 Severity: hcl.DiagError,
339 Summary: fmt.Sprintf("Duplicate %s block", s.TypeName),
340 Detail: fmt.Sprintf(
341 "Only one block of type %q is allowed. Previous definition was at %s.",
342 s.TypeName, childBlock.DefRange.String(),
343 ),
344 Subject: &candidate.DefRange,
345 })
346 break
347 }
348
349 childBlock = candidate
350 }
351
352 if childBlock == nil {
353 if s.Required {
354 diags = append(diags, &hcl.Diagnostic{
355 Severity: hcl.DiagError,
356 Summary: fmt.Sprintf("Missing %s block", s.TypeName),
357 Detail: fmt.Sprintf(
358 "A block of type %q is required here.", s.TypeName,
359 ),
360 Subject: &content.MissingItemRange,
361 })
362 }
363 return cty.NullVal(s.Nested.impliedType()), diags
364 }
365
366 if s.Nested == nil {
367 panic("BlockSpec with no Nested Spec")
368 }
369 val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
370 diags = append(diags, childDiags...)
371 return val, diags
372}
373
374func (s *BlockSpec) impliedType() cty.Type {
375 return s.Nested.impliedType()
376}
377
378func (s *BlockSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
379 var childBlock *hcl.Block
380 for _, candidate := range content.Blocks {
381 if candidate.Type != s.TypeName {
382 continue
383 }
384
385 childBlock = candidate
386 break
387 }
388
389 if childBlock == nil {
390 return content.MissingItemRange
391 }
392
393 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
394}
395
396// A BlockListSpec is a Spec that produces a cty list of the results of
397// decoding all of the nested blocks of a given type, using a nested spec.
398type BlockListSpec struct {
399 TypeName string
400 Nested Spec
401 MinItems int
402 MaxItems int
403}
404
405func (s *BlockListSpec) visitSameBodyChildren(cb visitFunc) {
406 // leaf node ("Nested" does not use the same body)
407}
408
409// blockSpec implementation
410func (s *BlockListSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
411 return []hcl.BlockHeaderSchema{
412 {
413 Type: s.TypeName,
414 LabelNames: findLabelSpecs(s.Nested),
415 },
416 }
417}
418
419// blockSpec implementation
420func (s *BlockListSpec) nestedSpec() Spec {
421 return s.Nested
422}
423
424// specNeedingVariables implementation
425func (s *BlockListSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
426 var ret []hcl.Traversal
427
428 for _, childBlock := range content.Blocks {
429 if childBlock.Type != s.TypeName {
430 continue
431 }
432
433 ret = append(ret, Variables(childBlock.Body, s.Nested)...)
434 }
435
436 return ret
437}
438
439func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
440 var diags hcl.Diagnostics
441
442 if s.Nested == nil {
443 panic("BlockListSpec with no Nested Spec")
444 }
445
446 var elems []cty.Value
447 var sourceRanges []hcl.Range
448 for _, childBlock := range content.Blocks {
449 if childBlock.Type != s.TypeName {
450 continue
451 }
452
453 val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
454 diags = append(diags, childDiags...)
455 elems = append(elems, val)
456 sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
457 }
458
459 if len(elems) < s.MinItems {
460 diags = append(diags, &hcl.Diagnostic{
461 Severity: hcl.DiagError,
462 Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName),
463 Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName),
464 Subject: &content.MissingItemRange,
465 })
466 } else if s.MaxItems > 0 && len(elems) > s.MaxItems {
467 diags = append(diags, &hcl.Diagnostic{
468 Severity: hcl.DiagError,
469 Summary: fmt.Sprintf("Too many %s blocks", s.TypeName),
470 Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName),
471 Subject: &sourceRanges[s.MaxItems],
472 })
473 }
474
475 var ret cty.Value
476
477 if len(elems) == 0 {
478 ret = cty.ListValEmpty(s.Nested.impliedType())
479 } else {
480 ret = cty.ListVal(elems)
481 }
482
483 return ret, diags
484}
485
486func (s *BlockListSpec) impliedType() cty.Type {
487 return cty.List(s.Nested.impliedType())
488}
489
490func (s *BlockListSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
491 // We return the source range of the _first_ block of the given type,
492 // since they are not guaranteed to form a contiguous range.
493
494 var childBlock *hcl.Block
495 for _, candidate := range content.Blocks {
496 if candidate.Type != s.TypeName {
497 continue
498 }
499
500 childBlock = candidate
501 break
502 }
503
504 if childBlock == nil {
505 return content.MissingItemRange
506 }
507
508 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
509}
510
511// A BlockSetSpec is a Spec that produces a cty set of the results of
512// decoding all of the nested blocks of a given type, using a nested spec.
513type BlockSetSpec struct {
514 TypeName string
515 Nested Spec
516 MinItems int
517 MaxItems int
518}
519
520func (s *BlockSetSpec) visitSameBodyChildren(cb visitFunc) {
521 // leaf node ("Nested" does not use the same body)
522}
523
524// blockSpec implementation
525func (s *BlockSetSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
526 return []hcl.BlockHeaderSchema{
527 {
528 Type: s.TypeName,
529 LabelNames: findLabelSpecs(s.Nested),
530 },
531 }
532}
533
534// blockSpec implementation
535func (s *BlockSetSpec) nestedSpec() Spec {
536 return s.Nested
537}
538
539// specNeedingVariables implementation
540func (s *BlockSetSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
541 var ret []hcl.Traversal
542
543 for _, childBlock := range content.Blocks {
544 if childBlock.Type != s.TypeName {
545 continue
546 }
547
548 ret = append(ret, Variables(childBlock.Body, s.Nested)...)
549 }
550
551 return ret
552}
553
554func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
555 var diags hcl.Diagnostics
556
557 if s.Nested == nil {
558 panic("BlockSetSpec with no Nested Spec")
559 }
560
561 var elems []cty.Value
562 var sourceRanges []hcl.Range
563 for _, childBlock := range content.Blocks {
564 if childBlock.Type != s.TypeName {
565 continue
566 }
567
568 val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
569 diags = append(diags, childDiags...)
570 elems = append(elems, val)
571 sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
572 }
573
574 if len(elems) < s.MinItems {
575 diags = append(diags, &hcl.Diagnostic{
576 Severity: hcl.DiagError,
577 Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName),
578 Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName),
579 Subject: &content.MissingItemRange,
580 })
581 } else if s.MaxItems > 0 && len(elems) > s.MaxItems {
582 diags = append(diags, &hcl.Diagnostic{
583 Severity: hcl.DiagError,
584 Summary: fmt.Sprintf("Too many %s blocks", s.TypeName),
585 Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName),
586 Subject: &sourceRanges[s.MaxItems],
587 })
588 }
589
590 var ret cty.Value
591
592 if len(elems) == 0 {
593 ret = cty.SetValEmpty(s.Nested.impliedType())
594 } else {
595 ret = cty.SetVal(elems)
596 }
597
598 return ret, diags
599}
600
601func (s *BlockSetSpec) impliedType() cty.Type {
602 return cty.Set(s.Nested.impliedType())
603}
604
605func (s *BlockSetSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
606 // We return the source range of the _first_ block of the given type,
607 // since they are not guaranteed to form a contiguous range.
608
609 var childBlock *hcl.Block
610 for _, candidate := range content.Blocks {
611 if candidate.Type != s.TypeName {
612 continue
613 }
614
615 childBlock = candidate
616 break
617 }
618
619 if childBlock == nil {
620 return content.MissingItemRange
621 }
622
623 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
624}
625
626// A BlockMapSpec is a Spec that produces a cty map of the results of
627// decoding all of the nested blocks of a given type, using a nested spec.
628//
629// One level of map structure is created for each of the given label names.
630// There must be at least one given label name.
631type BlockMapSpec struct {
632 TypeName string
633 LabelNames []string
634 Nested Spec
635}
636
637func (s *BlockMapSpec) visitSameBodyChildren(cb visitFunc) {
638 // leaf node ("Nested" does not use the same body)
639}
640
641// blockSpec implementation
642func (s *BlockMapSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
643 return []hcl.BlockHeaderSchema{
644 {
645 Type: s.TypeName,
646 LabelNames: append(s.LabelNames, findLabelSpecs(s.Nested)...),
647 },
648 }
649}
650
651// blockSpec implementation
652func (s *BlockMapSpec) nestedSpec() Spec {
653 return s.Nested
654}
655
656// specNeedingVariables implementation
657func (s *BlockMapSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
658 var ret []hcl.Traversal
659
660 for _, childBlock := range content.Blocks {
661 if childBlock.Type != s.TypeName {
662 continue
663 }
664
665 ret = append(ret, Variables(childBlock.Body, s.Nested)...)
666 }
667
668 return ret
669}
670
671func (s *BlockMapSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
672 var diags hcl.Diagnostics
673
674 if s.Nested == nil {
675 panic("BlockSetSpec with no Nested Spec")
676 }
677
678 elems := map[string]interface{}{}
679 for _, childBlock := range content.Blocks {
680 if childBlock.Type != s.TypeName {
681 continue
682 }
683
684 childLabels := labelsForBlock(childBlock)
685 val, _, childDiags := decode(childBlock.Body, childLabels[len(s.LabelNames):], ctx, s.Nested, false)
686 targetMap := elems
687 for _, key := range childBlock.Labels[:len(s.LabelNames)-1] {
688 if _, exists := targetMap[key]; !exists {
689 targetMap[key] = make(map[string]interface{})
690 }
691 targetMap = targetMap[key].(map[string]interface{})
692 }
693
694 diags = append(diags, childDiags...)
695
696 key := childBlock.Labels[len(s.LabelNames)-1]
697 if _, exists := targetMap[key]; exists {
698 labelsBuf := bytes.Buffer{}
699 for _, label := range childBlock.Labels {
700 fmt.Fprintf(&labelsBuf, " %q", label)
701 }
702 diags = append(diags, &hcl.Diagnostic{
703 Severity: hcl.DiagError,
704 Summary: fmt.Sprintf("Duplicate %s block", s.TypeName),
705 Detail: fmt.Sprintf(
706 "A block for %s%s was already defined. The %s labels must be unique.",
707 s.TypeName, labelsBuf.String(), s.TypeName,
708 ),
709 Subject: &childBlock.DefRange,
710 })
711 continue
712 }
713
714 targetMap[key] = val
715 }
716
717 if len(elems) == 0 {
718 return cty.MapValEmpty(s.Nested.impliedType()), diags
719 }
720
721 var ctyMap func(map[string]interface{}, int) cty.Value
722 ctyMap = func(raw map[string]interface{}, depth int) cty.Value {
723 vals := make(map[string]cty.Value, len(raw))
724 if depth == 1 {
725 for k, v := range raw {
726 vals[k] = v.(cty.Value)
727 }
728 } else {
729 for k, v := range raw {
730 vals[k] = ctyMap(v.(map[string]interface{}), depth-1)
731 }
732 }
733 return cty.MapVal(vals)
734 }
735
736 return ctyMap(elems, len(s.LabelNames)), diags
737}
738
739func (s *BlockMapSpec) impliedType() cty.Type {
740 ret := s.Nested.impliedType()
741 for _ = range s.LabelNames {
742 ret = cty.Map(ret)
743 }
744 return ret
745}
746
747func (s *BlockMapSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
748 // We return the source range of the _first_ block of the given type,
749 // since they are not guaranteed to form a contiguous range.
750
751 var childBlock *hcl.Block
752 for _, candidate := range content.Blocks {
753 if candidate.Type != s.TypeName {
754 continue
755 }
756
757 childBlock = candidate
758 break
759 }
760
761 if childBlock == nil {
762 return content.MissingItemRange
763 }
764
765 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
766}
767
768// A BlockLabelSpec is a Spec that returns a cty.String representing the
769// label of the block its given body belongs to, if indeed its given body
770// belongs to a block. It is a programming error to use this in a non-block
771// context, so this spec will panic in that case.
772//
773// This spec only works in the nested spec within a BlockSpec, BlockListSpec,
774// BlockSetSpec or BlockMapSpec.
775//
776// The full set of label specs used against a particular block must have a
777// consecutive set of indices starting at zero. The maximum index found
778// defines how many labels the corresponding blocks must have in cty source.
779type BlockLabelSpec struct {
780 Index int
781 Name string
782}
783
784func (s *BlockLabelSpec) visitSameBodyChildren(cb visitFunc) {
785 // leaf node
786}
787
788func (s *BlockLabelSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
789 if s.Index >= len(blockLabels) {
790 panic("BlockListSpec used in non-block context")
791 }
792
793 return cty.StringVal(blockLabels[s.Index].Value), nil
794}
795
796func (s *BlockLabelSpec) impliedType() cty.Type {
797 return cty.String // labels are always strings
798}
799
800func (s *BlockLabelSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
801 if s.Index >= len(blockLabels) {
802 panic("BlockListSpec used in non-block context")
803 }
804
805 return blockLabels[s.Index].Range
806}
807
808func findLabelSpecs(spec Spec) []string {
809 maxIdx := -1
810 var names map[int]string
811
812 var visit visitFunc
813 visit = func(s Spec) {
814 if ls, ok := s.(*BlockLabelSpec); ok {
815 if maxIdx < ls.Index {
816 maxIdx = ls.Index
817 }
818 if names == nil {
819 names = make(map[int]string)
820 }
821 names[ls.Index] = ls.Name
822 }
823 s.visitSameBodyChildren(visit)
824 }
825
826 visit(spec)
827
828 if maxIdx < 0 {
829 return nil // no labels at all
830 }
831
832 ret := make([]string, maxIdx+1)
833 for i := range ret {
834 name := names[i]
835 if name == "" {
836 // Should never happen if the spec is conformant, since we require
837 // consecutive indices starting at zero.
838 name = fmt.Sprintf("missing%02d", i)
839 }
840 ret[i] = name
841 }
842
843 return ret
844}
845
846// DefaultSpec is a spec that wraps two specs, evaluating the primary first
847// and then evaluating the default if the primary returns a null value.
848//
849// The two specifications must have the same implied result type for correct
850// operation. If not, the result is undefined.
851type DefaultSpec struct {
852 Primary Spec
853 Default Spec
854}
855
856func (s *DefaultSpec) visitSameBodyChildren(cb visitFunc) {
857 cb(s.Primary)
858 cb(s.Default)
859}
860
861func (s *DefaultSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
862 val, diags := s.Primary.decode(content, blockLabels, ctx)
863 if val.IsNull() {
864 var moreDiags hcl.Diagnostics
865 val, moreDiags = s.Default.decode(content, blockLabels, ctx)
866 diags = append(diags, moreDiags...)
867 }
868 return val, diags
869}
870
871func (s *DefaultSpec) impliedType() cty.Type {
872 return s.Primary.impliedType()
873}
874
875func (s *DefaultSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
876 // We can't tell from here which of the two specs will ultimately be used
877 // in our result, so we'll just assume the first. This is usually the right
878 // choice because the default is often a literal spec that doesn't have a
879 // reasonable source range to return anyway.
880 return s.Primary.sourceRange(content, blockLabels)
881}
882
883// TransformExprSpec is a spec that wraps another and then evaluates a given
884// hcl.Expression on the result.
885//
886// The implied type of this spec is determined by evaluating the expression
887// with an unknown value of the nested spec's implied type, which may cause
888// the result to be imprecise. This spec should not be used in situations where
889// precise result type information is needed.
890type TransformExprSpec struct {
891 Wrapped Spec
892 Expr hcl.Expression
893 TransformCtx *hcl.EvalContext
894 VarName string
895}
896
897func (s *TransformExprSpec) visitSameBodyChildren(cb visitFunc) {
898 cb(s.Wrapped)
899}
900
901func (s *TransformExprSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
902 wrappedVal, diags := s.Wrapped.decode(content, blockLabels, ctx)
903 if diags.HasErrors() {
904 // We won't try to run our function in this case, because it'll probably
905 // generate confusing additional errors that will distract from the
906 // root cause.
907 return cty.UnknownVal(s.impliedType()), diags
908 }
909
910 chiCtx := s.TransformCtx.NewChild()
911 chiCtx.Variables = map[string]cty.Value{
912 s.VarName: wrappedVal,
913 }
914 resultVal, resultDiags := s.Expr.Value(chiCtx)
915 diags = append(diags, resultDiags...)
916 return resultVal, diags
917}
918
919func (s *TransformExprSpec) impliedType() cty.Type {
920 wrappedTy := s.Wrapped.impliedType()
921 chiCtx := s.TransformCtx.NewChild()
922 chiCtx.Variables = map[string]cty.Value{
923 s.VarName: cty.UnknownVal(wrappedTy),
924 }
925 resultVal, _ := s.Expr.Value(chiCtx)
926 return resultVal.Type()
927}
928
929func (s *TransformExprSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
930 // We'll just pass through our wrapped range here, even though that's
931 // not super-accurate, because there's nothing better to return.
932 return s.Wrapped.sourceRange(content, blockLabels)
933}
934
935// TransformFuncSpec is a spec that wraps another and then evaluates a given
936// cty function with the result. The given function must expect exactly one
937// argument, where the result of the wrapped spec will be passed.
938//
939// The implied type of this spec is determined by type-checking the function
940// with an unknown value of the nested spec's implied type, which may cause
941// the result to be imprecise. This spec should not be used in situations where
942// precise result type information is needed.
943//
944// If the given function produces an error when run, this spec will produce
945// a non-user-actionable diagnostic message. It's the caller's responsibility
946// to ensure that the given function cannot fail for any non-error result
947// of the wrapped spec.
948type TransformFuncSpec struct {
949 Wrapped Spec
950 Func function.Function
951}
952
953func (s *TransformFuncSpec) visitSameBodyChildren(cb visitFunc) {
954 cb(s.Wrapped)
955}
956
957func (s *TransformFuncSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
958 wrappedVal, diags := s.Wrapped.decode(content, blockLabels, ctx)
959 if diags.HasErrors() {
960 // We won't try to run our function in this case, because it'll probably
961 // generate confusing additional errors that will distract from the
962 // root cause.
963 return cty.UnknownVal(s.impliedType()), diags
964 }
965
966 resultVal, err := s.Func.Call([]cty.Value{wrappedVal})
967 if err != nil {
968 // This is not a good example of a diagnostic because it is reporting
969 // a programming error in the calling application, rather than something
970 // an end-user could act on.
971 diags = append(diags, &hcl.Diagnostic{
972 Severity: hcl.DiagError,
973 Summary: "Transform function failed",
974 Detail: fmt.Sprintf("Decoder transform returned an error: %s", err),
975 Subject: s.sourceRange(content, blockLabels).Ptr(),
976 })
977 return cty.UnknownVal(s.impliedType()), diags
978 }
979
980 return resultVal, diags
981}
982
983func (s *TransformFuncSpec) impliedType() cty.Type {
984 wrappedTy := s.Wrapped.impliedType()
985 resultTy, err := s.Func.ReturnType([]cty.Type{wrappedTy})
986 if err != nil {
987 // Should never happen with a correctly-configured spec
988 return cty.DynamicPseudoType
989 }
990
991 return resultTy
992}
993
994func (s *TransformFuncSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
995 // We'll just pass through our wrapped range here, even though that's
996 // not super-accurate, because there's nothing better to return.
997 return s.Wrapped.sourceRange(content, blockLabels)
998}
diff --git a/vendor/github.com/hashicorp/hcl2/hcldec/variables.go b/vendor/github.com/hashicorp/hcl2/hcldec/variables.go
new file mode 100644
index 0000000..427b0d0
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcldec/variables.go
@@ -0,0 +1,34 @@
1package hcldec
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5)
6
7// Variables processes the given body with the given spec and returns a
8// list of the variable traversals that would be required to decode
9// the same pairing of body and spec.
10//
11// This can be used to conditionally populate the variables in the EvalContext
12// passed to Decode, for applications where a static scope is insufficient.
13//
14// If the given body is not compliant with the given schema, the result may
15// be incomplete, but that's assumed to be okay because the eventual call
16// to Decode will produce error diagnostics anyway.
17func Variables(body hcl.Body, spec Spec) []hcl.Traversal {
18 schema := ImpliedSchema(spec)
19
20 content, _, _ := body.PartialContent(schema)
21
22 var vars []hcl.Traversal
23
24 if vs, ok := spec.(specNeedingVariables); ok {
25 vars = append(vars, vs.variablesNeeded(content)...)
26 }
27 spec.visitSameBodyChildren(func(s Spec) {
28 if vs, ok := s.(specNeedingVariables); ok {
29 vars = append(vars, vs.variablesNeeded(content)...)
30 }
31 })
32
33 return vars
34}