1package dynblock
2
3import (
4	"github.com/hashicorp/hcl/v2"
5	"github.com/zclconf/go-cty/cty"
6)
7
8// unknownBody is a funny body that just reports everything inside it as
9// unknown. It uses a given other body as a sort of template for what attributes
10// and blocks are inside -- including source location information -- but
11// subsitutes unknown values of unknown type for all attributes.
12//
13// This rather odd process is used to handle expansion of dynamic blocks whose
14// for_each expression is unknown. Since a block cannot itself be unknown,
15// we instead arrange for everything _inside_ the block to be unknown instead,
16// to give the best possible approximation.
17type unknownBody struct {
18	template hcl.Body
19}
20
21var _ hcl.Body = unknownBody{}
22
23// hcldec.UnkownBody impl
24func (b unknownBody) Unknown() bool {
25	return true
26}
27
28func (b unknownBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
29	content, diags := b.template.Content(schema)
30	content = b.fixupContent(content)
31
32	// We're intentionally preserving the diagnostics reported from the
33	// inner body so that we can still report where the template body doesn't
34	// match the requested schema.
35	return content, diags
36}
37
38func (b unknownBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
39	content, remain, diags := b.template.PartialContent(schema)
40	content = b.fixupContent(content)
41	remain = unknownBody{remain} // remaining content must also be wrapped
42
43	// We're intentionally preserving the diagnostics reported from the
44	// inner body so that we can still report where the template body doesn't
45	// match the requested schema.
46	return content, remain, diags
47}
48
49func (b unknownBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
50	attrs, diags := b.template.JustAttributes()
51	attrs = b.fixupAttrs(attrs)
52
53	// We're intentionally preserving the diagnostics reported from the
54	// inner body so that we can still report where the template body doesn't
55	// match the requested schema.
56	return attrs, diags
57}
58
59func (b unknownBody) MissingItemRange() hcl.Range {
60	return b.template.MissingItemRange()
61}
62
63func (b unknownBody) fixupContent(got *hcl.BodyContent) *hcl.BodyContent {
64	ret := &hcl.BodyContent{}
65	ret.Attributes = b.fixupAttrs(got.Attributes)
66	if len(got.Blocks) > 0 {
67		ret.Blocks = make(hcl.Blocks, 0, len(got.Blocks))
68		for _, gotBlock := range got.Blocks {
69			new := *gotBlock                      // shallow copy
70			new.Body = unknownBody{gotBlock.Body} // nested content must also be marked unknown
71			ret.Blocks = append(ret.Blocks, &new)
72		}
73	}
74
75	return ret
76}
77
78func (b unknownBody) fixupAttrs(got hcl.Attributes) hcl.Attributes {
79	if len(got) == 0 {
80		return nil
81	}
82	ret := make(hcl.Attributes, len(got))
83	for name, gotAttr := range got {
84		new := *gotAttr // shallow copy
85		new.Expr = hcl.StaticExpr(cty.DynamicVal, gotAttr.Expr.Range())
86		ret[name] = &new
87	}
88	return ret
89}
90