diff --git a/internal/configs/configschema/path.go b/internal/configs/configschema/path.go index 2760eecbd8..3f359a6c42 100644 --- a/internal/configs/configschema/path.go +++ b/internal/configs/configschema/path.go @@ -56,3 +56,23 @@ func (o *Object) AttributeByPath(path cty.Path) *Attribute { } return nil } + +// BlockByPath looks up the Block schema which corresponds to the given +// cty.Path. A nil value is returned if the given path does not correspond to a +// specific attribute. +func (b *Block) BlockByPath(path cty.Path) *Block { + for i, step := range path { + switch step := step.(type) { + case cty.GetAttrStep: + if blockType := b.BlockTypes[step.Name]; blockType != nil { + if len(blockType.Block.BlockTypes) > 0 && i < len(path)-1 { + return blockType.Block.BlockByPath(path[i+1:]) + } else if i < len(path)-1 { + return nil + } + return &blockType.Block + } + } + } + return nil +} diff --git a/internal/configs/configschema/path_test.go b/internal/configs/configschema/path_test.go index bfd887f1bd..71c8ac561e 100644 --- a/internal/configs/configschema/path_test.go +++ b/internal/configs/configschema/path_test.go @@ -4,6 +4,7 @@ package configschema import ( + "fmt" "testing" "github.com/zclconf/go-cty/cty" @@ -256,5 +257,88 @@ func TestObject_AttributeByPath(t *testing.T) { } }) } +} + +func TestBlockByPath(t *testing.T) { + schema := &Block{ + BlockTypes: map[string]*NestedBlock{ + "b1": { + Nesting: NestingList, + Block: Block{ + Attributes: map[string]*Attribute{ + "a3": {Description: "a3"}, + "a4": {Description: "a4"}, + }, + BlockTypes: map[string]*NestedBlock{ + "b2": { + Nesting: NestingMap, + Block: Block{ + Attributes: map[string]*Attribute{ + "a5": {Description: "a5"}, + "a6": {Description: "a6"}, + }, + }, + }, + }, + }, + }, + "b3": { + Nesting: NestingMap, + Block: Block{ + Attributes: map[string]*Attribute{ + "a7": {Description: "a7"}, + "a8": {Description: "a8"}, + }, + BlockTypes: map[string]*NestedBlock{ + "b4": { + Nesting: NestingSet, + Block: Block{ + Attributes: map[string]*Attribute{ + "a9": {Description: "a9"}, + "a10": {Description: "a10"}, + }, + }, + }, + }, + }, + }, + }, + } + for i, tc := range []struct { + path cty.Path + exists bool + }{ + { + cty.GetAttrPath("b1").IndexInt(1).GetAttr("b2"), + true, + }, + { + cty.GetAttrPath("b1"), + true, + }, + { + cty.GetAttrPath("b2"), + false, + }, + { + cty.GetAttrPath("b3").IndexString("foo").GetAttr("b2"), + false, + }, + { + cty.GetAttrPath("b3").IndexString("foo").GetAttr("b4"), + true, + }, + } { + t.Run(fmt.Sprint(i), func(t *testing.T) { + block := schema.BlockByPath(tc.path) + if !tc.exists && block == nil { + return + } + + if block == nil { + t.Fatalf("missing block from path %#v\n", tc.path) + } + }) + } }