]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/hcl2/hcldec/spec.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hcldec / spec.go
1 package hcldec
2
3 import (
4 "bytes"
5 "fmt"
6 "sort"
7
8 "github.com/hashicorp/hcl2/hcl"
9 "github.com/zclconf/go-cty/cty"
10 "github.com/zclconf/go-cty/cty/convert"
11 "github.com/zclconf/go-cty/cty/function"
12 )
13
14 // A Spec is a description of how to decode a hcl.Body to a cty.Value.
15 //
16 // The various other types in this package whose names end in "Spec" are
17 // the spec implementations. The most common top-level spec is ObjectSpec,
18 // which decodes body content into a cty.Value of an object type.
19 type Spec interface {
20 // Perform the decode operation on the given body, in the context of
21 // the given block (which might be null), using the given eval context.
22 //
23 // "block" is provided only by the nested calls performed by the spec
24 // types that work on block bodies.
25 decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
26
27 // Return the cty.Type that should be returned when decoding a body with
28 // this spec.
29 impliedType() cty.Type
30
31 // Call the given callback once for each of the nested specs that would
32 // get decoded with the same body and block as the receiver. This should
33 // not descend into the nested specs used when decoding blocks.
34 visitSameBodyChildren(cb visitFunc)
35
36 // Determine the source range of the value that would be returned for the
37 // spec in the given content, in the context of the given block
38 // (which might be null). If the corresponding item is missing, return
39 // a place where it might be inserted.
40 sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range
41 }
42
43 type visitFunc func(spec Spec)
44
45 // An ObjectSpec is a Spec that produces a cty.Value of an object type whose
46 // attributes correspond to the keys of the spec map.
47 type ObjectSpec map[string]Spec
48
49 // attrSpec is implemented by specs that require attributes from the body.
50 type attrSpec interface {
51 attrSchemata() []hcl.AttributeSchema
52 }
53
54 // blockSpec is implemented by specs that require blocks from the body.
55 type blockSpec interface {
56 blockHeaderSchemata() []hcl.BlockHeaderSchema
57 nestedSpec() Spec
58 }
59
60 // specNeedingVariables is implemented by specs that can use variables
61 // from the EvalContext, to declare which variables they need.
62 type specNeedingVariables interface {
63 variablesNeeded(content *hcl.BodyContent) []hcl.Traversal
64 }
65
66 func (s ObjectSpec) visitSameBodyChildren(cb visitFunc) {
67 for _, c := range s {
68 cb(c)
69 }
70 }
71
72 func (s ObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
73 vals := make(map[string]cty.Value, len(s))
74 var diags hcl.Diagnostics
75
76 for k, spec := range s {
77 var kd hcl.Diagnostics
78 vals[k], kd = spec.decode(content, blockLabels, ctx)
79 diags = append(diags, kd...)
80 }
81
82 return cty.ObjectVal(vals), diags
83 }
84
85 func (s ObjectSpec) impliedType() cty.Type {
86 if len(s) == 0 {
87 return cty.EmptyObject
88 }
89
90 attrTypes := make(map[string]cty.Type)
91 for k, childSpec := range s {
92 attrTypes[k] = childSpec.impliedType()
93 }
94 return cty.Object(attrTypes)
95 }
96
97 func (s ObjectSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
98 // This is not great, but the best we can do. In practice, it's rather
99 // strange to ask for the source range of an entire top-level body, since
100 // that's already readily available to the caller.
101 return content.MissingItemRange
102 }
103
104 // A TupleSpec is a Spec that produces a cty.Value of a tuple type whose
105 // elements correspond to the elements of the spec slice.
106 type TupleSpec []Spec
107
108 func (s TupleSpec) visitSameBodyChildren(cb visitFunc) {
109 for _, c := range s {
110 cb(c)
111 }
112 }
113
114 func (s TupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
115 vals := make([]cty.Value, len(s))
116 var diags hcl.Diagnostics
117
118 for i, spec := range s {
119 var ed hcl.Diagnostics
120 vals[i], ed = spec.decode(content, blockLabels, ctx)
121 diags = append(diags, ed...)
122 }
123
124 return cty.TupleVal(vals), diags
125 }
126
127 func (s TupleSpec) impliedType() cty.Type {
128 if len(s) == 0 {
129 return cty.EmptyTuple
130 }
131
132 attrTypes := make([]cty.Type, len(s))
133 for i, childSpec := range s {
134 attrTypes[i] = childSpec.impliedType()
135 }
136 return cty.Tuple(attrTypes)
137 }
138
139 func (s TupleSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
140 // This is not great, but the best we can do. In practice, it's rather
141 // strange to ask for the source range of an entire top-level body, since
142 // that's already readily available to the caller.
143 return content.MissingItemRange
144 }
145
146 // An AttrSpec is a Spec that evaluates a particular attribute expression in
147 // the body and returns its resulting value converted to the requested type,
148 // or produces a diagnostic if the type is incorrect.
149 type AttrSpec struct {
150 Name string
151 Type cty.Type
152 Required bool
153 }
154
155 func (s *AttrSpec) visitSameBodyChildren(cb visitFunc) {
156 // leaf node
157 }
158
159 // specNeedingVariables implementation
160 func (s *AttrSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
161 attr, exists := content.Attributes[s.Name]
162 if !exists {
163 return nil
164 }
165
166 return attr.Expr.Variables()
167 }
168
169 // attrSpec implementation
170 func (s *AttrSpec) attrSchemata() []hcl.AttributeSchema {
171 return []hcl.AttributeSchema{
172 {
173 Name: s.Name,
174 Required: s.Required,
175 },
176 }
177 }
178
179 func (s *AttrSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
180 attr, exists := content.Attributes[s.Name]
181 if !exists {
182 return content.MissingItemRange
183 }
184
185 return attr.Expr.Range()
186 }
187
188 func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
189 attr, exists := content.Attributes[s.Name]
190 if !exists {
191 // We don't need to check required and emit a diagnostic here, because
192 // that would already have happened when building "content".
193 return cty.NullVal(s.Type), nil
194 }
195
196 val, diags := attr.Expr.Value(ctx)
197
198 convVal, err := convert.Convert(val, s.Type)
199 if err != nil {
200 diags = append(diags, &hcl.Diagnostic{
201 Severity: hcl.DiagError,
202 Summary: "Incorrect attribute value type",
203 Detail: fmt.Sprintf(
204 "Inappropriate value for attribute %q: %s.",
205 s.Name, err.Error(),
206 ),
207 Subject: attr.Expr.StartRange().Ptr(),
208 Context: hcl.RangeBetween(attr.NameRange, attr.Expr.StartRange()).Ptr(),
209 })
210 // We'll return an unknown value of the _correct_ type so that the
211 // incomplete result can still be used for some analysis use-cases.
212 val = cty.UnknownVal(s.Type)
213 } else {
214 val = convVal
215 }
216
217 return val, diags
218 }
219
220 func (s *AttrSpec) impliedType() cty.Type {
221 return s.Type
222 }
223
224 // A LiteralSpec is a Spec that produces the given literal value, ignoring
225 // the given body.
226 type LiteralSpec struct {
227 Value cty.Value
228 }
229
230 func (s *LiteralSpec) visitSameBodyChildren(cb visitFunc) {
231 // leaf node
232 }
233
234 func (s *LiteralSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
235 return s.Value, nil
236 }
237
238 func (s *LiteralSpec) impliedType() cty.Type {
239 return s.Value.Type()
240 }
241
242 func (s *LiteralSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
243 // No sensible range to return for a literal, so the caller had better
244 // ensure it doesn't cause any diagnostics.
245 return hcl.Range{
246 Filename: "<unknown>",
247 }
248 }
249
250 // An ExprSpec is a Spec that evaluates the given expression, ignoring the
251 // given body.
252 type ExprSpec struct {
253 Expr hcl.Expression
254 }
255
256 func (s *ExprSpec) visitSameBodyChildren(cb visitFunc) {
257 // leaf node
258 }
259
260 // specNeedingVariables implementation
261 func (s *ExprSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
262 return s.Expr.Variables()
263 }
264
265 func (s *ExprSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
266 return s.Expr.Value(ctx)
267 }
268
269 func (s *ExprSpec) impliedType() cty.Type {
270 // We can't know the type of our expression until we evaluate it
271 return cty.DynamicPseudoType
272 }
273
274 func (s *ExprSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
275 return s.Expr.Range()
276 }
277
278 // A BlockSpec is a Spec that produces a cty.Value by decoding the contents
279 // of a single nested block of a given type, using a nested spec.
280 //
281 // If the Required flag is not set, the nested block may be omitted, in which
282 // case a null value is produced. If it _is_ set, an error diagnostic is
283 // produced if there are no nested blocks of the given type.
284 type BlockSpec struct {
285 TypeName string
286 Nested Spec
287 Required bool
288 }
289
290 func (s *BlockSpec) visitSameBodyChildren(cb visitFunc) {
291 // leaf node ("Nested" does not use the same body)
292 }
293
294 // blockSpec implementation
295 func (s *BlockSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
296 return []hcl.BlockHeaderSchema{
297 {
298 Type: s.TypeName,
299 LabelNames: findLabelSpecs(s.Nested),
300 },
301 }
302 }
303
304 // blockSpec implementation
305 func (s *BlockSpec) nestedSpec() Spec {
306 return s.Nested
307 }
308
309 // specNeedingVariables implementation
310 func (s *BlockSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
311 var childBlock *hcl.Block
312 for _, candidate := range content.Blocks {
313 if candidate.Type != s.TypeName {
314 continue
315 }
316
317 childBlock = candidate
318 break
319 }
320
321 if childBlock == nil {
322 return nil
323 }
324
325 return Variables(childBlock.Body, s.Nested)
326 }
327
328 func (s *BlockSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
329 var diags hcl.Diagnostics
330
331 var childBlock *hcl.Block
332 for _, candidate := range content.Blocks {
333 if candidate.Type != s.TypeName {
334 continue
335 }
336
337 if childBlock != nil {
338 diags = append(diags, &hcl.Diagnostic{
339 Severity: hcl.DiagError,
340 Summary: fmt.Sprintf("Duplicate %s block", s.TypeName),
341 Detail: fmt.Sprintf(
342 "Only one block of type %q is allowed. Previous definition was at %s.",
343 s.TypeName, childBlock.DefRange.String(),
344 ),
345 Subject: &candidate.DefRange,
346 })
347 break
348 }
349
350 childBlock = candidate
351 }
352
353 if childBlock == nil {
354 if s.Required {
355 diags = append(diags, &hcl.Diagnostic{
356 Severity: hcl.DiagError,
357 Summary: fmt.Sprintf("Missing %s block", s.TypeName),
358 Detail: fmt.Sprintf(
359 "A block of type %q is required here.", s.TypeName,
360 ),
361 Subject: &content.MissingItemRange,
362 })
363 }
364 return cty.NullVal(s.Nested.impliedType()), diags
365 }
366
367 if s.Nested == nil {
368 panic("BlockSpec with no Nested Spec")
369 }
370 val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
371 diags = append(diags, childDiags...)
372 return val, diags
373 }
374
375 func (s *BlockSpec) impliedType() cty.Type {
376 return s.Nested.impliedType()
377 }
378
379 func (s *BlockSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
380 var childBlock *hcl.Block
381 for _, candidate := range content.Blocks {
382 if candidate.Type != s.TypeName {
383 continue
384 }
385
386 childBlock = candidate
387 break
388 }
389
390 if childBlock == nil {
391 return content.MissingItemRange
392 }
393
394 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
395 }
396
397 // A BlockListSpec is a Spec that produces a cty list of the results of
398 // decoding all of the nested blocks of a given type, using a nested spec.
399 type BlockListSpec struct {
400 TypeName string
401 Nested Spec
402 MinItems int
403 MaxItems int
404 }
405
406 func (s *BlockListSpec) visitSameBodyChildren(cb visitFunc) {
407 // leaf node ("Nested" does not use the same body)
408 }
409
410 // blockSpec implementation
411 func (s *BlockListSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
412 return []hcl.BlockHeaderSchema{
413 {
414 Type: s.TypeName,
415 LabelNames: findLabelSpecs(s.Nested),
416 },
417 }
418 }
419
420 // blockSpec implementation
421 func (s *BlockListSpec) nestedSpec() Spec {
422 return s.Nested
423 }
424
425 // specNeedingVariables implementation
426 func (s *BlockListSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
427 var ret []hcl.Traversal
428
429 for _, childBlock := range content.Blocks {
430 if childBlock.Type != s.TypeName {
431 continue
432 }
433
434 ret = append(ret, Variables(childBlock.Body, s.Nested)...)
435 }
436
437 return ret
438 }
439
440 func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
441 var diags hcl.Diagnostics
442
443 if s.Nested == nil {
444 panic("BlockListSpec with no Nested Spec")
445 }
446
447 var elems []cty.Value
448 var sourceRanges []hcl.Range
449 for _, childBlock := range content.Blocks {
450 if childBlock.Type != s.TypeName {
451 continue
452 }
453
454 val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
455 diags = append(diags, childDiags...)
456 elems = append(elems, val)
457 sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
458 }
459
460 if len(elems) < s.MinItems {
461 diags = append(diags, &hcl.Diagnostic{
462 Severity: hcl.DiagError,
463 Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName),
464 Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName),
465 Subject: &content.MissingItemRange,
466 })
467 } else if s.MaxItems > 0 && len(elems) > s.MaxItems {
468 diags = append(diags, &hcl.Diagnostic{
469 Severity: hcl.DiagError,
470 Summary: fmt.Sprintf("Too many %s blocks", s.TypeName),
471 Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName),
472 Subject: &sourceRanges[s.MaxItems],
473 })
474 }
475
476 var ret cty.Value
477
478 if len(elems) == 0 {
479 ret = cty.ListValEmpty(s.Nested.impliedType())
480 } else {
481 // Since our target is a list, all of the decoded elements must have the
482 // same type or cty.ListVal will panic below. Different types can arise
483 // if there is an attribute spec of type cty.DynamicPseudoType in the
484 // nested spec; all given values must be convertable to a single type
485 // in order for the result to be considered valid.
486 etys := make([]cty.Type, len(elems))
487 for i, v := range elems {
488 etys[i] = v.Type()
489 }
490 ety, convs := convert.UnifyUnsafe(etys)
491 if ety == cty.NilType {
492 // FIXME: This is a pretty terrible error message.
493 diags = append(diags, &hcl.Diagnostic{
494 Severity: hcl.DiagError,
495 Summary: fmt.Sprintf("Unconsistent argument types in %s blocks", s.TypeName),
496 Detail: "Corresponding attributes in all blocks of this type must be the same.",
497 Subject: &sourceRanges[0],
498 })
499 return cty.DynamicVal, diags
500 }
501 for i, v := range elems {
502 if convs[i] != nil {
503 newV, err := convs[i](v)
504 if err != nil {
505 // FIXME: This is a pretty terrible error message.
506 diags = append(diags, &hcl.Diagnostic{
507 Severity: hcl.DiagError,
508 Summary: fmt.Sprintf("Unconsistent argument types in %s blocks", s.TypeName),
509 Detail: fmt.Sprintf("Block with index %d has inconsistent argument types: %s.", i, err),
510 Subject: &sourceRanges[i],
511 })
512 // Bail early here so we won't panic below in cty.ListVal
513 return cty.DynamicVal, diags
514 }
515 elems[i] = newV
516 }
517 }
518
519 ret = cty.ListVal(elems)
520 }
521
522 return ret, diags
523 }
524
525 func (s *BlockListSpec) impliedType() cty.Type {
526 return cty.List(s.Nested.impliedType())
527 }
528
529 func (s *BlockListSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
530 // We return the source range of the _first_ block of the given type,
531 // since they are not guaranteed to form a contiguous range.
532
533 var childBlock *hcl.Block
534 for _, candidate := range content.Blocks {
535 if candidate.Type != s.TypeName {
536 continue
537 }
538
539 childBlock = candidate
540 break
541 }
542
543 if childBlock == nil {
544 return content.MissingItemRange
545 }
546
547 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
548 }
549
550 // A BlockTupleSpec is a Spec that produces a cty tuple of the results of
551 // decoding all of the nested blocks of a given type, using a nested spec.
552 //
553 // This is similar to BlockListSpec, but it permits the nested blocks to have
554 // different result types in situations where cty.DynamicPseudoType attributes
555 // are present.
556 type BlockTupleSpec struct {
557 TypeName string
558 Nested Spec
559 MinItems int
560 MaxItems int
561 }
562
563 func (s *BlockTupleSpec) visitSameBodyChildren(cb visitFunc) {
564 // leaf node ("Nested" does not use the same body)
565 }
566
567 // blockSpec implementation
568 func (s *BlockTupleSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
569 return []hcl.BlockHeaderSchema{
570 {
571 Type: s.TypeName,
572 LabelNames: findLabelSpecs(s.Nested),
573 },
574 }
575 }
576
577 // blockSpec implementation
578 func (s *BlockTupleSpec) nestedSpec() Spec {
579 return s.Nested
580 }
581
582 // specNeedingVariables implementation
583 func (s *BlockTupleSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
584 var ret []hcl.Traversal
585
586 for _, childBlock := range content.Blocks {
587 if childBlock.Type != s.TypeName {
588 continue
589 }
590
591 ret = append(ret, Variables(childBlock.Body, s.Nested)...)
592 }
593
594 return ret
595 }
596
597 func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
598 var diags hcl.Diagnostics
599
600 if s.Nested == nil {
601 panic("BlockListSpec with no Nested Spec")
602 }
603
604 var elems []cty.Value
605 var sourceRanges []hcl.Range
606 for _, childBlock := range content.Blocks {
607 if childBlock.Type != s.TypeName {
608 continue
609 }
610
611 val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
612 diags = append(diags, childDiags...)
613 elems = append(elems, val)
614 sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
615 }
616
617 if len(elems) < s.MinItems {
618 diags = append(diags, &hcl.Diagnostic{
619 Severity: hcl.DiagError,
620 Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName),
621 Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName),
622 Subject: &content.MissingItemRange,
623 })
624 } else if s.MaxItems > 0 && len(elems) > s.MaxItems {
625 diags = append(diags, &hcl.Diagnostic{
626 Severity: hcl.DiagError,
627 Summary: fmt.Sprintf("Too many %s blocks", s.TypeName),
628 Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName),
629 Subject: &sourceRanges[s.MaxItems],
630 })
631 }
632
633 var ret cty.Value
634
635 if len(elems) == 0 {
636 ret = cty.EmptyTupleVal
637 } else {
638 ret = cty.TupleVal(elems)
639 }
640
641 return ret, diags
642 }
643
644 func (s *BlockTupleSpec) impliedType() cty.Type {
645 // We can't predict our type, because we don't know how many blocks
646 // there will be until we decode.
647 return cty.DynamicPseudoType
648 }
649
650 func (s *BlockTupleSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
651 // We return the source range of the _first_ block of the given type,
652 // since they are not guaranteed to form a contiguous range.
653
654 var childBlock *hcl.Block
655 for _, candidate := range content.Blocks {
656 if candidate.Type != s.TypeName {
657 continue
658 }
659
660 childBlock = candidate
661 break
662 }
663
664 if childBlock == nil {
665 return content.MissingItemRange
666 }
667
668 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
669 }
670
671 // A BlockSetSpec is a Spec that produces a cty set of the results of
672 // decoding all of the nested blocks of a given type, using a nested spec.
673 type BlockSetSpec struct {
674 TypeName string
675 Nested Spec
676 MinItems int
677 MaxItems int
678 }
679
680 func (s *BlockSetSpec) visitSameBodyChildren(cb visitFunc) {
681 // leaf node ("Nested" does not use the same body)
682 }
683
684 // blockSpec implementation
685 func (s *BlockSetSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
686 return []hcl.BlockHeaderSchema{
687 {
688 Type: s.TypeName,
689 LabelNames: findLabelSpecs(s.Nested),
690 },
691 }
692 }
693
694 // blockSpec implementation
695 func (s *BlockSetSpec) nestedSpec() Spec {
696 return s.Nested
697 }
698
699 // specNeedingVariables implementation
700 func (s *BlockSetSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
701 var ret []hcl.Traversal
702
703 for _, childBlock := range content.Blocks {
704 if childBlock.Type != s.TypeName {
705 continue
706 }
707
708 ret = append(ret, Variables(childBlock.Body, s.Nested)...)
709 }
710
711 return ret
712 }
713
714 func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
715 var diags hcl.Diagnostics
716
717 if s.Nested == nil {
718 panic("BlockSetSpec with no Nested Spec")
719 }
720
721 var elems []cty.Value
722 var sourceRanges []hcl.Range
723 for _, childBlock := range content.Blocks {
724 if childBlock.Type != s.TypeName {
725 continue
726 }
727
728 val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
729 diags = append(diags, childDiags...)
730 elems = append(elems, val)
731 sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
732 }
733
734 if len(elems) < s.MinItems {
735 diags = append(diags, &hcl.Diagnostic{
736 Severity: hcl.DiagError,
737 Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName),
738 Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName),
739 Subject: &content.MissingItemRange,
740 })
741 } else if s.MaxItems > 0 && len(elems) > s.MaxItems {
742 diags = append(diags, &hcl.Diagnostic{
743 Severity: hcl.DiagError,
744 Summary: fmt.Sprintf("Too many %s blocks", s.TypeName),
745 Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName),
746 Subject: &sourceRanges[s.MaxItems],
747 })
748 }
749
750 var ret cty.Value
751
752 if len(elems) == 0 {
753 ret = cty.SetValEmpty(s.Nested.impliedType())
754 } else {
755 // Since our target is a set, all of the decoded elements must have the
756 // same type or cty.SetVal will panic below. Different types can arise
757 // if there is an attribute spec of type cty.DynamicPseudoType in the
758 // nested spec; all given values must be convertable to a single type
759 // in order for the result to be considered valid.
760 etys := make([]cty.Type, len(elems))
761 for i, v := range elems {
762 etys[i] = v.Type()
763 }
764 ety, convs := convert.UnifyUnsafe(etys)
765 if ety == cty.NilType {
766 // FIXME: This is a pretty terrible error message.
767 diags = append(diags, &hcl.Diagnostic{
768 Severity: hcl.DiagError,
769 Summary: fmt.Sprintf("Unconsistent argument types in %s blocks", s.TypeName),
770 Detail: "Corresponding attributes in all blocks of this type must be the same.",
771 Subject: &sourceRanges[0],
772 })
773 return cty.DynamicVal, diags
774 }
775 for i, v := range elems {
776 if convs[i] != nil {
777 newV, err := convs[i](v)
778 if err != nil {
779 // FIXME: This is a pretty terrible error message.
780 diags = append(diags, &hcl.Diagnostic{
781 Severity: hcl.DiagError,
782 Summary: fmt.Sprintf("Unconsistent argument types in %s blocks", s.TypeName),
783 Detail: fmt.Sprintf("Block with index %d has inconsistent argument types: %s.", i, err),
784 Subject: &sourceRanges[i],
785 })
786 // Bail early here so we won't panic below in cty.ListVal
787 return cty.DynamicVal, diags
788 }
789 elems[i] = newV
790 }
791 }
792
793 ret = cty.SetVal(elems)
794 }
795
796 return ret, diags
797 }
798
799 func (s *BlockSetSpec) impliedType() cty.Type {
800 return cty.Set(s.Nested.impliedType())
801 }
802
803 func (s *BlockSetSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
804 // We return the source range of the _first_ block of the given type,
805 // since they are not guaranteed to form a contiguous range.
806
807 var childBlock *hcl.Block
808 for _, candidate := range content.Blocks {
809 if candidate.Type != s.TypeName {
810 continue
811 }
812
813 childBlock = candidate
814 break
815 }
816
817 if childBlock == nil {
818 return content.MissingItemRange
819 }
820
821 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
822 }
823
824 // A BlockMapSpec is a Spec that produces a cty map of the results of
825 // decoding all of the nested blocks of a given type, using a nested spec.
826 //
827 // One level of map structure is created for each of the given label names.
828 // There must be at least one given label name.
829 type BlockMapSpec struct {
830 TypeName string
831 LabelNames []string
832 Nested Spec
833 }
834
835 func (s *BlockMapSpec) visitSameBodyChildren(cb visitFunc) {
836 // leaf node ("Nested" does not use the same body)
837 }
838
839 // blockSpec implementation
840 func (s *BlockMapSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
841 return []hcl.BlockHeaderSchema{
842 {
843 Type: s.TypeName,
844 LabelNames: append(s.LabelNames, findLabelSpecs(s.Nested)...),
845 },
846 }
847 }
848
849 // blockSpec implementation
850 func (s *BlockMapSpec) nestedSpec() Spec {
851 return s.Nested
852 }
853
854 // specNeedingVariables implementation
855 func (s *BlockMapSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
856 var ret []hcl.Traversal
857
858 for _, childBlock := range content.Blocks {
859 if childBlock.Type != s.TypeName {
860 continue
861 }
862
863 ret = append(ret, Variables(childBlock.Body, s.Nested)...)
864 }
865
866 return ret
867 }
868
869 func (s *BlockMapSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
870 var diags hcl.Diagnostics
871
872 if s.Nested == nil {
873 panic("BlockMapSpec with no Nested Spec")
874 }
875 if ImpliedType(s).HasDynamicTypes() {
876 panic("cty.DynamicPseudoType attributes may not be used inside a BlockMapSpec")
877 }
878
879 elems := map[string]interface{}{}
880 for _, childBlock := range content.Blocks {
881 if childBlock.Type != s.TypeName {
882 continue
883 }
884
885 childLabels := labelsForBlock(childBlock)
886 val, _, childDiags := decode(childBlock.Body, childLabels[len(s.LabelNames):], ctx, s.Nested, false)
887 targetMap := elems
888 for _, key := range childBlock.Labels[:len(s.LabelNames)-1] {
889 if _, exists := targetMap[key]; !exists {
890 targetMap[key] = make(map[string]interface{})
891 }
892 targetMap = targetMap[key].(map[string]interface{})
893 }
894
895 diags = append(diags, childDiags...)
896
897 key := childBlock.Labels[len(s.LabelNames)-1]
898 if _, exists := targetMap[key]; exists {
899 labelsBuf := bytes.Buffer{}
900 for _, label := range childBlock.Labels {
901 fmt.Fprintf(&labelsBuf, " %q", label)
902 }
903 diags = append(diags, &hcl.Diagnostic{
904 Severity: hcl.DiagError,
905 Summary: fmt.Sprintf("Duplicate %s block", s.TypeName),
906 Detail: fmt.Sprintf(
907 "A block for %s%s was already defined. The %s labels must be unique.",
908 s.TypeName, labelsBuf.String(), s.TypeName,
909 ),
910 Subject: &childBlock.DefRange,
911 })
912 continue
913 }
914
915 targetMap[key] = val
916 }
917
918 if len(elems) == 0 {
919 return cty.MapValEmpty(s.Nested.impliedType()), diags
920 }
921
922 var ctyMap func(map[string]interface{}, int) cty.Value
923 ctyMap = func(raw map[string]interface{}, depth int) cty.Value {
924 vals := make(map[string]cty.Value, len(raw))
925 if depth == 1 {
926 for k, v := range raw {
927 vals[k] = v.(cty.Value)
928 }
929 } else {
930 for k, v := range raw {
931 vals[k] = ctyMap(v.(map[string]interface{}), depth-1)
932 }
933 }
934 return cty.MapVal(vals)
935 }
936
937 return ctyMap(elems, len(s.LabelNames)), diags
938 }
939
940 func (s *BlockMapSpec) impliedType() cty.Type {
941 ret := s.Nested.impliedType()
942 for _ = range s.LabelNames {
943 ret = cty.Map(ret)
944 }
945 return ret
946 }
947
948 func (s *BlockMapSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
949 // We return the source range of the _first_ block of the given type,
950 // since they are not guaranteed to form a contiguous range.
951
952 var childBlock *hcl.Block
953 for _, candidate := range content.Blocks {
954 if candidate.Type != s.TypeName {
955 continue
956 }
957
958 childBlock = candidate
959 break
960 }
961
962 if childBlock == nil {
963 return content.MissingItemRange
964 }
965
966 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
967 }
968
969 // A BlockObjectSpec is a Spec that produces a cty object of the results of
970 // decoding all of the nested blocks of a given type, using a nested spec.
971 //
972 // One level of object structure is created for each of the given label names.
973 // There must be at least one given label name.
974 //
975 // This is similar to BlockMapSpec, but it permits the nested blocks to have
976 // different result types in situations where cty.DynamicPseudoType attributes
977 // are present.
978 type BlockObjectSpec struct {
979 TypeName string
980 LabelNames []string
981 Nested Spec
982 }
983
984 func (s *BlockObjectSpec) visitSameBodyChildren(cb visitFunc) {
985 // leaf node ("Nested" does not use the same body)
986 }
987
988 // blockSpec implementation
989 func (s *BlockObjectSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
990 return []hcl.BlockHeaderSchema{
991 {
992 Type: s.TypeName,
993 LabelNames: append(s.LabelNames, findLabelSpecs(s.Nested)...),
994 },
995 }
996 }
997
998 // blockSpec implementation
999 func (s *BlockObjectSpec) nestedSpec() Spec {
1000 return s.Nested
1001 }
1002
1003 // specNeedingVariables implementation
1004 func (s *BlockObjectSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
1005 var ret []hcl.Traversal
1006
1007 for _, childBlock := range content.Blocks {
1008 if childBlock.Type != s.TypeName {
1009 continue
1010 }
1011
1012 ret = append(ret, Variables(childBlock.Body, s.Nested)...)
1013 }
1014
1015 return ret
1016 }
1017
1018 func (s *BlockObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1019 var diags hcl.Diagnostics
1020
1021 if s.Nested == nil {
1022 panic("BlockObjectSpec with no Nested Spec")
1023 }
1024
1025 elems := map[string]interface{}{}
1026 for _, childBlock := range content.Blocks {
1027 if childBlock.Type != s.TypeName {
1028 continue
1029 }
1030
1031 childLabels := labelsForBlock(childBlock)
1032 val, _, childDiags := decode(childBlock.Body, childLabels[len(s.LabelNames):], ctx, s.Nested, false)
1033 targetMap := elems
1034 for _, key := range childBlock.Labels[:len(s.LabelNames)-1] {
1035 if _, exists := targetMap[key]; !exists {
1036 targetMap[key] = make(map[string]interface{})
1037 }
1038 targetMap = targetMap[key].(map[string]interface{})
1039 }
1040
1041 diags = append(diags, childDiags...)
1042
1043 key := childBlock.Labels[len(s.LabelNames)-1]
1044 if _, exists := targetMap[key]; exists {
1045 labelsBuf := bytes.Buffer{}
1046 for _, label := range childBlock.Labels {
1047 fmt.Fprintf(&labelsBuf, " %q", label)
1048 }
1049 diags = append(diags, &hcl.Diagnostic{
1050 Severity: hcl.DiagError,
1051 Summary: fmt.Sprintf("Duplicate %s block", s.TypeName),
1052 Detail: fmt.Sprintf(
1053 "A block for %s%s was already defined. The %s labels must be unique.",
1054 s.TypeName, labelsBuf.String(), s.TypeName,
1055 ),
1056 Subject: &childBlock.DefRange,
1057 })
1058 continue
1059 }
1060
1061 targetMap[key] = val
1062 }
1063
1064 if len(elems) == 0 {
1065 return cty.EmptyObjectVal, diags
1066 }
1067
1068 var ctyObj func(map[string]interface{}, int) cty.Value
1069 ctyObj = func(raw map[string]interface{}, depth int) cty.Value {
1070 vals := make(map[string]cty.Value, len(raw))
1071 if depth == 1 {
1072 for k, v := range raw {
1073 vals[k] = v.(cty.Value)
1074 }
1075 } else {
1076 for k, v := range raw {
1077 vals[k] = ctyObj(v.(map[string]interface{}), depth-1)
1078 }
1079 }
1080 return cty.ObjectVal(vals)
1081 }
1082
1083 return ctyObj(elems, len(s.LabelNames)), diags
1084 }
1085
1086 func (s *BlockObjectSpec) impliedType() cty.Type {
1087 // We can't predict our type, since we don't know how many blocks are
1088 // present and what labels they have until we decode.
1089 return cty.DynamicPseudoType
1090 }
1091
1092 func (s *BlockObjectSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
1093 // We return the source range of the _first_ block of the given type,
1094 // since they are not guaranteed to form a contiguous range.
1095
1096 var childBlock *hcl.Block
1097 for _, candidate := range content.Blocks {
1098 if candidate.Type != s.TypeName {
1099 continue
1100 }
1101
1102 childBlock = candidate
1103 break
1104 }
1105
1106 if childBlock == nil {
1107 return content.MissingItemRange
1108 }
1109
1110 return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)
1111 }
1112
1113 // A BlockAttrsSpec is a Spec that interprets a single block as if it were
1114 // a map of some element type. That is, each attribute within the block
1115 // becomes a key in the resulting map and the attribute's value becomes the
1116 // element value, after conversion to the given element type. The resulting
1117 // value is a cty.Map of the given element type.
1118 //
1119 // This spec imposes a validation constraint that there be exactly one block
1120 // of the given type name and that this block may contain only attributes. The
1121 // block does not accept any labels.
1122 //
1123 // This is an alternative to an AttrSpec of a map type for situations where
1124 // block syntax is desired. Note that block syntax does not permit dynamic
1125 // keys, construction of the result via a "for" expression, etc. In most cases
1126 // an AttrSpec is preferred if the desired result is a map whose keys are
1127 // chosen by the user rather than by schema.
1128 type BlockAttrsSpec struct {
1129 TypeName string
1130 ElementType cty.Type
1131 Required bool
1132 }
1133
1134 func (s *BlockAttrsSpec) visitSameBodyChildren(cb visitFunc) {
1135 // leaf node
1136 }
1137
1138 // blockSpec implementation
1139 func (s *BlockAttrsSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
1140 return []hcl.BlockHeaderSchema{
1141 {
1142 Type: s.TypeName,
1143 LabelNames: nil,
1144 },
1145 }
1146 }
1147
1148 // blockSpec implementation
1149 func (s *BlockAttrsSpec) nestedSpec() Spec {
1150 // This is an odd case: we aren't actually going to apply a nested spec
1151 // in this case, since we're going to interpret the body directly as
1152 // attributes, but we need to return something non-nil so that the
1153 // decoder will recognize this as a block spec. We won't actually be
1154 // using this for anything at decode time.
1155 return noopSpec{}
1156 }
1157
1158 // specNeedingVariables implementation
1159 func (s *BlockAttrsSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
1160
1161 block, _ := s.findBlock(content)
1162 if block == nil {
1163 return nil
1164 }
1165
1166 var vars []hcl.Traversal
1167
1168 attrs, diags := block.Body.JustAttributes()
1169 if diags.HasErrors() {
1170 return nil
1171 }
1172
1173 for _, attr := range attrs {
1174 vars = append(vars, attr.Expr.Variables()...)
1175 }
1176
1177 // We'll return the variables references in source order so that any
1178 // error messages that result are also in source order.
1179 sort.Slice(vars, func(i, j int) bool {
1180 return vars[i].SourceRange().Start.Byte < vars[j].SourceRange().Start.Byte
1181 })
1182
1183 return vars
1184 }
1185
1186 func (s *BlockAttrsSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1187 var diags hcl.Diagnostics
1188
1189 block, other := s.findBlock(content)
1190 if block == nil {
1191 if s.Required {
1192 diags = append(diags, &hcl.Diagnostic{
1193 Severity: hcl.DiagError,
1194 Summary: fmt.Sprintf("Missing %s block", s.TypeName),
1195 Detail: fmt.Sprintf(
1196 "A block of type %q is required here.", s.TypeName,
1197 ),
1198 Subject: &content.MissingItemRange,
1199 })
1200 }
1201 return cty.NullVal(cty.Map(s.ElementType)), diags
1202 }
1203 if other != nil {
1204 diags = append(diags, &hcl.Diagnostic{
1205 Severity: hcl.DiagError,
1206 Summary: fmt.Sprintf("Duplicate %s block", s.TypeName),
1207 Detail: fmt.Sprintf(
1208 "Only one block of type %q is allowed. Previous definition was at %s.",
1209 s.TypeName, block.DefRange.String(),
1210 ),
1211 Subject: &other.DefRange,
1212 })
1213 }
1214
1215 attrs, attrDiags := block.Body.JustAttributes()
1216 diags = append(diags, attrDiags...)
1217
1218 if len(attrs) == 0 {
1219 return cty.MapValEmpty(s.ElementType), diags
1220 }
1221
1222 vals := make(map[string]cty.Value, len(attrs))
1223 for name, attr := range attrs {
1224 attrVal, attrDiags := attr.Expr.Value(ctx)
1225 diags = append(diags, attrDiags...)
1226
1227 attrVal, err := convert.Convert(attrVal, s.ElementType)
1228 if err != nil {
1229 diags = append(diags, &hcl.Diagnostic{
1230 Severity: hcl.DiagError,
1231 Summary: "Invalid attribute value",
1232 Detail: fmt.Sprintf("Invalid value for attribute of %q block: %s.", s.TypeName, err),
1233 Subject: attr.Expr.Range().Ptr(),
1234 })
1235 attrVal = cty.UnknownVal(s.ElementType)
1236 }
1237
1238 vals[name] = attrVal
1239 }
1240
1241 return cty.MapVal(vals), diags
1242 }
1243
1244 func (s *BlockAttrsSpec) impliedType() cty.Type {
1245 return cty.Map(s.ElementType)
1246 }
1247
1248 func (s *BlockAttrsSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
1249 block, _ := s.findBlock(content)
1250 if block == nil {
1251 return content.MissingItemRange
1252 }
1253 return block.DefRange
1254 }
1255
1256 func (s *BlockAttrsSpec) findBlock(content *hcl.BodyContent) (block *hcl.Block, other *hcl.Block) {
1257 for _, candidate := range content.Blocks {
1258 if candidate.Type != s.TypeName {
1259 continue
1260 }
1261 if block != nil {
1262 return block, candidate
1263 }
1264 block = candidate
1265 }
1266
1267 return block, nil
1268 }
1269
1270 // A BlockLabelSpec is a Spec that returns a cty.String representing the
1271 // label of the block its given body belongs to, if indeed its given body
1272 // belongs to a block. It is a programming error to use this in a non-block
1273 // context, so this spec will panic in that case.
1274 //
1275 // This spec only works in the nested spec within a BlockSpec, BlockListSpec,
1276 // BlockSetSpec or BlockMapSpec.
1277 //
1278 // The full set of label specs used against a particular block must have a
1279 // consecutive set of indices starting at zero. The maximum index found
1280 // defines how many labels the corresponding blocks must have in cty source.
1281 type BlockLabelSpec struct {
1282 Index int
1283 Name string
1284 }
1285
1286 func (s *BlockLabelSpec) visitSameBodyChildren(cb visitFunc) {
1287 // leaf node
1288 }
1289
1290 func (s *BlockLabelSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1291 if s.Index >= len(blockLabels) {
1292 panic("BlockListSpec used in non-block context")
1293 }
1294
1295 return cty.StringVal(blockLabels[s.Index].Value), nil
1296 }
1297
1298 func (s *BlockLabelSpec) impliedType() cty.Type {
1299 return cty.String // labels are always strings
1300 }
1301
1302 func (s *BlockLabelSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
1303 if s.Index >= len(blockLabels) {
1304 panic("BlockListSpec used in non-block context")
1305 }
1306
1307 return blockLabels[s.Index].Range
1308 }
1309
1310 func findLabelSpecs(spec Spec) []string {
1311 maxIdx := -1
1312 var names map[int]string
1313
1314 var visit visitFunc
1315 visit = func(s Spec) {
1316 if ls, ok := s.(*BlockLabelSpec); ok {
1317 if maxIdx < ls.Index {
1318 maxIdx = ls.Index
1319 }
1320 if names == nil {
1321 names = make(map[int]string)
1322 }
1323 names[ls.Index] = ls.Name
1324 }
1325 s.visitSameBodyChildren(visit)
1326 }
1327
1328 visit(spec)
1329
1330 if maxIdx < 0 {
1331 return nil // no labels at all
1332 }
1333
1334 ret := make([]string, maxIdx+1)
1335 for i := range ret {
1336 name := names[i]
1337 if name == "" {
1338 // Should never happen if the spec is conformant, since we require
1339 // consecutive indices starting at zero.
1340 name = fmt.Sprintf("missing%02d", i)
1341 }
1342 ret[i] = name
1343 }
1344
1345 return ret
1346 }
1347
1348 // DefaultSpec is a spec that wraps two specs, evaluating the primary first
1349 // and then evaluating the default if the primary returns a null value.
1350 //
1351 // The two specifications must have the same implied result type for correct
1352 // operation. If not, the result is undefined.
1353 //
1354 // Any requirements imposed by the "Default" spec apply even if "Primary" does
1355 // not return null. For example, if the "Default" spec is for a required
1356 // attribute then that attribute is always required, regardless of the result
1357 // of the "Primary" spec.
1358 //
1359 // The "Default" spec must not describe a nested block, since otherwise the
1360 // result of ChildBlockTypes would not be decidable without evaluation. If
1361 // the default spec _does_ describe a nested block then the result is
1362 // undefined.
1363 type DefaultSpec struct {
1364 Primary Spec
1365 Default Spec
1366 }
1367
1368 func (s *DefaultSpec) visitSameBodyChildren(cb visitFunc) {
1369 cb(s.Primary)
1370 cb(s.Default)
1371 }
1372
1373 func (s *DefaultSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1374 val, diags := s.Primary.decode(content, blockLabels, ctx)
1375 if val.IsNull() {
1376 var moreDiags hcl.Diagnostics
1377 val, moreDiags = s.Default.decode(content, blockLabels, ctx)
1378 diags = append(diags, moreDiags...)
1379 }
1380 return val, diags
1381 }
1382
1383 func (s *DefaultSpec) impliedType() cty.Type {
1384 return s.Primary.impliedType()
1385 }
1386
1387 // attrSpec implementation
1388 func (s *DefaultSpec) attrSchemata() []hcl.AttributeSchema {
1389 // We must pass through the union of both of our nested specs so that
1390 // we'll have both values available in the result.
1391 var ret []hcl.AttributeSchema
1392 if as, ok := s.Primary.(attrSpec); ok {
1393 ret = append(ret, as.attrSchemata()...)
1394 }
1395 if as, ok := s.Default.(attrSpec); ok {
1396 ret = append(ret, as.attrSchemata()...)
1397 }
1398 return ret
1399 }
1400
1401 // blockSpec implementation
1402 func (s *DefaultSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
1403 // Only the primary spec may describe a block, since otherwise
1404 // our nestedSpec method below can't know which to return.
1405 if bs, ok := s.Primary.(blockSpec); ok {
1406 return bs.blockHeaderSchemata()
1407 }
1408 return nil
1409 }
1410
1411 // blockSpec implementation
1412 func (s *DefaultSpec) nestedSpec() Spec {
1413 if bs, ok := s.Primary.(blockSpec); ok {
1414 return bs.nestedSpec()
1415 }
1416 return nil
1417 }
1418
1419 func (s *DefaultSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
1420 // We can't tell from here which of the two specs will ultimately be used
1421 // in our result, so we'll just assume the first. This is usually the right
1422 // choice because the default is often a literal spec that doesn't have a
1423 // reasonable source range to return anyway.
1424 return s.Primary.sourceRange(content, blockLabels)
1425 }
1426
1427 // TransformExprSpec is a spec that wraps another and then evaluates a given
1428 // hcl.Expression on the result.
1429 //
1430 // The implied type of this spec is determined by evaluating the expression
1431 // with an unknown value of the nested spec's implied type, which may cause
1432 // the result to be imprecise. This spec should not be used in situations where
1433 // precise result type information is needed.
1434 type TransformExprSpec struct {
1435 Wrapped Spec
1436 Expr hcl.Expression
1437 TransformCtx *hcl.EvalContext
1438 VarName string
1439 }
1440
1441 func (s *TransformExprSpec) visitSameBodyChildren(cb visitFunc) {
1442 cb(s.Wrapped)
1443 }
1444
1445 func (s *TransformExprSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1446 wrappedVal, diags := s.Wrapped.decode(content, blockLabels, ctx)
1447 if diags.HasErrors() {
1448 // We won't try to run our function in this case, because it'll probably
1449 // generate confusing additional errors that will distract from the
1450 // root cause.
1451 return cty.UnknownVal(s.impliedType()), diags
1452 }
1453
1454 chiCtx := s.TransformCtx.NewChild()
1455 chiCtx.Variables = map[string]cty.Value{
1456 s.VarName: wrappedVal,
1457 }
1458 resultVal, resultDiags := s.Expr.Value(chiCtx)
1459 diags = append(diags, resultDiags...)
1460 return resultVal, diags
1461 }
1462
1463 func (s *TransformExprSpec) impliedType() cty.Type {
1464 wrappedTy := s.Wrapped.impliedType()
1465 chiCtx := s.TransformCtx.NewChild()
1466 chiCtx.Variables = map[string]cty.Value{
1467 s.VarName: cty.UnknownVal(wrappedTy),
1468 }
1469 resultVal, _ := s.Expr.Value(chiCtx)
1470 return resultVal.Type()
1471 }
1472
1473 func (s *TransformExprSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
1474 // We'll just pass through our wrapped range here, even though that's
1475 // not super-accurate, because there's nothing better to return.
1476 return s.Wrapped.sourceRange(content, blockLabels)
1477 }
1478
1479 // TransformFuncSpec is a spec that wraps another and then evaluates a given
1480 // cty function with the result. The given function must expect exactly one
1481 // argument, where the result of the wrapped spec will be passed.
1482 //
1483 // The implied type of this spec is determined by type-checking the function
1484 // with an unknown value of the nested spec's implied type, which may cause
1485 // the result to be imprecise. This spec should not be used in situations where
1486 // precise result type information is needed.
1487 //
1488 // If the given function produces an error when run, this spec will produce
1489 // a non-user-actionable diagnostic message. It's the caller's responsibility
1490 // to ensure that the given function cannot fail for any non-error result
1491 // of the wrapped spec.
1492 type TransformFuncSpec struct {
1493 Wrapped Spec
1494 Func function.Function
1495 }
1496
1497 func (s *TransformFuncSpec) visitSameBodyChildren(cb visitFunc) {
1498 cb(s.Wrapped)
1499 }
1500
1501 func (s *TransformFuncSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1502 wrappedVal, diags := s.Wrapped.decode(content, blockLabels, ctx)
1503 if diags.HasErrors() {
1504 // We won't try to run our function in this case, because it'll probably
1505 // generate confusing additional errors that will distract from the
1506 // root cause.
1507 return cty.UnknownVal(s.impliedType()), diags
1508 }
1509
1510 resultVal, err := s.Func.Call([]cty.Value{wrappedVal})
1511 if err != nil {
1512 // This is not a good example of a diagnostic because it is reporting
1513 // a programming error in the calling application, rather than something
1514 // an end-user could act on.
1515 diags = append(diags, &hcl.Diagnostic{
1516 Severity: hcl.DiagError,
1517 Summary: "Transform function failed",
1518 Detail: fmt.Sprintf("Decoder transform returned an error: %s", err),
1519 Subject: s.sourceRange(content, blockLabels).Ptr(),
1520 })
1521 return cty.UnknownVal(s.impliedType()), diags
1522 }
1523
1524 return resultVal, diags
1525 }
1526
1527 func (s *TransformFuncSpec) impliedType() cty.Type {
1528 wrappedTy := s.Wrapped.impliedType()
1529 resultTy, err := s.Func.ReturnType([]cty.Type{wrappedTy})
1530 if err != nil {
1531 // Should never happen with a correctly-configured spec
1532 return cty.DynamicPseudoType
1533 }
1534
1535 return resultTy
1536 }
1537
1538 func (s *TransformFuncSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
1539 // We'll just pass through our wrapped range here, even though that's
1540 // not super-accurate, because there's nothing better to return.
1541 return s.Wrapped.sourceRange(content, blockLabels)
1542 }
1543
1544 // noopSpec is a placeholder spec that does nothing, used in situations where
1545 // a non-nil placeholder spec is required. It is not exported because there is
1546 // no reason to use it directly; it is always an implementation detail only.
1547 type noopSpec struct {
1548 }
1549
1550 func (s noopSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1551 return cty.NullVal(cty.DynamicPseudoType), nil
1552 }
1553
1554 func (s noopSpec) impliedType() cty.Type {
1555 return cty.DynamicPseudoType
1556 }
1557
1558 func (s noopSpec) visitSameBodyChildren(cb visitFunc) {
1559 // nothing to do
1560 }
1561
1562 func (s noopSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range {
1563 // No useful range for a noopSpec, and nobody should be calling this anyway.
1564 return hcl.Range{
1565 Filename: "noopSpec",
1566 }
1567 }