commit
99e2f22d1c
39
decoder.go
39
decoder.go
@ -404,6 +404,11 @@ func (d *decoder) decodeMap(name string, node ast.Node, result reflect.Value) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) decodePtr(name string, node ast.Node, result reflect.Value) error {
|
func (d *decoder) decodePtr(name string, node ast.Node, result reflect.Value) error {
|
||||||
|
// if pointer is not nil, decode into existing value
|
||||||
|
if !result.IsNil() {
|
||||||
|
return d.decode(name, node, result.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
// Create an element of the concrete (non pointer) type and decode
|
// Create an element of the concrete (non pointer) type and decode
|
||||||
// into that. Then set the value of the pointer to this type.
|
// into that. Then set the value of the pointer to this type.
|
||||||
resultType := result.Type()
|
resultType := result.Type()
|
||||||
@ -512,7 +517,7 @@ func expandObject(node ast.Node, result reflect.Value) ast.Node {
|
|||||||
// we need to un-flatten the ast enough to decode
|
// we need to un-flatten the ast enough to decode
|
||||||
newNode := &ast.ObjectItem{
|
newNode := &ast.ObjectItem{
|
||||||
Keys: []*ast.ObjectKey{
|
Keys: []*ast.ObjectKey{
|
||||||
&ast.ObjectKey{
|
{
|
||||||
Token: keyToken,
|
Token: keyToken,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -631,10 +636,19 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usedKeys := make(map[string]struct{})
|
|
||||||
decodedFields := make([]string, 0, len(fields))
|
decodedFields := make([]string, 0, len(fields))
|
||||||
decodedFieldsVal := make([]reflect.Value, 0)
|
decodedFieldsVal := make([]reflect.Value, 0)
|
||||||
unusedKeysVal := make([]reflect.Value, 0)
|
unusedKeysVal := make([]reflect.Value, 0)
|
||||||
|
|
||||||
|
// fill unusedNodeKeys with keys from the AST
|
||||||
|
// a slice because we have to do equals case fold to match Filter
|
||||||
|
unusedNodeKeys := make([]string, 0)
|
||||||
|
for _, item := range list.Items {
|
||||||
|
for _, k := range item.Keys {
|
||||||
|
unusedNodeKeys = append(unusedNodeKeys, k.Token.Value().(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
field, fieldValue := f.field, f.val
|
field, fieldValue := f.field, f.val
|
||||||
if !fieldValue.IsValid() {
|
if !fieldValue.IsValid() {
|
||||||
@ -689,8 +703,8 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track the used key
|
// Track the used keys
|
||||||
usedKeys[fieldName] = struct{}{}
|
unusedNodeKeys = removeCaseFold(unusedNodeKeys, fieldName)
|
||||||
|
|
||||||
// Create the field name and decode. We range over the elements
|
// Create the field name and decode. We range over the elements
|
||||||
// because we actually want the value.
|
// because we actually want the value.
|
||||||
@ -723,6 +737,14 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(unusedNodeKeys) > 0 {
|
||||||
|
// like decodedFields, populated the unusedKeys field(s)
|
||||||
|
sort.Strings(unusedNodeKeys)
|
||||||
|
for _, v := range unusedKeysVal {
|
||||||
|
v.Set(reflect.ValueOf(unusedNodeKeys))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,3 +756,12 @@ func findNodeType() reflect.Type {
|
|||||||
value := reflect.ValueOf(nodeContainer).FieldByName("Node")
|
value := reflect.ValueOf(nodeContainer).FieldByName("Node")
|
||||||
return value.Type()
|
return value.Type()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeCaseFold(xs []string, y string) []string {
|
||||||
|
for i, x := range xs {
|
||||||
|
if strings.EqualFold(x, y) {
|
||||||
|
return append(xs[:i], xs[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xs
|
||||||
|
}
|
||||||
|
@ -616,6 +616,35 @@ func TestDecode_structurePtr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecode_nonNilStructurePtr(t *testing.T) {
|
||||||
|
type V struct {
|
||||||
|
Key int
|
||||||
|
Foo string
|
||||||
|
DontChange string
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := &V{
|
||||||
|
Key: 42,
|
||||||
|
Foo: "foo",
|
||||||
|
DontChange: "don't change me",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Decode(&actual, testReadFile(t, "flat.hcl"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := &V{
|
||||||
|
Key: 7,
|
||||||
|
Foo: "bar",
|
||||||
|
DontChange: "don't change me",
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("Actual: %#v\n\nExpected: %#v", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecode_structureArray(t *testing.T) {
|
func TestDecode_structureArray(t *testing.T) {
|
||||||
// This test is extracted from a failure in Consul (consul.io),
|
// This test is extracted from a failure in Consul (consul.io),
|
||||||
// hence the interesting structure naming.
|
// hence the interesting structure naming.
|
||||||
@ -786,6 +815,36 @@ func TestDecode_structureMapInvalid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecode_structureMapExtraKeys(t *testing.T) {
|
||||||
|
type hclVariable struct {
|
||||||
|
A int
|
||||||
|
B int
|
||||||
|
Found []string `hcl:",decodedFields"`
|
||||||
|
Extra []string `hcl:",unusedKeys"`
|
||||||
|
}
|
||||||
|
|
||||||
|
q := hclVariable{
|
||||||
|
A: 1,
|
||||||
|
B: 2,
|
||||||
|
Found: []string{"A", "B"},
|
||||||
|
Extra: []string{"extra1", "extra2"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var p hclVariable
|
||||||
|
ast, _ := Parse(testReadFile(t, "structure_map_extra_keys.hcl"))
|
||||||
|
DecodeObject(&p, ast)
|
||||||
|
if !reflect.DeepEqual(p, q) {
|
||||||
|
t.Fatal("not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
var j hclVariable
|
||||||
|
ast, _ = Parse(testReadFile(t, "structure_map_extra_keys.json"))
|
||||||
|
DecodeObject(&j, ast)
|
||||||
|
if !reflect.DeepEqual(p, j) {
|
||||||
|
t.Fatal("not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecode_interfaceNonPointer(t *testing.T) {
|
func TestDecode_interfaceNonPointer(t *testing.T) {
|
||||||
var value interface{}
|
var value interface{}
|
||||||
err := Decode(value, testReadFile(t, "basic_int_string.hcl"))
|
err := Decode(value, testReadFile(t, "basic_int_string.hcl"))
|
||||||
|
@ -240,7 +240,7 @@ func TestListType_lineComment(t *testing.T) {
|
|||||||
comment := l.comment[i]
|
comment := l.comment[i]
|
||||||
|
|
||||||
if (lt.LineComment == nil) != (comment == "") {
|
if (lt.LineComment == nil) != (comment == "") {
|
||||||
t.Fatalf("bad: %s", lt)
|
t.Fatalf("bad: %s", lt.Token.Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
if comment == "" {
|
if comment == "" {
|
||||||
|
4
test-fixtures/structure_map_extra_keys.hcl
Normal file
4
test-fixtures/structure_map_extra_keys.hcl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
a = 1
|
||||||
|
b = 2
|
||||||
|
extra1 = 3
|
||||||
|
extra2 = 4
|
6
test-fixtures/structure_map_extra_keys.json
Normal file
6
test-fixtures/structure_map_extra_keys.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"a": 1,
|
||||||
|
"b": 2,
|
||||||
|
"extra1": 3,
|
||||||
|
"extra2": 4
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user