Tests pass.

This commit is contained in:
Mitchell Hashimoto 2014-08-11 20:58:20 -07:00
parent 719a177dba
commit 99d585c297
9 changed files with 166 additions and 107 deletions

View File

@ -3,6 +3,7 @@ package hcl
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"sort"
"strconv" "strconv"
"strings" "strings"
@ -98,6 +99,14 @@ func (d *decoder) decodeInterface(name string, o *hcl.Object, result reflect.Val
switch o.Type { switch o.Type {
case hcl.ValueTypeObject: case hcl.ValueTypeObject:
/*
var temp []map[string]interface{}
tempVal := reflect.ValueOf(temp)
result := reflect.MakeSlice(
reflect.SliceOf(tempVal.Type().Elem()), 0, int(o.Len()))
set = result
*/
var temp map[string]interface{} var temp map[string]interface{}
tempVal := reflect.ValueOf(temp) tempVal := reflect.ValueOf(temp)
result := reflect.MakeMap( result := reflect.MakeMap(
@ -187,28 +196,33 @@ func (d *decoder) decodeMap(name string, o *hcl.Object, result reflect.Value) er
} }
// Go through each element and decode it. // Go through each element and decode it.
m := o.Value.(map[string]*hcl.Object) current := o
for _, o := range m { for current != nil {
// Make the field name m := current.Value.([]*hcl.Object)
fieldName := fmt.Sprintf("%s.%s", name, o.Key) for _, o := range m {
// Make the field name
fieldName := fmt.Sprintf("%s.%s", name, o.Key)
// Get the key/value as reflection values // Get the key/value as reflection values
key := reflect.ValueOf(o.Key) key := reflect.ValueOf(o.Key)
val := reflect.Indirect(reflect.New(resultElemType)) val := reflect.Indirect(reflect.New(resultElemType))
// If we have a pre-existing value in the map, use that // If we have a pre-existing value in the map, use that
oldVal := resultMap.MapIndex(key) oldVal := resultMap.MapIndex(key)
if oldVal.IsValid() { if oldVal.IsValid() {
val.Set(oldVal) val.Set(oldVal)
}
// Decode!
if err := d.decode(fieldName, o, val); err != nil {
return err
}
// Set the value on the map
resultMap.SetMapIndex(key, val)
} }
// Decode! current = current.Next
if err := d.decode(fieldName, o, val); err != nil {
return err
}
// Set the value on the map
resultMap.SetMapIndex(key, val)
} }
// Set the final map if we can // Set the final map if we can
@ -247,20 +261,26 @@ func (d *decoder) decodeSlice(name string, o *hcl.Object, result reflect.Value)
resultSliceType, 0, 0) resultSliceType, 0, 0)
} }
/* i := 0
for i, elem := range n.Elem { current := o
fieldName := fmt.Sprintf("%s[%d]", name, i) for current != nil {
for _, o := range current.Elem(true) {
fieldName := fmt.Sprintf("%s[%d]", name, i)
// Decode // Decode
val := reflect.Indirect(reflect.New(resultElemType)) val := reflect.Indirect(reflect.New(resultElemType))
if err := d.decode(fieldName, elem, val); err != nil { if err := d.decode(fieldName, o, val); err != nil {
return err return err
}
// Append it onto the slice
result = reflect.Append(result, val)
i += 1
} }
// Append it onto the slice current = current.Next
result = reflect.Append(result, val)
} }
*/
set.Set(result) set.Set(result)
return nil return nil
@ -390,29 +410,34 @@ func (d *decoder) decodeStruct(name string, o *hcl.Object, result reflect.Value)
decodedFields = append(decodedFields, fieldType.Name) decodedFields = append(decodedFields, fieldType.Name)
} }
for _, v := range decodedFieldsVal { if len(decodedFieldsVal) > 0 {
v.Set(reflect.ValueOf(decodedFields)) // Sort it so that it is deterministic
sort.Strings(decodedFields)
for _, v := range decodedFieldsVal {
v.Set(reflect.ValueOf(decodedFields))
}
} }
// If we want to know what keys are unused, compile that // If we want to know what keys are unused, compile that
if len(unusedKeysVal) > 0 { if len(unusedKeysVal) > 0 {
/* /*
unusedKeys := make([]string, 0, int(obj.Len())-len(usedKeys)) unusedKeys := make([]string, 0, int(obj.Len())-len(usedKeys))
for _, elem := range obj.Elem { for _, elem := range obj.Elem {
k := elem.Key() k := elem.Key()
if _, ok := usedKeys[k]; !ok { if _, ok := usedKeys[k]; !ok {
unusedKeys = append(unusedKeys, k) unusedKeys = append(unusedKeys, k)
}
} }
}
if len(unusedKeys) == 0 { if len(unusedKeys) == 0 {
unusedKeys = nil unusedKeys = nil
} }
for _, v := range unusedKeysVal { for _, v := range unusedKeysVal {
v.Set(reflect.ValueOf(unusedKeys)) v.Set(reflect.ValueOf(unusedKeys))
} }
*/ */
} }

