package configs import ( "github.com/hashicorp/hcl2/hcl" ) func mergeBodies(base, override hcl.Body) hcl.Body { return mergeBody{ Base: base, Override: override, } } // mergeBody is a hcl.Body implementation that wraps a pair of other bodies // and allows attributes and blocks within the override to take precedence // over those defined in the base body. // // This is used to deal with dynamically-processed bodies in Module.mergeFile. // It uses a shallow-only merging strategy where direct attributes defined // in Override will override attributes of the same name in Base, while any // blocks defined in Override will hide all blocks of the same type in Base. // // This cannot possibly "do the right thing" in all cases, because we don't // have enough information about user intent. However, this behavior is intended // to be reasonable for simple overriding use-cases. type mergeBody struct { Base hcl.Body Override hcl.Body } var _ hcl.Body = mergeBody{} func (b mergeBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { var diags hcl.Diagnostics oSchema := schemaForOverrides(schema) baseContent, cDiags := b.Base.Content(schema) diags = append(diags, cDiags...) overrideContent, cDiags := b.Override.Content(oSchema) diags = append(diags, cDiags...) content := b.prepareContent(baseContent, overrideContent) return content, diags } func (b mergeBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { var diags hcl.Diagnostics oSchema := schemaForOverrides(schema) baseContent, baseRemain, cDiags := b.Base.PartialContent(schema) diags = append(diags, cDiags...) overrideContent, overrideRemain, cDiags := b.Override.PartialContent(oSchema) diags = append(diags, cDiags...) content := b.prepareContent(baseContent, overrideContent) remain := mergeBodies(baseRemain, overrideRemain) return content, remain, diags } func (b mergeBody) prepareContent(base *hcl.BodyContent, override *hcl.BodyContent) *hcl.BodyContent { content := &hcl.BodyContent{ Attributes: make(hcl.Attributes), } // For attributes we just assign from each map in turn and let the override // map clobber any matching entries from base. for k, a := range base.Attributes { content.Attributes[k] = a } for k, a := range override.Attributes { content.Attributes[k] = a } // Things are a little more interesting for blocks because they arrive // as a flat list. Our merging semantics call for us to suppress blocks // from base if at least one block of the same type appears in override. // We explicitly do not try to correlate and deeply merge nested blocks, // since we don't have enough context here to infer user intent. overriddenBlockTypes := make(map[string]bool) for _, block := range override.Blocks { overriddenBlockTypes[block.Type] = true } for _, block := range base.Blocks { if overriddenBlockTypes[block.Type] { continue } content.Blocks = append(content.Blocks, block) } for _, block := range override.Blocks { content.Blocks = append(content.Blocks, block) } return content } func (b mergeBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { var diags hcl.Diagnostics ret := make(hcl.Attributes) baseAttrs, aDiags := b.Base.JustAttributes() diags = append(diags, aDiags...) overrideAttrs, aDiags := b.Override.JustAttributes() diags = append(diags, aDiags...) for k, a := range baseAttrs { ret[k] = a } for k, a := range overrideAttrs { ret[k] = a } return ret, diags } func (b mergeBody) MissingItemRange() hcl.Range { return b.Base.MissingItemRange() }