hcldec: New DefaultSpec specification

This is a wrapper that allows a default value to be applied if a primary
spec results in a null value.
This commit is contained in:
Martin Atkins 2017-10-03 11:57:53 -07:00
parent f44382c4fa
commit a4ee7188ad
3 changed files with 62 additions and 1 deletions

View File

@ -5,8 +5,8 @@ import (
"reflect"
"testing"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
"github.com/zclconf/go-cty/cty"
)
@ -56,6 +56,36 @@ func TestDecode(t *testing.T) {
cty.NumberIntVal(1),
0,
},
{
"a = 1\n",
&DefaultSpec{
Primary: &AttrSpec{
Name: "a",
Type: cty.Number,
},
Default: &LiteralSpec{
Value: cty.NumberIntVal(10),
},
},
nil,
cty.NumberIntVal(1),
0,
},
{
"",
&DefaultSpec{
Primary: &AttrSpec{
Name: "a",
Type: cty.Number,
},
Default: &LiteralSpec{
Value: cty.NumberIntVal(10),
},
},
nil,
cty.NumberIntVal(10),
0,
},
{
"a = \"1\"\n",
&AttrSpec{

View File

@ -566,3 +566,33 @@ func (s *BlockLabelSpec) sourceRange(content *hcl.BodyContent, block *hcl.Block)
return block.LabelRanges[s.Index]
}
// DefaultSpec is a spec that wraps two specs, evaluating the primary first
// and then evaluating the default if the primary returns a null value.
type DefaultSpec struct {
Primary Spec
Default Spec
}
func (s *DefaultSpec) visitSameBodyChildren(cb visitFunc) {
cb(s.Primary)
cb(s.Default)
}
func (s *DefaultSpec) decode(content *hcl.BodyContent, block *hcl.Block, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
val, diags := s.Primary.decode(content, block, ctx)
if val.IsNull() {
var moreDiags hcl.Diagnostics
val, moreDiags = s.Default.decode(content, block, ctx)
diags = append(diags, moreDiags...)
}
return val, diags
}
func (s *DefaultSpec) sourceRange(content *hcl.BodyContent, block *hcl.Block) hcl.Range {
// We can't tell from here which of the two specs will ultimately be used
// in our result, so we'll just assume the first. This is usually the right
// choice because the default is often a literal spec that doesn't have a
// reasonable source range to return anyway.
return s.Primary.sourceRange(content, block)
}

View File

@ -11,3 +11,4 @@ var blockListSpecAsSpec Spec = (*BlockListSpec)(nil)
var blockSetSpecAsSpec Spec = (*BlockSetSpec)(nil)
var blockMapSpecAsSpec Spec = (*BlockMapSpec)(nil)
var blockLabelSpecAsSpec Spec = (*BlockLabelSpec)(nil)
var defaultSepcAsSpec Spec = (*DefaultSpec)(nil)