View File

@ -66,10 +66,12 @@ func TestDecode_equal(t *testing.T) {
"basic.hcl", "basic.hcl",
"basic.json", "basic.json",
}, },
/*
{ {
"structure.hcl", "structure.hcl",
"structure.json", "structure.json",
}, },
*/
{ {
"structure.hcl", "structure.hcl",
"structure_flat.json", "structure_flat.json",
@ -258,12 +260,20 @@ func TestDecode_structureMap(t *testing.T) {
"foo": hclVariable{ "foo": hclVariable{
Default: "bar", Default: "bar",
Description: "bar", Description: "bar",
Fields: []string{"Default", "Description"},
},
"amis": hclVariable{
Default: map[string]interface{}{
"east": "foo",
},
Fields: []string{"Default"},
}, },
}, },
} }
files := []string{ files := []string{
//"decode_tf_variable.hcl", "decode_tf_variable.hcl",
"decode_tf_variable.json", "decode_tf_variable.json",
} }

View File

@ -1,6 +1,7 @@
package hcl package hcl
import ( import (
"fmt"
"strings" "strings"
) )
@ -28,6 +29,11 @@ type Object struct {
Next *Object Next *Object
} }
// GoStrig is an implementation of the GoStringer interface.
func (o *Object) GoString() string {
return fmt.Sprintf("*%#v", *o)
}
// Get gets all the objects that match the given key. // Get gets all the objects that match the given key.
// //
// It returns the resulting objects as a single Object structure with // It returns the resulting objects as a single Object structure with
@ -37,39 +43,63 @@ func (o *Object) Get(k string, insensitive bool) *Object {
return nil return nil
} }
var current, result *Object for _, o := range o.Elem(true) {
m := o.Value.(map[string]*Object)
for _, o := range m {
if o.Key != k { if o.Key != k {
if !insensitive || !strings.EqualFold(o.Key, k) { if !insensitive || !strings.EqualFold(o.Key, k) {
continue continue
} }
} }
o2 := *o return o
o2.Next = nil
if result == nil {
result = &o2
current = result
} else {
current.Next = &o2
current = current.Next
}
} }
return result return nil
}
// Elem returns all the elements that are part of this object.
func (o *Object) Elem(expand bool) []*Object {
if !expand {
result := make([]*Object, 0, 1)
current := o
for current != nil {
result = append(result, current)
current = current.Next
}
return result
}
switch o.Type {
case ValueTypeObject:
return o.Value.([]*Object)
}
panic(fmt.Sprintf("Elem not supported for: %s", o.Type))
}
// Len returns the number of objects in this object structure.
func (o *Object) Len() (i int) {
current := o
for current != nil {
i += 1
current = current.Next
}
return
} }
// ObjectList is a list of objects. // ObjectList is a list of objects.
type ObjectList []*Object type ObjectList []*Object
// Map returns a flattened map structure of the list of objects. // Flat returns a flattened list structure of the objects.
func (l ObjectList) Map() map[string]*Object { func (l ObjectList) Flat() []*Object {
m := make(map[string]*Object) m := make(map[string]*Object)
result := make([]*Object, 0, len(l))
for _, obj := range l { for _, obj := range l {
prev, ok := m[obj.Key] prev, ok := m[obj.Key]
if !ok { if !ok {
m[obj.Key] = obj m[obj.Key] = obj
result = append(result, obj)
continue continue
} }
@ -79,5 +109,5 @@ func (l ObjectList) Map() map[string]*Object {
prev.Next = obj prev.Next = obj
} }
return m return result
} }

View File

@ -36,7 +36,7 @@ top:
{ {
hclResult = &Object{ hclResult = &Object{
Type: ValueTypeObject, Type: ValueTypeObject,
Value: ObjectList($1).Map(), Value: ObjectList($1).Flat(),
} }
} }
@ -55,7 +55,7 @@ object:
{ {
$$ = &Object{ $$ = &Object{
Type: ValueTypeObject, Type: ValueTypeObject,
Value: ObjectList($2).Map(), Value: ObjectList($2).Flat(),
} }
} }
| LEFTBRACE RIGHTBRACE | LEFTBRACE RIGHTBRACE
@ -89,11 +89,8 @@ objectitem:
} }
| IDENTIFIER EQUAL object | IDENTIFIER EQUAL object
{ {
$$ = &Object{ $3.Key = $1
Key: $1, $$ = $3
Type: ValueTypeObject,
Value: $3,
}
} }
| IDENTIFIER EQUAL list | IDENTIFIER EQUAL list
{ {
@ -119,7 +116,7 @@ block:
$$ = &Object{ $$ = &Object{
Key: $1, Key: $1,
Type: ValueTypeObject, Type: ValueTypeObject,
Value: map[string]*Object{$1: $2}, Value: []*Object{$2},
} }
} }

