hcldec: New function ChildBlockTypes

This function returns a map describing all of the child block types
declared inside a spec. This can be used for recursive decoding of bodies
using the low-level HCL API, though in most cases callers should just use
Decode which does recursive decoding of an entire nested structure in
a single call.
This commit is contained in:
Martin Atkins 2018-01-21 18:08:43 -08:00
parent 45c6cc83f0
commit 130b3c5105
3 changed files with 64 additions and 11 deletions

View File

@ -51,3 +51,28 @@ func ImpliedType(spec Spec) cty.Type {
func SourceRange(body hcl.Body, spec Spec) hcl.Range {
return sourceRange(body, nil, spec)
}
// ChildBlockTypes returns a map of all of the child block types declared
// by the given spec, with block type names as keys and the associated
// nested body specs as values.
func ChildBlockTypes(spec Spec) map[string]Spec {
ret := map[string]Spec{}
// visitSameBodyChildren walks through the spec structure, calling
// the given callback for each descendent spec encountered. We are
// interested in the specs that reference attributes and blocks.
var visit visitFunc
visit = func(s Spec) {
if bs, ok := s.(blockSpec); ok {
for _, blockS := range bs.blockHeaderSchemata() {
ret[blockS.Type] = bs.nestedSpec()
}
}
s.visitSameBodyChildren(visit)
}
visit(spec)
return ret
}

View File

@ -52,6 +52,7 @@ type attrSpec interface {
// blockSpec is implemented by specs that require blocks from the body.
type blockSpec interface {
blockHeaderSchemata() []hcl.BlockHeaderSchema
nestedSpec() Spec
}
// specNeedingVariables is implemented by specs that can use variables
@ -298,6 +299,11 @@ func (s *BlockSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
}
}
// blockSpec implementation
func (s *BlockSpec) nestedSpec() Spec {
return s.Nested
}
// specNeedingVariables implementation
func (s *BlockSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
var childBlock *hcl.Block
@ -409,6 +415,11 @@ func (s *BlockListSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
}
}
// blockSpec implementation
func (s *BlockListSpec) nestedSpec() Spec {
return s.Nested
}
// specNeedingVariables implementation
func (s *BlockListSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
var ret []hcl.Traversal
@ -519,6 +530,11 @@ func (s *BlockSetSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
}
}
// blockSpec implementation
func (s *BlockSetSpec) nestedSpec() Spec {
return s.Nested
}
// specNeedingVariables implementation
func (s *BlockSetSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
var ret []hcl.Traversal
@ -631,6 +647,11 @@ func (s *BlockMapSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema {
}
}
// blockSpec implementation
func (s *BlockMapSpec) nestedSpec() Spec {
return s.Nested
}
// specNeedingVariables implementation
func (s *BlockMapSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal {
var ret []hcl.Traversal

View File

@ -1,14 +1,21 @@
package hcldec
// Verify that all of our spec types implement the necessary interfaces
var objectSpecAsSpec Spec = ObjectSpec(nil)
var tupleSpecAsSpec Spec = TupleSpec(nil)
var attrSpecAsSpec Spec = (*AttrSpec)(nil)
var literalSpecAsSpec Spec = (*LiteralSpec)(nil)
var exprSpecAsSpec Spec = (*ExprSpec)(nil)
var blockSpecAsSpec Spec = (*BlockSpec)(nil)
var blockListSpecAsSpec Spec = (*BlockListSpec)(nil)
var blockSetSpecAsSpec Spec = (*BlockSetSpec)(nil)
var blockMapSpecAsSpec Spec = (*BlockMapSpec)(nil)
var blockLabelSpecAsSpec Spec = (*BlockLabelSpec)(nil)
var defaultSpecAsSpec Spec = (*DefaultSpec)(nil)
var _ Spec = ObjectSpec(nil)
var _ Spec = TupleSpec(nil)
var _ Spec = (*AttrSpec)(nil)
var _ Spec = (*LiteralSpec)(nil)
var _ Spec = (*ExprSpec)(nil)
var _ Spec = (*BlockSpec)(nil)
var _ Spec = (*BlockListSpec)(nil)
var _ Spec = (*BlockSetSpec)(nil)
var _ Spec = (*BlockMapSpec)(nil)
var _ Spec = (*BlockLabelSpec)(nil)
var _ Spec = (*DefaultSpec)(nil)
var _ attrSpec = (*AttrSpec)(nil)
var _ blockSpec = (*BlockSpec)(nil)
var _ blockSpec = (*BlockListSpec)(nil)
var _ blockSpec = (*BlockSetSpec)(nil)
var _ blockSpec = (*BlockMapSpec)(nil)