- decoded structs may now use the ast.Node type to save the raw ast.Node; conceptually similar to how json.RawMessage works and is useful in situations where you would like to either (a) defer decoding or (b) require additional meta-data like #Pos

This commit is contained in:
Matt 2015-12-06 21:31:48 -08:00
parent c40ec20b12
commit 1793d02c0e
3 changed files with 104 additions and 0 deletions

6
.gitignore vendored
View File

@ -1 +1,7 @@
y.output
# ignore intellij files
.idea
*.iml
*.ipr
*.iws

View File

@ -15,6 +15,11 @@ import (
// This is the tag to use with structures to have settings for HCL
const tagName = "hcl"
var (
// nodeType holds a reference to the type of ast.Node
nodeType reflect.Type = findNodeType()
)
// Decode reads the given input and decodes it into the structure
// given by `out`.
func Decode(out interface{}, in string) error {
@ -158,6 +163,14 @@ func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) er
}
func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Value) error {
// When we see an ast.Node, we retain the value to enable deferred decoding.
// Very useful in situations where we want to preserve ast.Node information
// like Pos
if result.Type() == nodeType && result.CanSet() {
result.Set(reflect.ValueOf(node))
return nil
}
var set reflect.Value
redecode := true
@ -574,3 +587,12 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
return nil
}
// findNodeType returns the type of ast.Node
func findNodeType() reflect.Type {
var nodeContainer struct {
Node ast.Node
}
value := reflect.ValueOf(nodeContainer).FieldByName("Node")
return value.Type()
}

View File

@ -5,6 +5,8 @@ import (
"path/filepath"
"reflect"
"testing"
"github.com/hashicorp/hcl/hcl/ast"
)
func TestDecode_interface(t *testing.T) {
@ -586,3 +588,77 @@ func TestDecode_intString(t *testing.T) {
t.Fatalf("bad: %#v", value.Count)
}
}
func TestDecode_Node(t *testing.T) {
// given
var value struct {
Content ast.Node
Nested struct {
Content ast.Node
}
}
content := `
content {
hello = "world"
}
`
// when
err := Decode(&value, content)
// then
if err != nil {
t.Errorf("unable to decode content, %v", err)
return
}
// verify ast.Node can be decoded later
var v map[string]interface{}
err = DecodeObject(&v, value.Content)
if err != nil {
t.Errorf("unable to decode content, %v", err)
return
}
if v["hello"] != "world" {
t.Errorf("expected mapping to be returned")
}
}
func TestDecode_NestedNode(t *testing.T) {
// given
var value struct {
Nested struct {
Content ast.Node
}
}
content := `
nested "content" {
hello = "world"
}
`
// when
err := Decode(&value, content)
// then
if err != nil {
t.Errorf("unable to decode content, %v", err)
return
}
// verify ast.Node can be decoded later
var v map[string]interface{}
err = DecodeObject(&v, value.Nested.Content)
if err != nil {
t.Errorf("unable to decode content, %v", err)
return
}
if v["hello"] != "world" {
t.Errorf("expected mapping to be returned")
}
}