diff options
| author | Martin Atkins <mart@degeneration.co.uk> | 2017-10-03 11:57:53 -0700 |
|---|---|---|
| committer | Martin Atkins <mart@degeneration.co.uk> | 2017-10-03 11:57:53 -0700 |
| commit | a4ee7188ad9ea5f18d11fe8b5469da7ac7c29de0 (patch) | |
| tree | 927757ce22059f0df4e97380ff4def2483cdaa77 /hcldec | |
| parent | f44382c4fab57666c486c77a8b89c9ea00be08ba (diff) | |
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.
Diffstat (limited to 'hcldec')
| -rw-r--r-- | hcldec/public_test.go | 32 | ||||
| -rw-r--r-- | hcldec/spec.go | 30 | ||||
| -rw-r--r-- | hcldec/spec_test.go | 1 |
3 files changed, 62 insertions, 1 deletions
diff --git a/hcldec/public_test.go b/hcldec/public_test.go index afd483f..79ef710 100644 --- a/hcldec/public_test.go +++ b/hcldec/public_test.go @@ -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" ) @@ -57,6 +57,36 @@ func TestDecode(t *testing.T) { 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{ Name: "a", diff --git a/hcldec/spec.go b/hcldec/spec.go index b385642..3dc9fdb 100644 --- a/hcldec/spec.go +++ b/hcldec/spec.go @@ -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) +} diff --git a/hcldec/spec_test.go b/hcldec/spec_test.go index 4982ffe..164bdaa 100644 --- a/hcldec/spec_test.go +++ b/hcldec/spec_test.go @@ -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) |
