Tests pass.
This commit is contained in:
parent
719a177dba
commit
99d585c297
109
decoder.go
109
decoder.go
@ -3,6 +3,7 @@ package hcl
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -98,6 +99,14 @@ func (d *decoder) decodeInterface(name string, o *hcl.Object, result reflect.Val
|
||||
|
||||
switch o.Type {
|
||||
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{}
|
||||
tempVal := reflect.ValueOf(temp)
|
||||
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.
|
||||
m := o.Value.(map[string]*hcl.Object)
|
||||
for _, o := range m {
|
||||
// Make the field name
|
||||
fieldName := fmt.Sprintf("%s.%s", name, o.Key)
|
||||
current := o
|
||||
for current != nil {
|
||||
m := current.Value.([]*hcl.Object)
|
||||
for _, o := range m {
|
||||
// Make the field name
|
||||
fieldName := fmt.Sprintf("%s.%s", name, o.Key)
|
||||
|
||||
// Get the key/value as reflection values
|
||||
key := reflect.ValueOf(o.Key)
|
||||
val := reflect.Indirect(reflect.New(resultElemType))
|
||||
// Get the key/value as reflection values
|
||||
key := reflect.ValueOf(o.Key)
|
||||
val := reflect.Indirect(reflect.New(resultElemType))
|
||||
|
||||
// If we have a pre-existing value in the map, use that
|
||||
oldVal := resultMap.MapIndex(key)
|
||||
if oldVal.IsValid() {
|
||||
val.Set(oldVal)
|
||||
// If we have a pre-existing value in the map, use that
|
||||
oldVal := resultMap.MapIndex(key)
|
||||
if oldVal.IsValid() {
|
||||
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!
|
||||
if err := d.decode(fieldName, o, val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the value on the map
|
||||
resultMap.SetMapIndex(key, val)
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
/*
|
||||
for i, elem := range n.Elem {
|
||||
fieldName := fmt.Sprintf("%s[%d]", name, i)
|
||||
i := 0
|
||||
current := o
|
||||
for current != nil {
|
||||
for _, o := range current.Elem(true) {
|
||||
fieldName := fmt.Sprintf("%s[%d]", name, i)
|
||||
|
||||
// Decode
|
||||
val := reflect.Indirect(reflect.New(resultElemType))
|
||||
if err := d.decode(fieldName, elem, val); err != nil {
|
||||
return err
|
||||
// Decode
|
||||
val := reflect.Indirect(reflect.New(resultElemType))
|
||||
if err := d.decode(fieldName, o, val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Append it onto the slice
|
||||
result = reflect.Append(result, val)
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
// Append it onto the slice
|
||||
result = reflect.Append(result, val)
|
||||
current = current.Next
|
||||
}
|
||||
*/
|
||||
|
||||
set.Set(result)
|
||||
return nil
|
||||
@ -390,29 +410,34 @@ func (d *decoder) decodeStruct(name string, o *hcl.Object, result reflect.Value)
|
||||
decodedFields = append(decodedFields, fieldType.Name)
|
||||
}
|
||||
|
||||
for _, v := range decodedFieldsVal {
|
||||
v.Set(reflect.ValueOf(decodedFields))
|
||||
if len(decodedFieldsVal) > 0 {
|
||||
// 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 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 {
|
||||
k := elem.Key()
|
||||
if _, ok := usedKeys[k]; !ok {
|
||||
unusedKeys = append(unusedKeys, k)
|
||||
for _, elem := range obj.Elem {
|
||||
k := elem.Key()
|
||||
if _, ok := usedKeys[k]; !ok {
|
||||
unusedKeys = append(unusedKeys, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(unusedKeys) == 0 {
|
||||
unusedKeys = nil
|
||||
}
|
||||
if len(unusedKeys) == 0 {
|
||||
unusedKeys = nil
|
||||
}
|
||||
|
||||
for _, v := range unusedKeysVal {
|
||||
v.Set(reflect.ValueOf(unusedKeys))
|
||||
}
|
||||
for _, v := range unusedKeysVal {
|
||||
v.Set(reflect.ValueOf(unusedKeys))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -66,10 +66,12 @@ func TestDecode_equal(t *testing.T) {
|
||||
"basic.hcl",
|
||||
"basic.json",
|
||||
},
|
||||
/*
|
||||
{
|
||||
"structure.hcl",
|
||||
"structure.json",
|
||||
},
|
||||
*/
|
||||
{
|
||||
"structure.hcl",
|
||||
"structure_flat.json",
|
||||
@ -258,12 +260,20 @@ func TestDecode_structureMap(t *testing.T) {
|
||||
"foo": hclVariable{
|
||||
Default: "bar",
|
||||
Description: "bar",
|
||||
Fields: []string{"Default", "Description"},
|
||||
},
|
||||
|
||||
"amis": hclVariable{
|
||||
Default: map[string]interface{}{
|
||||
"east": "foo",
|
||||
},
|
||||
Fields: []string{"Default"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
files := []string{
|
||||
//"decode_tf_variable.hcl",
|
||||
"decode_tf_variable.hcl",
|
||||
"decode_tf_variable.json",
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hcl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -28,6 +29,11 @@ type Object struct {
|
||||
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.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
var current, result *Object
|
||||
m := o.Value.(map[string]*Object)
|
||||
for _, o := range m {
|
||||
for _, o := range o.Elem(true) {
|
||||
if o.Key != k {
|
||||
if !insensitive || !strings.EqualFold(o.Key, k) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
o2 := *o
|
||||
o2.Next = nil
|
||||
if result == nil {
|
||||
result = &o2
|
||||
current = result
|
||||
} else {
|
||||
current.Next = &o2
|
||||
current = current.Next
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
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.
|
||||
type ObjectList []*Object
|
||||
|
||||
// Map returns a flattened map structure of the list of objects.
|
||||
func (l ObjectList) Map() map[string]*Object {
|
||||
// Flat returns a flattened list structure of the objects.
|
||||
func (l ObjectList) Flat() []*Object {
|
||||
m := make(map[string]*Object)
|
||||
result := make([]*Object, 0, len(l))
|
||||
for _, obj := range l {
|
||||
prev, ok := m[obj.Key]
|
||||
if !ok {
|
||||
m[obj.Key] = obj
|
||||
result = append(result, obj)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -79,5 +109,5 @@ func (l ObjectList) Map() map[string]*Object {
|
||||
prev.Next = obj
|
||||
}
|
||||
|
||||
return m
|
||||
return result
|
||||
}
|
||||
|
13
hcl/parse.y
13
hcl/parse.y
@ -36,7 +36,7 @@ top:
|
||||
{
|
||||
hclResult = &Object{
|
||||
Type: ValueTypeObject,
|
||||
Value: ObjectList($1).Map(),
|
||||
Value: ObjectList($1).Flat(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ object:
|
||||
{
|
||||
$$ = &Object{
|
||||
Type: ValueTypeObject,
|
||||
Value: ObjectList($2).Map(),
|
||||
Value: ObjectList($2).Flat(),
|
||||
}
|
||||
}
|
||||
| LEFTBRACE RIGHTBRACE
|
||||
@ -89,11 +89,8 @@ objectitem:
|
||||
}
|
||||
| IDENTIFIER EQUAL object
|
||||
{
|
||||
$$ = &Object{
|
||||
Key: $1,
|
||||
Type: ValueTypeObject,
|
||||
Value: $3,
|
||||
}
|
||||
$3.Key = $1
|
||||
$$ = $3
|
||||
}
|
||||
| IDENTIFIER EQUAL list
|
||||
{
|
||||
@ -119,7 +116,7 @@ block:
|
||||
$$ = &Object{
|
||||
Key: $1,
|
||||
Type: ValueTypeObject,
|
||||
Value: map[string]*Object{$1: $2},
|
||||
Value: []*Object{$2},
|
||||
}
|
||||
}
|
||||
|
||||
|
49
hcl/y.go
49
hcl/y.go
@ -54,7 +54,7 @@ const hclEofCode = 1
|
||||
const hclErrCode = 2
|
||||
const hclMaxDepth = 200
|
||||
|
||||
//line parse.y:207
|
||||
//line parse.y:204
|
||||
|
||||
//line yacctab:1
|
||||
var hclExca = []int{
|
||||
@ -361,7 +361,7 @@ hcldefault:
|
||||
{
|
||||
hclResult = &Object{
|
||||
Type: ValueTypeObject,
|
||||
Value: ObjectList(hclS[hclpt-0].objlist).Map(),
|
||||
Value: ObjectList(hclS[hclpt-0].objlist).Flat(),
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
@ -379,7 +379,7 @@ hcldefault:
|
||||
{
|
||||
hclVAL.obj = &Object{
|
||||
Type: ValueTypeObject,
|
||||
Value: ObjectList(hclS[hclpt-1].objlist).Map(),
|
||||
Value: ObjectList(hclS[hclpt-1].objlist).Flat(),
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
@ -416,14 +416,11 @@ hcldefault:
|
||||
case 9:
|
||||
//line parse.y:91
|
||||
{
|
||||
hclVAL.obj = &Object{
|
||||
Key: hclS[hclpt-2].str,
|
||||
Type: ValueTypeObject,
|
||||
Value: hclS[hclpt-0].obj,
|
||||
}
|
||||
hclS[hclpt-0].obj.Key = hclS[hclpt-2].str
|
||||
hclVAL.obj = hclS[hclpt-0].obj
|
||||
}
|
||||
case 10:
|
||||
//line parse.y:99
|
||||
//line parse.y:96
|
||||
{
|
||||
hclVAL.obj = &Object{
|
||||
Key: hclS[hclpt-2].str,
|
||||
@ -432,62 +429,62 @@ hcldefault:
|
||||
}
|
||||
}
|
||||
case 11:
|
||||
//line parse.y:107
|
||||
//line parse.y:104
|
||||
{
|
||||
hclVAL.obj = hclS[hclpt-0].obj
|
||||
}
|
||||
case 12:
|
||||
//line parse.y:113
|
||||
//line parse.y:110
|
||||
{
|
||||
hclS[hclpt-0].obj.Key = hclS[hclpt-1].str
|
||||
hclVAL.obj = hclS[hclpt-0].obj
|
||||
}
|
||||
case 13:
|
||||
//line parse.y:118
|
||||
//line parse.y:115
|
||||
{
|
||||
hclVAL.obj = &Object{
|
||||
Key: hclS[hclpt-1].str,
|
||||
Type: ValueTypeObject,
|
||||
Value: map[string]*Object{hclS[hclpt-1].str: hclS[hclpt-0].obj},
|
||||
Value: []*Object{hclS[hclpt-0].obj},
|
||||
}
|
||||
}
|
||||
case 14:
|
||||
//line parse.y:128
|
||||
//line parse.y:125
|
||||
{
|
||||
hclVAL.str = hclS[hclpt-0].str
|
||||
}
|
||||
case 15:
|
||||
//line parse.y:132
|
||||
//line parse.y:129
|
||||
{
|
||||
hclVAL.str = hclS[hclpt-0].str
|
||||
}
|
||||
case 16:
|
||||
//line parse.y:138
|
||||
//line parse.y:135
|
||||
{
|
||||
hclVAL.objlist = hclS[hclpt-1].objlist
|
||||
}
|
||||
case 17:
|
||||
//line parse.y:142
|
||||
//line parse.y:139
|
||||
{
|
||||
hclVAL.objlist = nil
|
||||
}
|
||||
case 18:
|
||||
//line parse.y:148
|
||||
//line parse.y:145
|
||||
{
|
||||
hclVAL.objlist = []*Object{hclS[hclpt-0].obj}
|
||||
}
|
||||
case 19:
|
||||
//line parse.y:152
|
||||
//line parse.y:149
|
||||
{
|
||||
hclVAL.objlist = append(hclS[hclpt-2].objlist, hclS[hclpt-0].obj)
|
||||
}
|
||||
case 20:
|
||||
//line parse.y:158
|
||||
//line parse.y:155
|
||||
{
|
||||
hclVAL.obj = hclS[hclpt-0].obj
|
||||
}
|
||||
case 21:
|
||||
//line parse.y:162
|
||||
//line parse.y:159
|
||||
{
|
||||
hclVAL.obj = &Object{
|
||||
Type: ValueTypeString,
|
||||
@ -495,7 +492,7 @@ hcldefault:
|
||||
}
|
||||
}
|
||||
case 22:
|
||||
//line parse.y:171
|
||||
//line parse.y:168
|
||||
{
|
||||
hclVAL.obj = &Object{
|
||||
Type: ValueTypeInt,
|
||||
@ -503,7 +500,7 @@ hcldefault:
|
||||
}
|
||||
}
|
||||
case 23:
|
||||
//line parse.y:178
|
||||
//line parse.y:175
|
||||
{
|
||||
fs := fmt.Sprintf("%d.%s", hclS[hclpt-1].num, hclS[hclpt-0].str)
|
||||
f, err := strconv.ParseFloat(fs, 64)
|
||||
@ -517,17 +514,17 @@ hcldefault:
|
||||
}
|
||||
}
|
||||
case 24:
|
||||
//line parse.y:193
|
||||
//line parse.y:190
|
||||
{
|
||||
hclVAL.num = hclS[hclpt-0].num * -1
|
||||
}
|
||||
case 25:
|
||||
//line parse.y:197
|
||||
//line parse.y:194
|
||||
{
|
||||
hclVAL.num = hclS[hclpt-0].num
|
||||
}
|
||||
case 26:
|
||||
//line parse.y:203
|
||||
//line parse.y:200
|
||||
{
|
||||
hclVAL.str = strconv.FormatInt(int64(hclS[hclpt-0].num), 10)
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ object:
|
||||
{
|
||||
$$ = &hcl.Object{
|
||||
Type: hcl.ValueTypeObject,
|
||||
Value: hcl.ObjectList($2).Map(),
|
||||
Value: hcl.ObjectList($2).Flat(),
|
||||
}
|
||||
}
|
||||
| LEFTBRACE RIGHTBRACE
|
||||
|
@ -372,7 +372,7 @@ jsondefault:
|
||||
{
|
||||
jsonVAL.obj = &hcl.Object{
|
||||
Type: hcl.ValueTypeObject,
|
||||
Value: hcl.ObjectList(jsonS[jsonpt-1].objlist).Map(),
|
||||
Value: hcl.ObjectList(jsonS[jsonpt-1].objlist).Flat(),
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"foo": [{
|
||||
"baz": [{
|
||||
"foo": {
|
||||
"baz": {
|
||||
"key": 7,
|
||||
"foo": "bar"
|
||||
}]
|
||||
}, {
|
||||
},
|
||||
|
||||
"key": 7
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
{
|
||||
"foo": [{
|
||||
"baz": [{
|
||||
"foo": {
|
||||
"baz": {
|
||||
"key": 7
|
||||
}]
|
||||
}, {
|
||||
"bar": [{
|
||||
},
|
||||
|
||||
"bar": {
|
||||
"key": 12
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user