diff --git a/decoder.go b/decoder.go index 81c939d..7fb14ab 100644 --- a/decoder.go +++ b/decoder.go @@ -31,8 +31,8 @@ func DecodeObject(out interface{}, n *hcl.Object) error { return d.decode("root", n, reflect.ValueOf(out).Elem()) } -type decoder struct{ - last, current reflect.Kind +type decoder struct { + stack []reflect.Kind } func (d *decoder) decode(name string, o *hcl.Object, result reflect.Value) error { @@ -47,10 +47,15 @@ func (d *decoder) decode(name string, o *hcl.Object, result reflect.Value) error } } - // Keep track of the last known type and the current since we use - // some context to determine things. - d.last = d.current - d.current = k.Kind() + // Push current onto stack unless it is an interface. + if k.Kind() != reflect.Interface { + d.stack = append(d.stack, k.Kind()) + + // Schedule a pop + defer func() { + d.stack = d.stack[:len(d.stack)-1] + }() + } switch k.Kind() { case reflect.Bool: @@ -122,7 +127,7 @@ func (d *decoder) decodeInterface(name string, o *hcl.Object, result reflect.Val // If we're at the root or we're directly within a slice, then we // decode objects into map[string]interface{}, otherwise we decode // them into lists. - if d.last == reflect.Invalid || d.last == reflect.Slice { + if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { var temp map[string]interface{} tempVal := reflect.ValueOf(temp) result := reflect.MakeMap( @@ -139,29 +144,11 @@ func (d *decoder) decodeInterface(name string, o *hcl.Object, result reflect.Val set = result } case hcl.ValueTypeList: - /* - var temp []interface{} - tempVal := reflect.ValueOf(temp) - result := reflect.MakeSlice( - reflect.SliceOf(tempVal.Type().Elem()), 0, 0) - set = result - */ - redecode = false - list := o.Value.([]*hcl.Object) - result := make([]interface{}, 0, len(list)) - - for _, elem := range list { - raw := new(interface{}) - err := d.decode( - name, elem, reflect.Indirect(reflect.ValueOf(raw))) - if err != nil { - return err - } - - result = append(result, *raw) - } - - set = reflect.ValueOf(result) + var temp []interface{} + tempVal := reflect.ValueOf(temp) + result := reflect.MakeSlice( + reflect.SliceOf(tempVal.Type().Elem()), 0, 0) + set = result case hcl.ValueTypeBool: var result bool set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) diff --git a/decoder_test.go b/decoder_test.go index ffbceca..1a8abcf 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -115,7 +115,7 @@ func TestDecode_interface(t *testing.T) { "structure_list.json", false, map[string]interface{}{ - "foo": []map[string]interface{}{ + "foo": []interface{}{ map[string]interface{}{ "key": 7, }, @@ -125,10 +125,32 @@ func TestDecode_interface(t *testing.T) { }, }, }, + { + "structure_list_deep.json", + false, + map[string]interface{}{ + "bar": []map[string]interface{}{ + map[string]interface{}{ + "foo": []map[string]interface{}{ + map[string]interface{}{ + "name": "terraform_example", + "ingress": []interface{}{ + map[string]interface{}{ + "from_port": 22, + }, + map[string]interface{}{ + "from_port": 80, + }, + }, + }, + }, + }, + }, + }, + }, } for _, tc := range cases { - if tc.File != "empty.hcl" { continue } d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.File)) if err != nil { t.Fatalf("err: %s", err) diff --git a/test-fixtures/structure_list_deep.json b/test-fixtures/structure_list_deep.json new file mode 100644 index 0000000..46e98be --- /dev/null +++ b/test-fixtures/structure_list_deep.json @@ -0,0 +1,16 @@ +{ + "bar": { + "foo": { + "name": "terraform_example", + "ingress": [ + { + "from_port": 22 + }, + { + "from_port": 80 + } + ] + } + } +} +