Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unused keys #275

Merged
merged 6 commits into from
Apr 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
// if pointer is not nil, decode into existing value
if !result.IsNil() {
return d.decode(name, node, result.Elem())
}

schmichael marked this conversation as resolved.
Show resolved Hide resolved
// Create an element of the concrete (non pointer) type and decode
// into that. Then set the value of the pointer to this type.
resultType := result.Type()
Expand Down Expand Up @@ -512,7 +517,7 @@ func expandObject(node ast.Node, result reflect.Value) ast.Node {
// we need to un-flatten the ast enough to decode
newNode := &ast.ObjectItem{
Keys: []*ast.ObjectKey{
&ast.ObjectKey{
{
Token: keyToken,
},
},
Expand Down Expand Up @@ -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))
decodedFieldsVal := 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))
langmartin marked this conversation as resolved.
Show resolved Hide resolved
}
}

for _, f := range fields {
field, fieldValue := f.field, f.val
if !fieldValue.IsValid() {
Expand Down Expand Up @@ -689,8 +703,8 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
continue
}

// Track the used key
usedKeys[fieldName] = struct{}{}
// Track the used keys
unusedNodeKeys = removeCaseFold(unusedNodeKeys, fieldName)

// Create the field name and decode. We range over the elements
// because we actually want the value.
Expand Down Expand Up @@ -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
}

Expand All @@ -734,3 +756,12 @@ func findNodeType() reflect.Type {
value := reflect.ValueOf(nodeContainer).FieldByName("Node")
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
}
59 changes: 59 additions & 0 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
// This test is extracted from a failure in Consul (consul.io),
// hence the interesting structure naming.
Expand Down Expand Up @@ -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) {
var value interface{}
err := Decode(value, testReadFile(t, "basic_int_string.hcl"))
Expand Down
2 changes: 1 addition & 1 deletion hcl/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func TestListType_lineComment(t *testing.T) {
comment := l.comment[i]

if (lt.LineComment == nil) != (comment == "") {
t.Fatalf("bad: %s", lt)
t.Fatalf("bad: %s", lt.Token.Value())
}

if comment == "" {
Expand Down
4 changes: 4 additions & 0 deletions test-fixtures/structure_map_extra_keys.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
a = 1
b = 2
extra1 = 3
extra2 = 4
6 changes: 6 additions & 0 deletions test-fixtures/structure_map_extra_keys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"a": 1,
"b": 2,
"extra1": 3,
"extra2": 4
}