diff options
author | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
---|---|---|
committer | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
commit | 107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch) | |
tree | ca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/hashicorp/hcl2/ext/dynblock/expand_body.go | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
download | terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip |
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/ext/dynblock/expand_body.go')
-rw-r--r-- | vendor/github.com/hashicorp/hcl2/ext/dynblock/expand_body.go | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/ext/dynblock/expand_body.go b/vendor/github.com/hashicorp/hcl2/ext/dynblock/expand_body.go new file mode 100644 index 0000000..dd30822 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl2/ext/dynblock/expand_body.go | |||
@@ -0,0 +1,262 @@ | |||
1 | package dynblock | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/hcl2/hcl" | ||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | ) | ||
9 | |||
10 | // expandBody wraps another hcl.Body and expands any "dynamic" blocks found | ||
11 | // inside whenever Content or PartialContent is called. | ||
12 | type expandBody struct { | ||
13 | original hcl.Body | ||
14 | forEachCtx *hcl.EvalContext | ||
15 | iteration *iteration // non-nil if we're nested inside another "dynamic" block | ||
16 | |||
17 | // These are used with PartialContent to produce a "remaining items" | ||
18 | // body to return. They are nil on all bodies fresh out of the transformer. | ||
19 | // | ||
20 | // Note that this is re-implemented here rather than delegating to the | ||
21 | // existing support required by the underlying body because we need to | ||
22 | // retain access to the entire original body on subsequent decode operations | ||
23 | // so we can retain any "dynamic" blocks for types we didn't take consume | ||
24 | // on the first pass. | ||
25 | hiddenAttrs map[string]struct{} | ||
26 | hiddenBlocks map[string]hcl.BlockHeaderSchema | ||
27 | } | ||
28 | |||
29 | func (b *expandBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { | ||
30 | extSchema := b.extendSchema(schema) | ||
31 | rawContent, diags := b.original.Content(extSchema) | ||
32 | |||
33 | blocks, blockDiags := b.expandBlocks(schema, rawContent.Blocks, false) | ||
34 | diags = append(diags, blockDiags...) | ||
35 | attrs := b.prepareAttributes(rawContent.Attributes) | ||
36 | |||
37 | content := &hcl.BodyContent{ | ||
38 | Attributes: attrs, | ||
39 | Blocks: blocks, | ||
40 | MissingItemRange: b.original.MissingItemRange(), | ||
41 | } | ||
42 | |||
43 | return content, diags | ||
44 | } | ||
45 | |||
46 | func (b *expandBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { | ||
47 | extSchema := b.extendSchema(schema) | ||
48 | rawContent, _, diags := b.original.PartialContent(extSchema) | ||
49 | // We discard the "remain" argument above because we're going to construct | ||
50 | // our own remain that also takes into account remaining "dynamic" blocks. | ||
51 | |||
52 | blocks, blockDiags := b.expandBlocks(schema, rawContent.Blocks, true) | ||
53 | diags = append(diags, blockDiags...) | ||
54 | attrs := b.prepareAttributes(rawContent.Attributes) | ||
55 | |||
56 | content := &hcl.BodyContent{ | ||
57 | Attributes: attrs, | ||
58 | Blocks: blocks, | ||
59 | MissingItemRange: b.original.MissingItemRange(), | ||
60 | } | ||
61 | |||
62 | remain := &expandBody{ | ||
63 | original: b.original, | ||
64 | forEachCtx: b.forEachCtx, | ||
65 | iteration: b.iteration, | ||
66 | hiddenAttrs: make(map[string]struct{}), | ||
67 | hiddenBlocks: make(map[string]hcl.BlockHeaderSchema), | ||
68 | } | ||
69 | for name := range b.hiddenAttrs { | ||
70 | remain.hiddenAttrs[name] = struct{}{} | ||
71 | } | ||
72 | for typeName, blockS := range b.hiddenBlocks { | ||
73 | remain.hiddenBlocks[typeName] = blockS | ||
74 | } | ||
75 | for _, attrS := range schema.Attributes { | ||
76 | remain.hiddenAttrs[attrS.Name] = struct{}{} | ||
77 | } | ||
78 | for _, blockS := range schema.Blocks { | ||
79 | remain.hiddenBlocks[blockS.Type] = blockS | ||
80 | } | ||
81 | |||
82 | return content, remain, diags | ||
83 | } | ||
84 | |||
85 | func (b *expandBody) extendSchema(schema *hcl.BodySchema) *hcl.BodySchema { | ||
86 | // We augment the requested schema to also include our special "dynamic" | ||
87 | // block type, since then we'll get instances of it interleaved with | ||
88 | // all of the literal child blocks we must also include. | ||
89 | extSchema := &hcl.BodySchema{ | ||
90 | Attributes: schema.Attributes, | ||
91 | Blocks: make([]hcl.BlockHeaderSchema, len(schema.Blocks), len(schema.Blocks)+len(b.hiddenBlocks)+1), | ||
92 | } | ||
93 | copy(extSchema.Blocks, schema.Blocks) | ||
94 | extSchema.Blocks = append(extSchema.Blocks, dynamicBlockHeaderSchema) | ||
95 | |||
96 | // If we have any hiddenBlocks then we also need to register those here | ||
97 | // so that a call to "Content" on the underlying body won't fail. | ||
98 | // (We'll filter these out again once we process the result of either | ||
99 | // Content or PartialContent.) | ||
100 | for _, blockS := range b.hiddenBlocks { | ||
101 | extSchema.Blocks = append(extSchema.Blocks, blockS) | ||
102 | } | ||
103 | |||
104 | // If we have any hiddenAttrs then we also need to register these, for | ||
105 | // the same reason as we deal with hiddenBlocks above. | ||
106 | if len(b.hiddenAttrs) != 0 { | ||
107 | newAttrs := make([]hcl.AttributeSchema, len(schema.Attributes), len(schema.Attributes)+len(b.hiddenAttrs)) | ||
108 | copy(newAttrs, extSchema.Attributes) | ||
109 | for name := range b.hiddenAttrs { | ||
110 | newAttrs = append(newAttrs, hcl.AttributeSchema{ | ||
111 | Name: name, | ||
112 | Required: false, | ||
113 | }) | ||
114 | } | ||
115 | extSchema.Attributes = newAttrs | ||
116 | } | ||
117 | |||
118 | return extSchema | ||
119 | } | ||
120 | |||
121 | func (b *expandBody) prepareAttributes(rawAttrs hcl.Attributes) hcl.Attributes { | ||
122 | if len(b.hiddenAttrs) == 0 && b.iteration == nil { | ||
123 | // Easy path: just pass through the attrs from the original body verbatim | ||
124 | return rawAttrs | ||
125 | } | ||
126 | |||
127 | // Otherwise we have some work to do: we must filter out any attributes | ||
128 | // that are hidden (since a previous PartialContent call already saw these) | ||
129 | // and wrap the expressions of the inner attributes so that they will | ||
130 | // have access to our iteration variables. | ||
131 | attrs := make(hcl.Attributes, len(rawAttrs)) | ||
132 | for name, rawAttr := range rawAttrs { | ||
133 | if _, hidden := b.hiddenAttrs[name]; hidden { | ||
134 | continue | ||
135 | } | ||
136 | if b.iteration != nil { | ||
137 | attr := *rawAttr // shallow copy so we can mutate it | ||
138 | attr.Expr = exprWrap{ | ||
139 | Expression: attr.Expr, | ||
140 | i: b.iteration, | ||
141 | } | ||
142 | attrs[name] = &attr | ||
143 | } else { | ||
144 | // If we have no active iteration then no wrapping is required. | ||
145 | attrs[name] = rawAttr | ||
146 | } | ||
147 | } | ||
148 | return attrs | ||
149 | } | ||
150 | |||
151 | func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks, partial bool) (hcl.Blocks, hcl.Diagnostics) { | ||
152 | var blocks hcl.Blocks | ||
153 | var diags hcl.Diagnostics | ||
154 | |||
155 | for _, rawBlock := range rawBlocks { | ||
156 | switch rawBlock.Type { | ||
157 | case "dynamic": | ||
158 | realBlockType := rawBlock.Labels[0] | ||
159 | if _, hidden := b.hiddenBlocks[realBlockType]; hidden { | ||
160 | continue | ||
161 | } | ||
162 | |||
163 | var blockS *hcl.BlockHeaderSchema | ||
164 | for _, candidate := range schema.Blocks { | ||
165 | if candidate.Type == realBlockType { | ||
166 | blockS = &candidate | ||
167 | break | ||
168 | } | ||
169 | } | ||
170 | if blockS == nil { | ||
171 | // Not a block type that the caller requested. | ||
172 | if !partial { | ||
173 | diags = append(diags, &hcl.Diagnostic{ | ||
174 | Severity: hcl.DiagError, | ||
175 | Summary: "Unsupported block type", | ||
176 | Detail: fmt.Sprintf("Blocks of type %q are not expected here.", realBlockType), | ||
177 | Subject: &rawBlock.LabelRanges[0], | ||
178 | }) | ||
179 | } | ||
180 | continue | ||
181 | } | ||
182 | |||
183 | spec, specDiags := b.decodeSpec(blockS, rawBlock) | ||
184 | diags = append(diags, specDiags...) | ||
185 | if specDiags.HasErrors() { | ||
186 | continue | ||
187 | } | ||
188 | |||
189 | if spec.forEachVal.IsKnown() { | ||
190 | for it := spec.forEachVal.ElementIterator(); it.Next(); { | ||
191 | key, value := it.Element() | ||
192 | i := b.iteration.MakeChild(spec.iteratorName, key, value) | ||
193 | |||
194 | block, blockDiags := spec.newBlock(i, b.forEachCtx) | ||
195 | diags = append(diags, blockDiags...) | ||
196 | if block != nil { | ||
197 | // Attach our new iteration context so that attributes | ||
198 | // and other nested blocks can refer to our iterator. | ||
199 | block.Body = b.expandChild(block.Body, i) | ||
200 | blocks = append(blocks, block) | ||
201 | } | ||
202 | } | ||
203 | } else { | ||
204 | // If our top-level iteration value isn't known then we're forced | ||
205 | // to compromise since HCL doesn't have any concept of an | ||
206 | // "unknown block". In this case then, we'll produce a single | ||
207 | // dynamic block with the iterator values set to DynamicVal, | ||
208 | // which at least makes the potential for a block visible | ||
209 | // in our result, even though it's not represented in a fully-accurate | ||
210 | // way. | ||
211 | i := b.iteration.MakeChild(spec.iteratorName, cty.DynamicVal, cty.DynamicVal) | ||
212 | block, blockDiags := spec.newBlock(i, b.forEachCtx) | ||
213 | diags = append(diags, blockDiags...) | ||
214 | if block != nil { | ||
215 | block.Body = b.expandChild(block.Body, i) | ||
216 | |||
217 | // We additionally force all of the leaf attribute values | ||
218 | // in the result to be unknown so the calling application | ||
219 | // can, if necessary, use that as a heuristic to detect | ||
220 | // when a single nested block might be standing in for | ||
221 | // multiple blocks yet to be expanded. This retains the | ||
222 | // structure of the generated body but forces all of its | ||
223 | // leaf attribute values to be unknown. | ||
224 | block.Body = unknownBody{block.Body} | ||
225 | |||
226 | blocks = append(blocks, block) | ||
227 | } | ||
228 | } | ||
229 | |||
230 | default: | ||
231 | if _, hidden := b.hiddenBlocks[rawBlock.Type]; !hidden { | ||
232 | // A static block doesn't create a new iteration context, but | ||
233 | // it does need to inherit _our own_ iteration context in | ||
234 | // case it contains expressions that refer to our inherited | ||
235 | // iterators, or nested "dynamic" blocks. | ||
236 | expandedBlock := *rawBlock // shallow copy | ||
237 | expandedBlock.Body = b.expandChild(rawBlock.Body, b.iteration) | ||
238 | blocks = append(blocks, &expandedBlock) | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | return blocks, diags | ||
244 | } | ||
245 | |||
246 | func (b *expandBody) expandChild(child hcl.Body, i *iteration) hcl.Body { | ||
247 | chiCtx := i.EvalContext(b.forEachCtx) | ||
248 | ret := Expand(child, chiCtx) | ||
249 | ret.(*expandBody).iteration = i | ||
250 | return ret | ||
251 | } | ||
252 | |||
253 | func (b *expandBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { | ||
254 | // blocks aren't allowed in JustAttributes mode and this body can | ||
255 | // only produce blocks, so we'll just pass straight through to our | ||
256 | // underlying body here. | ||
257 | return b.original.JustAttributes() | ||
258 | } | ||
259 | |||
260 | func (b *expandBody) MissingItemRange() hcl.Range { | ||
261 | return b.original.MissingItemRange() | ||
262 | } | ||