]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | package configschema |
2 | ||
3 | import ( | |
4 | "github.com/hashicorp/hcl2/hcldec" | |
5 | ) | |
6 | ||
7 | var mapLabelNames = []string{"key"} | |
8 | ||
9 | // DecoderSpec returns a hcldec.Spec that can be used to decode a HCL Body | |
10 | // using the facilities in the hcldec package. | |
11 | // | |
12 | // The returned specification is guaranteed to return a value of the same type | |
13 | // returned by method ImpliedType, but it may contain null values if any of the | |
14 | // block attributes are defined as optional and/or computed respectively. | |
15 | func (b *Block) DecoderSpec() hcldec.Spec { | |
16 | ret := hcldec.ObjectSpec{} | |
17 | if b == nil { | |
18 | return ret | |
19 | } | |
20 | ||
21 | for name, attrS := range b.Attributes { | |
22 | ret[name] = attrS.decoderSpec(name) | |
23 | } | |
24 | ||
25 | for name, blockS := range b.BlockTypes { | |
26 | if _, exists := ret[name]; exists { | |
27 | // This indicates an invalid schema, since it's not valid to | |
28 | // define both an attribute and a block type of the same name. | |
29 | // However, we don't raise this here since it's checked by | |
30 | // InternalValidate. | |
31 | continue | |
32 | } | |
33 | ||
34 | childSpec := blockS.Block.DecoderSpec() | |
35 | ||
863486a6 AG |
36 | // We can only validate 0 or 1 for MinItems, because a dynamic block |
37 | // may satisfy any number of min items while only having a single | |
38 | // block in the config. | |
39 | minItems := 0 | |
40 | if blockS.MinItems > 1 { | |
41 | minItems = 1 | |
42 | } | |
43 | ||
107c1cdb ND |
44 | switch blockS.Nesting { |
45 | case NestingSingle, NestingGroup: | |
46 | ret[name] = &hcldec.BlockSpec{ | |
47 | TypeName: name, | |
48 | Nested: childSpec, | |
49 | Required: blockS.MinItems == 1 && blockS.MaxItems >= 1, | |
50 | } | |
51 | if blockS.Nesting == NestingGroup { | |
52 | ret[name] = &hcldec.DefaultSpec{ | |
53 | Primary: ret[name], | |
54 | Default: &hcldec.LiteralSpec{ | |
55 | Value: blockS.EmptyValue(), | |
56 | }, | |
57 | } | |
58 | } | |
59 | case NestingList: | |
60 | // We prefer to use a list where possible, since it makes our | |
61 | // implied type more complete, but if there are any | |
62 | // dynamically-typed attributes inside we must use a tuple | |
63 | // instead, at the expense of our type then not being predictable. | |
64 | if blockS.Block.ImpliedType().HasDynamicTypes() { | |
65 | ret[name] = &hcldec.BlockTupleSpec{ | |
66 | TypeName: name, | |
67 | Nested: childSpec, | |
863486a6 | 68 | MinItems: minItems, |
107c1cdb ND |
69 | MaxItems: blockS.MaxItems, |
70 | } | |
71 | } else { | |
72 | ret[name] = &hcldec.BlockListSpec{ | |
73 | TypeName: name, | |
74 | Nested: childSpec, | |
863486a6 | 75 | MinItems: minItems, |
107c1cdb ND |
76 | MaxItems: blockS.MaxItems, |
77 | } | |
78 | } | |
79 | case NestingSet: | |
80 | // We forbid dynamically-typed attributes inside NestingSet in | |
81 | // InternalValidate, so we don't do anything special to handle | |
82 | // that here. (There is no set analog to tuple and object types, | |
83 | // because cty's set implementation depends on knowing the static | |
84 | // type in order to properly compute its internal hashes.) | |
85 | ret[name] = &hcldec.BlockSetSpec{ | |
86 | TypeName: name, | |
87 | Nested: childSpec, | |
863486a6 | 88 | MinItems: minItems, |
107c1cdb ND |
89 | MaxItems: blockS.MaxItems, |
90 | } | |
91 | case NestingMap: | |
92 | // We prefer to use a list where possible, since it makes our | |
93 | // implied type more complete, but if there are any | |
94 | // dynamically-typed attributes inside we must use a tuple | |
95 | // instead, at the expense of our type then not being predictable. | |
96 | if blockS.Block.ImpliedType().HasDynamicTypes() { | |
97 | ret[name] = &hcldec.BlockObjectSpec{ | |
98 | TypeName: name, | |
99 | Nested: childSpec, | |
100 | LabelNames: mapLabelNames, | |
101 | } | |
102 | } else { | |
103 | ret[name] = &hcldec.BlockMapSpec{ | |
104 | TypeName: name, | |
105 | Nested: childSpec, | |
106 | LabelNames: mapLabelNames, | |
107 | } | |
108 | } | |
109 | default: | |
110 | // Invalid nesting type is just ignored. It's checked by | |
111 | // InternalValidate. | |
112 | continue | |
113 | } | |
114 | } | |
115 | ||
116 | return ret | |
117 | } | |
118 | ||
119 | func (a *Attribute) decoderSpec(name string) hcldec.Spec { | |
120 | return &hcldec.AttrSpec{ | |
121 | Name: name, | |
122 | Type: a.Type, | |
123 | Required: a.Required, | |
124 | } | |
125 | } |