From d0d829848ac17684b63dd4d05f86500c0c1fe8ae Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 2 Oct 2017 18:48:40 -0700 Subject: [PATCH] config/configschema: Block.ImpliedType method This returns a cty.Type that the caller can expect to recieve when decoding a value using the (not yet implemented) decoder specification for a given schema. --- config/configschema/implied_type.go | 38 ++++++- config/configschema/implied_type_test.go | 124 +++++++++++++++++++++++ 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 config/configschema/implied_type_test.go diff --git a/config/configschema/implied_type.go b/config/configschema/implied_type.go index 485d121b3..fe69c5b64 100644 --- a/config/configschema/implied_type.go +++ b/config/configschema/implied_type.go @@ -12,5 +12,41 @@ import ( // tested using the InternalValidate method to detect any inconsistencies // that would cause this method to fall back on defaults and assumptions. func (b *Block) ImpliedType() cty.Type { - return cty.DynamicPseudoType + if b == nil { + return cty.EmptyObject + } + + attrTypes := map[string]cty.Type{} + + for name, attrS := range b.Attributes { + attrTypes[name] = attrS.Type + } + + for name, blockS := range b.BlockTypes { + if _, exists := attrTypes[name]; exists { + // This indicates an invalid schema, since it's not valid to + // define both an attribute and a block type of the same name. + // However, we don't raise this here since it's checked by + // InternalValidate. + continue + } + + childType := blockS.Block.ImpliedType() + switch blockS.Nesting { + case NestingSingle: + attrTypes[name] = childType + case NestingList: + attrTypes[name] = cty.List(childType) + case NestingSet: + attrTypes[name] = cty.Set(childType) + case NestingMap: + attrTypes[name] = cty.Map(childType) + default: + // Invalid nesting type is just ignored. It's checked by + // InternalValidate. + continue + } + } + + return cty.Object(attrTypes) } diff --git a/config/configschema/implied_type_test.go b/config/configschema/implied_type_test.go new file mode 100644 index 000000000..85e4a88d5 --- /dev/null +++ b/config/configschema/implied_type_test.go @@ -0,0 +1,124 @@ +package configschema + +import ( + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestBlockImpliedType(t *testing.T) { + tests := map[string]struct { + Schema *Block + Want cty.Type + }{ + "nil": { + nil, + cty.EmptyObject, + }, + "empty": { + &Block{}, + cty.EmptyObject, + }, + "attributes": { + &Block{ + Attributes: map[string]*Attribute{ + "optional": { + Type: cty.String, + Optional: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + }, + }, + }, + cty.Object(map[string]cty.Type{ + "optional": cty.String, + "required": cty.Number, + "computed": cty.List(cty.Bool), + "optional_computed": cty.Map(cty.Bool), + }), + }, + "blocks": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "single": &NestedBlock{ + Nesting: NestingSingle, + Block: Block{ + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.DynamicPseudoType, + Required: true, + }, + }, + }, + }, + "list": &NestedBlock{ + Nesting: NestingList, + }, + "set": &NestedBlock{ + Nesting: NestingSet, + }, + "map": &NestedBlock{ + Nesting: NestingMap, + }, + }, + }, + cty.Object(map[string]cty.Type{ + "single": cty.Object(map[string]cty.Type{ + "foo": cty.DynamicPseudoType, + }), + "list": cty.List(cty.EmptyObject), + "set": cty.Set(cty.EmptyObject), + "map": cty.Map(cty.EmptyObject), + }), + }, + "deep block nesting": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "single": &NestedBlock{ + Nesting: NestingSingle, + Block: Block{ + BlockTypes: map[string]*NestedBlock{ + "list": &NestedBlock{ + Nesting: NestingList, + Block: Block{ + BlockTypes: map[string]*NestedBlock{ + "set": &NestedBlock{ + Nesting: NestingSet, + }, + }, + }, + }, + }, + }, + }, + }, + }, + cty.Object(map[string]cty.Type{ + "single": cty.Object(map[string]cty.Type{ + "list": cty.List(cty.Object(map[string]cty.Type{ + "set": cty.Set(cty.EmptyObject), + })), + }), + }), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + got := test.Schema.ImpliedType() + if !got.Equals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +}