Skip to content

Commit

Permalink
hcldec: New DefaultSpec specification
Browse files Browse the repository at this point in the history
This is a wrapper that allows a default value to be applied if a primary
spec results in a null value.
  • Loading branch information
apparentlymart committed Oct 3, 2017
1 parent f44382c commit a4ee718
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 1 deletion.
32 changes: 31 additions & 1 deletion hcldec/public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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{
Expand Down
30 changes: 30 additions & 0 deletions hcldec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
1 change: 1 addition & 0 deletions hcldec/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit a4ee718

Please sign in to comment.