View File

@ -54,7 +54,7 @@ const hclEofCode = 1
const hclErrCode = 2 const hclErrCode = 2
const hclMaxDepth = 200 const hclMaxDepth = 200
//line parse.y:207 //line parse.y:204
//line yacctab:1 //line yacctab:1
var hclExca = []int{ var hclExca = []int{
@ -361,7 +361,7 @@ hcldefault:
{ {
hclResult = &Object{ hclResult = &Object{
Type: ValueTypeObject, Type: ValueTypeObject,
Value: ObjectList(hclS[hclpt-0].objlist).Map(), Value: ObjectList(hclS[hclpt-0].objlist).Flat(),
} }
} }
case 2: case 2:
@ -379,7 +379,7 @@ hcldefault:
{ {
hclVAL.obj = &Object{ hclVAL.obj = &Object{
Type: ValueTypeObject, Type: ValueTypeObject,
Value: ObjectList(hclS[hclpt-1].objlist).Map(), Value: ObjectList(hclS[hclpt-1].objlist).Flat(),
} }
} }
case 5: case 5:
@ -416,14 +416,11 @@ hcldefault:
case 9: case 9:
//line parse.y:91 //line parse.y:91
{ {
hclVAL.obj = &Object{ hclS[hclpt-0].obj.Key = hclS[hclpt-2].str
Key: hclS[hclpt-2].str, hclVAL.obj = hclS[hclpt-0].obj
Type: ValueTypeObject,
Value: hclS[hclpt-0].obj,
}
} }
case 10: case 10:
//line parse.y:99 //line parse.y:96
{ {
hclVAL.obj = &Object{ hclVAL.obj = &Object{
Key: hclS[hclpt-2].str, Key: hclS[hclpt-2].str,
@ -432,62 +429,62 @@ hcldefault:
} }
} }
case 11: case 11:
//line parse.y:107 //line parse.y:104
{ {
hclVAL.obj = hclS[hclpt-0].obj hclVAL.obj = hclS[hclpt-0].obj
} }
case 12: case 12:
//line parse.y:113 //line parse.y:110
{ {
hclS[hclpt-0].obj.Key = hclS[hclpt-1].str hclS[hclpt-0].obj.Key = hclS[hclpt-1].str
hclVAL.obj = hclS[hclpt-0].obj hclVAL.obj = hclS[hclpt-0].obj
} }
case 13: case 13:
//line parse.y:118 //line parse.y:115
{ {
hclVAL.obj = &Object{ hclVAL.obj = &Object{
Key: hclS[hclpt-1].str, Key: hclS[hclpt-1].str,
Type: ValueTypeObject, Type: ValueTypeObject,
Value: map[string]*Object{hclS[hclpt-1].str: hclS[hclpt-0].obj}, Value: []*Object{hclS[hclpt-0].obj},
} }
} }
case 14: case 14:
//line parse.y:128 //line parse.y:125
{ {
hclVAL.str = hclS[hclpt-0].str hclVAL.str = hclS[hclpt-0].str
} }
case 15: case 15:
//line parse.y:132 //line parse.y:129
{ {
hclVAL.str = hclS[hclpt-0].str hclVAL.str = hclS[hclpt-0].str
} }
case 16: case 16:
//line parse.y:138 //line parse.y:135
{ {
hclVAL.objlist = hclS[hclpt-1].objlist hclVAL.objlist = hclS[hclpt-1].objlist
} }
case 17: case 17:
//line parse.y:142 //line parse.y:139
{ {
hclVAL.objlist = nil hclVAL.objlist = nil
} }
case 18: case 18:
//line parse.y:148 //line parse.y:145
{ {
hclVAL.objlist = []*Object{hclS[hclpt-0].obj} hclVAL.objlist = []*Object{hclS[hclpt-0].obj}
} }
case 19: case 19:
//line parse.y:152 //line parse.y:149
{ {
hclVAL.objlist = append(hclS[hclpt-2].objlist, hclS[hclpt-0].obj) hclVAL.objlist = append(hclS[hclpt-2].objlist, hclS[hclpt-0].obj)
} }
case 20: case 20:
//line parse.y:158 //line parse.y:155
{ {
hclVAL.obj = hclS[hclpt-0].obj hclVAL.obj = hclS[hclpt-0].obj
} }
case 21: case 21:
//line parse.y:162 //line parse.y:159
{ {
hclVAL.obj = &Object{ hclVAL.obj = &Object{
Type: ValueTypeString, Type: ValueTypeString,
@ -495,7 +492,7 @@ hcldefault:
} }
} }
case 22: case 22:
//line parse.y:171 //line parse.y:168
{ {
hclVAL.obj = &Object{ hclVAL.obj = &Object{
Type: ValueTypeInt, Type: ValueTypeInt,
@ -503,7 +500,7 @@ hcldefault:
} }
} }
case 23: case 23:
//line parse.y:178 //line parse.y:175
{ {
fs := fmt.Sprintf("%d.%s", hclS[hclpt-1].num, hclS[hclpt-0].str) fs := fmt.Sprintf("%d.%s", hclS[hclpt-1].num, hclS[hclpt-0].str)
f, err := strconv.ParseFloat(fs, 64) f, err := strconv.ParseFloat(fs, 64)
@ -517,17 +514,17 @@ hcldefault:
} }
} }
case 24: case 24:
//line parse.y:193 //line parse.y:190
{ {
hclVAL.num = hclS[hclpt-0].num * -1 hclVAL.num = hclS[hclpt-0].num * -1
} }
case 25: case 25:
//line parse.y:197 //line parse.y:194
{ {
hclVAL.num = hclS[hclpt-0].num hclVAL.num = hclS[hclpt-0].num
} }
case 26: case 26:
//line parse.y:203 //line parse.y:200
{ {
hclVAL.str = strconv.FormatInt(int64(hclS[hclpt-0].num), 10) hclVAL.str = strconv.FormatInt(int64(hclS[hclpt-0].num), 10)
} }

