4 "github.com/hashicorp/hcl2/hcl"
7 // MergeBodies creates a new HCL body that contains a combination of the
8 // given base and override bodies. Attributes and blocks defined in the
9 // override body take precedence over those of the same name defined in
12 // If any block of a particular type appears in "override" then it will
13 // replace _all_ of the blocks of the same type in "base" in the new
15 func MergeBodies(base, override hcl.Body) hcl.Body {
22 // mergeBody is a hcl.Body implementation that wraps a pair of other bodies
23 // and allows attributes and blocks within the override to take precedence
24 // over those defined in the base body.
26 // This is used to deal with dynamically-processed bodies in Module.mergeFile.
27 // It uses a shallow-only merging strategy where direct attributes defined
28 // in Override will override attributes of the same name in Base, while any
29 // blocks defined in Override will hide all blocks of the same type in Base.
31 // This cannot possibly "do the right thing" in all cases, because we don't
32 // have enough information about user intent. However, this behavior is intended
33 // to be reasonable for simple overriding use-cases.
34 type mergeBody struct {
39 var _ hcl.Body = mergeBody{}
41 func (b mergeBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
42 var diags hcl.Diagnostics
43 baseSchema := schemaWithDynamic(schema)
44 overrideSchema := schemaWithDynamic(schemaForOverrides(schema))
46 baseContent, _, cDiags := b.Base.PartialContent(baseSchema)
47 diags = append(diags, cDiags...)
48 overrideContent, _, cDiags := b.Override.PartialContent(overrideSchema)
49 diags = append(diags, cDiags...)
51 content := b.prepareContent(baseContent, overrideContent)
56 func (b mergeBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
57 var diags hcl.Diagnostics
58 baseSchema := schemaWithDynamic(schema)
59 overrideSchema := schemaWithDynamic(schemaForOverrides(schema))
61 baseContent, baseRemain, cDiags := b.Base.PartialContent(baseSchema)
62 diags = append(diags, cDiags...)
63 overrideContent, overrideRemain, cDiags := b.Override.PartialContent(overrideSchema)
64 diags = append(diags, cDiags...)
66 content := b.prepareContent(baseContent, overrideContent)
68 remain := MergeBodies(baseRemain, overrideRemain)
70 return content, remain, diags
73 func (b mergeBody) prepareContent(base *hcl.BodyContent, override *hcl.BodyContent) *hcl.BodyContent {
74 content := &hcl.BodyContent{
75 Attributes: make(hcl.Attributes),
78 // For attributes we just assign from each map in turn and let the override
79 // map clobber any matching entries from base.
80 for k, a := range base.Attributes {
81 content.Attributes[k] = a
83 for k, a := range override.Attributes {
84 content.Attributes[k] = a
87 // Things are a little more interesting for blocks because they arrive
88 // as a flat list. Our merging semantics call for us to suppress blocks
89 // from base if at least one block of the same type appears in override.
90 // We explicitly do not try to correlate and deeply merge nested blocks,
91 // since we don't have enough context here to infer user intent.
93 overriddenBlockTypes := make(map[string]bool)
94 for _, block := range override.Blocks {
95 if block.Type == "dynamic" {
96 overriddenBlockTypes[block.Labels[0]] = true
99 overriddenBlockTypes[block.Type] = true
101 for _, block := range base.Blocks {
102 // We skip over dynamic blocks whose type label is an overridden type
103 // but note that below we do still leave them as dynamic blocks in
104 // the result because expanding the dynamic blocks that are left is
105 // done much later during the core graph walks, where we can safely
106 // evaluate the expressions.
107 if block.Type == "dynamic" && overriddenBlockTypes[block.Labels[0]] {
110 if overriddenBlockTypes[block.Type] {
113 content.Blocks = append(content.Blocks, block)
115 for _, block := range override.Blocks {
116 content.Blocks = append(content.Blocks, block)
122 func (b mergeBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
123 var diags hcl.Diagnostics
124 ret := make(hcl.Attributes)
126 baseAttrs, aDiags := b.Base.JustAttributes()
127 diags = append(diags, aDiags...)
128 overrideAttrs, aDiags := b.Override.JustAttributes()
129 diags = append(diags, aDiags...)
131 for k, a := range baseAttrs {
134 for k, a := range overrideAttrs {
141 func (b mergeBody) MissingItemRange() hcl.Range {
142 return b.Base.MissingItemRange()