opentofu/config/configschema/decoder_spec.go
Martin Atkins 0ef985cada config/configschema: Block.ImpliedType delegates to zcldec
zcldec now has its own function for computing the implied type for a spec,
so we can use that instead of our own logic.

The zcldec logic is more general since its spec model is more general than
our schema model here, but it produces the same results for the subset
of specifications that our DecoderSpec method produces.
2017-10-04 16:35:11 -07:00

98 lines
2.4 KiB
Go

package configschema
import (
"github.com/hashicorp/hcl2/hcldec"
"github.com/zclconf/go-cty/cty"
)
var mapLabelNames = []string{"key"}
// DecoderSpec returns a hcldec.Spec that can be used to decode a HCL Body
// using the facilities in the hcldec package.
//
// The returned specification is guaranteed to return a value of the same type
// returned by method ImpliedType, but it may contain null or unknown values if
// any of the block attributes are defined as optional and/or computed
// respectively.
func (b *Block) DecoderSpec() hcldec.Spec {
ret := hcldec.ObjectSpec{}
if b == nil {
return ret
}
for name, attrS := range b.Attributes {
switch {
case attrS.Computed && attrS.Optional:
// In this special case we use an unknown value as a default
// to get the intended behavior that the result is computed
// unless it has been explicitly set in config.
ret[name] = &hcldec.DefaultSpec{
Primary: &hcldec.AttrSpec{
Name: name,
Type: attrS.Type,
},
Default: &hcldec.LiteralSpec{
Value: cty.UnknownVal(attrS.Type),
},
}
case attrS.Computed:
ret[name] = &hcldec.LiteralSpec{
Value: cty.UnknownVal(attrS.Type),
}
default:
ret[name] = &hcldec.AttrSpec{
Name: name,
Type: attrS.Type,
Required: attrS.Required,
}
}
}
for name, blockS := range b.BlockTypes {
if _, exists := ret[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
}
childSpec := blockS.Block.DecoderSpec()
switch blockS.Nesting {
case NestingSingle:
ret[name] = &hcldec.BlockSpec{
TypeName: name,
Nested: childSpec,
Required: blockS.MinItems == 1 && blockS.MaxItems >= 1,
}
case NestingList:
ret[name] = &hcldec.BlockListSpec{
TypeName: name,
Nested: childSpec,
MinItems: blockS.MinItems,
MaxItems: blockS.MaxItems,
}
case NestingSet:
ret[name] = &hcldec.BlockSetSpec{
TypeName: name,
Nested: childSpec,
MinItems: blockS.MinItems,
MaxItems: blockS.MaxItems,
}
case NestingMap:
ret[name] = &hcldec.BlockMapSpec{
TypeName: name,
Nested: childSpec,
LabelNames: mapLabelNames,
}
default:
// Invalid nesting type is just ignored. It's checked by
// InternalValidate.
continue
}
}
return ret
}