From 130b3c5105e02268861d448a32ead57e99af3fcd Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sun, 21 Jan 2018 18:08:43 -0800 Subject: [PATCH] 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. --- hcldec/public.go | 25 +++++++++++++++++++++++++ hcldec/spec.go | 21 +++++++++++++++++++++ hcldec/spec_test.go | 29 ++++++++++++++++++----------- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/hcldec/public.go b/hcldec/public.go index 3e58f7b..5d1f10a 100644 --- a/hcldec/public.go +++ b/hcldec/public.go @@ -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 +} diff --git a/hcldec/spec.go b/hcldec/spec.go index f0e6842..cb98495 100644 --- a/hcldec/spec.go +++ b/hcldec/spec.go @@ -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 diff --git a/hcldec/spec_test.go b/hcldec/spec_test.go index 7086f9f..6ad147a 100644 --- a/hcldec/spec_test.go +++ b/hcldec/spec_test.go @@ -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)