hcl/ext/include/transformer.go

93 lines
2.8 KiB
Go

package include
import (
"github.com/hashicorp/hcl/v2/ext/transform"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2"
)
// Transformer builds a transformer that finds any "include" blocks in a body
// and produces a merged body that contains the original content plus the
// content of the other bodies referenced by the include blocks.
//
// blockType specifies the type of block to interpret. The conventional type name
// is "include".
//
// ctx provides an evaluation context for the path expressions in include blocks.
// If nil, path expressions may not reference variables nor functions.
//
// The given resolver is used to translate path strings (after expression
// evaluation) into bodies. FileResolver returns a reasonable implementation for
// applications that read configuration files from local disk.
//
// The returned Transformer can either be used directly to process includes
// in a shallow fashion on a single body, or it can be used with
// transform.Deep (from the sibling transform package) to allow includes
// at all levels of a nested block structure:
//
// transformer = include.Transformer("include", nil, include.FileResolver(".", parser))
// body = transform.Deep(body, transformer)
// // "body" will now have includes resolved in its own content and that
// // of any descendent blocks.
//
func Transformer(blockType string, ctx *hcl.EvalContext, resolver Resolver) transform.Transformer {
return &transformer{
Schema: &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: blockType,
},
},
},
Ctx: ctx,
Resolver: resolver,
}
}
type transformer struct {
Schema *hcl.BodySchema
Ctx *hcl.EvalContext
Resolver Resolver
}
func (t *transformer) TransformBody(in hcl.Body) hcl.Body {
content, remain, diags := in.PartialContent(t.Schema)
if content == nil || len(content.Blocks) == 0 {
// Nothing to do!
return transform.BodyWithDiagnostics(remain, diags)
}
bodies := make([]hcl.Body, 1, len(content.Blocks)+1)
bodies[0] = remain // content in "remain" takes priority over includes
for _, block := range content.Blocks {
incContent, incDiags := block.Body.Content(includeBlockSchema)
diags = append(diags, incDiags...)
if incDiags.HasErrors() {
continue
}
pathExpr := incContent.Attributes["path"].Expr
var path string
incDiags = gohcl.DecodeExpression(pathExpr, t.Ctx, &path)
diags = append(diags, incDiags...)
if incDiags.HasErrors() {
continue
}
incBody, incDiags := t.Resolver.ResolveBodyPath(path, pathExpr.Range())
bodies = append(bodies, transform.BodyWithDiagnostics(incBody, incDiags))
}
return hcl.MergeBodies(bodies)
}
var includeBlockSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "path",
Required: true,
},
},
}