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