hcl/ext/transform/transform.go
Martin Atkins 6c4344623b Unfold the "hcl" directory up into the root
The main HCL package is more visible this way, and so it's easier than
having to pick it out from dozens of other package directories.
2019-09-09 16:08:19 -07:00

84 lines
2.8 KiB
Go

package transform
import (
"github.com/hashicorp/hcl/v2"
)
// Shallow is equivalent to calling transformer.TransformBody(body), and
// is provided only for completeness of the top-level API.
func Shallow(body hcl.Body, transformer Transformer) hcl.Body {
return transformer.TransformBody(body)
}
// Deep applies the given transform to the given body and then
// wraps the result such that any descendent blocks that are decoded will
// also have the transform applied to their bodies.
//
// This allows for language extensions that define a particular block type
// for a particular body and all nested blocks within it.
//
// Due to the wrapping behavior, the body resulting from this function
// will not be of the type returned by the transformer. Callers may call
// only the methods defined for interface hcl.Body, and may not type-assert
// to access other methods.
func Deep(body hcl.Body, transformer Transformer) hcl.Body {
return deepWrapper{
Transformed: transformer.TransformBody(body),
Transformer: transformer,
}
}
// deepWrapper is a hcl.Body implementation that ensures that a given
// transformer is applied to another given body when content is extracted,
// and that it recursively applies to any child blocks that are extracted.
type deepWrapper struct {
Transformed hcl.Body
Transformer Transformer
}
func (w deepWrapper) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
content, diags := w.Transformed.Content(schema)
content = w.transformContent(content)
return content, diags
}
func (w deepWrapper) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
content, remain, diags := w.Transformed.PartialContent(schema)
content = w.transformContent(content)
return content, remain, diags
}
func (w deepWrapper) transformContent(content *hcl.BodyContent) *hcl.BodyContent {
if len(content.Blocks) == 0 {
// Easy path: if there are no blocks then there are no child bodies to wrap
return content
}
// Since we're going to change things here, we'll be polite and clone the
// structure so that we don't risk impacting any internal state of the
// original body.
ret := &hcl.BodyContent{
Attributes: content.Attributes,
MissingItemRange: content.MissingItemRange,
Blocks: make(hcl.Blocks, len(content.Blocks)),
}
for i, givenBlock := range content.Blocks {
// Shallow-copy the block so we can mutate it
newBlock := *givenBlock
newBlock.Body = Deep(newBlock.Body, w.Transformer)
ret.Blocks[i] = &newBlock
}
return ret
}
func (w deepWrapper) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
// Attributes can't have bodies or nested blocks, so this is just a thin wrapper.
return w.Transformed.JustAttributes()
}
func (w deepWrapper) MissingItemRange() hcl.Range {
return w.Transformed.MissingItemRange()
}