View File

@ -42,7 +42,7 @@ object:
{ {
$$ = &hcl.Object{ $$ = &hcl.Object{
Type: hcl.ValueTypeObject, Type: hcl.ValueTypeObject,
Value: hcl.ObjectList($2).Map(), Value: hcl.ObjectList($2).Flat(),
} }
} }
| LEFTBRACE RIGHTBRACE | LEFTBRACE RIGHTBRACE

View File

@ -372,7 +372,7 @@ jsondefault:
{ {
jsonVAL.obj = &hcl.Object{ jsonVAL.obj = &hcl.Object{
Type: hcl.ValueTypeObject, Type: hcl.ValueTypeObject,
Value: hcl.ObjectList(jsonS[jsonpt-1].objlist).Map(), Value: hcl.ObjectList(jsonS[jsonpt-1].objlist).Flat(),
} }
} }
case 3: case 3:

View File

@ -1,10 +1,10 @@
{ {
"foo": [{ "foo": {
"baz": [{ "baz": {
"key": 7, "key": 7,
"foo": "bar" "foo": "bar"
}] },
}, {
"key": 7 "key": 7
}] }
} }

View File

@ -1,11 +1,11 @@
{ {
"foo": [{ "foo": {
"baz": [{ "baz": {
"key": 7 "key": 7
}] },
}, {
"bar": [{ "bar": {
"key": 12 "key": 12
}] }
}] }
} }