]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/lang/blocktoattr/schema.go
47a02565922899b2f9967d9f144aecf54f66a307
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / lang / blocktoattr / schema.go
1 package blocktoattr
2
3 import (
4 "github.com/hashicorp/hcl2/hcl"
5 "github.com/hashicorp/terraform/configs/configschema"
6 "github.com/zclconf/go-cty/cty"
7 )
8
9 func ambiguousNames(schema *configschema.Block) map[string]struct{} {
10 if schema == nil {
11 return nil
12 }
13 ambiguousNames := make(map[string]struct{})
14 for name, attrS := range schema.Attributes {
15 aty := attrS.Type
16 if (aty.IsListType() || aty.IsSetType()) && aty.ElementType().IsObjectType() {
17 ambiguousNames[name] = struct{}{}
18 }
19 }
20 return ambiguousNames
21 }
22
23 func effectiveSchema(given *hcl.BodySchema, body hcl.Body, ambiguousNames map[string]struct{}, dynamicExpanded bool) *hcl.BodySchema {
24 ret := &hcl.BodySchema{}
25
26 appearsAsBlock := make(map[string]struct{})
27 {
28 // We'll construct some throwaway schemas here just to probe for
29 // whether each of our ambiguous names seems to be being used as
30 // an attribute or a block. We need to check both because in JSON
31 // syntax we rely on the schema to decide between attribute or block
32 // interpretation and so JSON will always answer yes to both of
33 // these questions and we want to prefer the attribute interpretation
34 // in that case.
35 var probeSchema hcl.BodySchema
36
37 for name := range ambiguousNames {
38 probeSchema = hcl.BodySchema{
39 Attributes: []hcl.AttributeSchema{
40 {
41 Name: name,
42 },
43 },
44 }
45 content, _, _ := body.PartialContent(&probeSchema)
46 if _, exists := content.Attributes[name]; exists {
47 // Can decode as an attribute, so we'll go with that.
48 continue
49 }
50 probeSchema = hcl.BodySchema{
51 Blocks: []hcl.BlockHeaderSchema{
52 {
53 Type: name,
54 },
55 },
56 }
57 content, _, _ = body.PartialContent(&probeSchema)
58 if len(content.Blocks) > 0 || dynamicExpanded {
59 // A dynamic block with an empty iterator returns nothing.
60 // If there's no attribute and we have either a block or a
61 // dynamic expansion, we need to rewrite this one as a
62 // block for a successful result.
63 appearsAsBlock[name] = struct{}{}
64 }
65 }
66 if !dynamicExpanded {
67 // If we're deciding for a context where dynamic blocks haven't
68 // been expanded yet then we need to probe for those too.
69 probeSchema = hcl.BodySchema{
70 Blocks: []hcl.BlockHeaderSchema{
71 {
72 Type: "dynamic",
73 LabelNames: []string{"type"},
74 },
75 },
76 }
77 content, _, _ := body.PartialContent(&probeSchema)
78 for _, block := range content.Blocks {
79 if _, exists := ambiguousNames[block.Labels[0]]; exists {
80 appearsAsBlock[block.Labels[0]] = struct{}{}
81 }
82 }
83 }
84 }
85
86 for _, attrS := range given.Attributes {
87 if _, exists := appearsAsBlock[attrS.Name]; exists {
88 ret.Blocks = append(ret.Blocks, hcl.BlockHeaderSchema{
89 Type: attrS.Name,
90 })
91 } else {
92 ret.Attributes = append(ret.Attributes, attrS)
93 }
94 }
95
96 // Anything that is specified as a block type in the input schema remains
97 // that way by just passing through verbatim.
98 ret.Blocks = append(ret.Blocks, given.Blocks...)
99
100 return ret
101 }
102
103 // SchemaForCtyElementType converts a cty object type into an
104 // approximately-equivalent configschema.Block representing the element of
105 // a list or set. If the given type is not an object type then this
106 // function will panic.
107 func SchemaForCtyElementType(ty cty.Type) *configschema.Block {
108 atys := ty.AttributeTypes()
109 ret := &configschema.Block{
110 Attributes: make(map[string]*configschema.Attribute, len(atys)),
111 }
112 for name, aty := range atys {
113 ret.Attributes[name] = &configschema.Attribute{
114 Type: aty,
115 Optional: true,
116 }
117 }
118 return ret
119 }
120
121 // SchemaForCtyContainerType converts a cty list-of-object or set-of-object type
122 // into an approximately-equivalent configschema.NestedBlock. If the given type
123 // is not of the expected kind then this function will panic.
124 func SchemaForCtyContainerType(ty cty.Type) *configschema.NestedBlock {
125 var nesting configschema.NestingMode
126 switch {
127 case ty.IsListType():
128 nesting = configschema.NestingList
129 case ty.IsSetType():
130 nesting = configschema.NestingSet
131 default:
132 panic("unsuitable type")
133 }
134 nested := SchemaForCtyElementType(ty.ElementType())
135 return &configschema.NestedBlock{
136 Nesting: nesting,
137 Block: *nested,
138 }
139 }
140
141 // TypeCanBeBlocks returns true if the given type is a list-of-object or
142 // set-of-object type, and would thus be subject to the blocktoattr fixup
143 // if used as an attribute type.
144 func TypeCanBeBlocks(ty cty.Type) bool {
145 return (ty.IsListType() || ty.IsSetType()) && ty.ElementType().IsObjectType()
146 }