// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package dynblock import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hcldec" "github.com/zclconf/go-cty/cty" ) // unknownBody is a funny body that just reports everything inside it as // unknown. It uses a given other body as a sort of template for what attributes // and blocks are inside -- including source location information -- but // subsitutes unknown values of unknown type for all attributes. // // This rather odd process is used to handle expansion of dynamic blocks whose // for_each expression is unknown. Since a block cannot itself be unknown, // we instead arrange for everything _inside_ the block to be unknown instead, // to give the best possible approximation. type unknownBody struct { template hcl.Body valueMarks cty.ValueMarks } var _ hcl.Body = unknownBody{} // hcldec.UnknownBody impl func (b unknownBody) Unknown() bool { return true } // hcldec.MarkedBody impl func (b unknownBody) BodyValueMarks() cty.ValueMarks { // We'll pass through to our template if it is a MarkedBody if t, ok := b.template.(hcldec.MarkedBody); ok { return t.BodyValueMarks() } return nil } func (b unknownBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { content, diags := b.template.Content(schema) content = b.fixupContent(content) // We're intentionally preserving the diagnostics reported from the // inner body so that we can still report where the template body doesn't // match the requested schema. return content, diags } func (b unknownBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { content, remain, diags := b.template.PartialContent(schema) content = b.fixupContent(content) remain = unknownBody{template: remain, valueMarks: b.valueMarks} // remaining content must also be wrapped // We're intentionally preserving the diagnostics reported from the // inner body so that we can still report where the template body doesn't // match the requested schema. return content, remain, diags } func (b unknownBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { attrs, diags := b.template.JustAttributes() attrs = b.fixupAttrs(attrs) // We're intentionally preserving the diagnostics reported from the // inner body so that we can still report where the template body doesn't // match the requested schema. return attrs, diags } func (b unknownBody) MissingItemRange() hcl.Range { return b.template.MissingItemRange() } func (b unknownBody) fixupContent(got *hcl.BodyContent) *hcl.BodyContent { ret := &hcl.BodyContent{} ret.Attributes = b.fixupAttrs(got.Attributes) if len(got.Blocks) > 0 { ret.Blocks = make(hcl.Blocks, 0, len(got.Blocks)) for _, gotBlock := range got.Blocks { new := *gotBlock // shallow copy new.Body = unknownBody{template: gotBlock.Body, valueMarks: b.valueMarks} // nested content must also be marked unknown ret.Blocks = append(ret.Blocks, &new) } } return ret } func (b unknownBody) fixupAttrs(got hcl.Attributes) hcl.Attributes { if len(got) == 0 { return nil } ret := make(hcl.Attributes, len(got)) for name, gotAttr := range got { new := *gotAttr // shallow copy new.Expr = hcl.StaticExpr(cty.DynamicVal.WithMarks(b.valueMarks), gotAttr.Expr.Range()) ret[name] = &new } return ret }