]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/lang/blocktoattr/schema.go
Upgrade to 0.12
[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 {
59 // No attribute present and at least one block present, so
60 // we'll need to rewrite this one as a block for a successful
61 // result.
62 appearsAsBlock[name] = struct{}{}
63 }
64 }
65 if !dynamicExpanded {
66 // If we're deciding for a context where dynamic blocks haven't
67 // been expanded yet then we need to probe for those too.
68 probeSchema = hcl.BodySchema{
69 Blocks: []hcl.BlockHeaderSchema{
70 {
71 Type: "dynamic",
72 LabelNames: []string{"type"},
73 },
74 },
75 }
76 content, _, _ := body.PartialContent(&probeSchema)
77 for _, block := range content.Blocks {
78 if _, exists := ambiguousNames[block.Labels[0]]; exists {
79 appearsAsBlock[block.Labels[0]] = struct{}{}
80 }
81 }
82 }
83 }
84
85 for _, attrS := range given.Attributes {
86 if _, exists := appearsAsBlock[attrS.Name]; exists {
87 ret.Blocks = append(ret.Blocks, hcl.BlockHeaderSchema{
88 Type: attrS.Name,
89 })
90 } else {
91 ret.Attributes = append(ret.Attributes, attrS)
92 }
93 }
94
95 // Anything that is specified as a block type in the input schema remains
96 // that way by just passing through verbatim.
97 ret.Blocks = append(ret.Blocks, given.Blocks...)
98
99 return ret
100 }
101
102 // SchemaForCtyElementType converts a cty object type into an
103 // approximately-equivalent configschema.Block representing the element of
104 // a list or set. If the given type is not an object type then this
105 // function will panic.
106 func SchemaForCtyElementType(ty cty.Type) *configschema.Block {
107 atys := ty.AttributeTypes()
108 ret := &configschema.Block{
109 Attributes: make(map[string]*configschema.Attribute, len(atys)),
110 }
111 for name, aty := range atys {
112 ret.Attributes[name] = &configschema.Attribute{
113 Type: aty,
114 Optional: true,
115 }
116 }
117 return ret
118 }
119
120 // SchemaForCtyContainerType converts a cty list-of-object or set-of-object type
121 // into an approximately-equivalent configschema.NestedBlock. If the given type
122 // is not of the expected kind then this function will panic.
123 func SchemaForCtyContainerType(ty cty.Type) *configschema.NestedBlock {
124 var nesting configschema.NestingMode
125 switch {
126 case ty.IsListType():
127 nesting = configschema.NestingList
128 case ty.IsSetType():
129 nesting = configschema.NestingSet
130 default:
131 panic("unsuitable type")
132 }
133 nested := SchemaForCtyElementType(ty.ElementType())
134 return &configschema.NestedBlock{
135 Nesting: nesting,
136 Block: *nested,
137 }
138 }
139
140 // TypeCanBeBlocks returns true if the given type is a list-of-object or
141 // set-of-object type, and would thus be subject to the blocktoattr fixup
142 // if used as an attribute type.
143 func TypeCanBeBlocks(ty cty.Type) bool {
144 return (ty.IsListType() || ty.IsSetType()) && ty.ElementType().IsObjectType()
145 }