down to one failing test
This commit is contained in:
parent
5aaa3e75b9
commit
e370e34aeb
108
decoder.go
108
decoder.go
@ -4,7 +4,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/hcl/ast"
|
"github.com/hashicorp/hcl/hcl/ast"
|
||||||
"github.com/hashicorp/hcl/hcl/token"
|
"github.com/hashicorp/hcl/hcl/token"
|
||||||
@ -104,7 +106,7 @@ func (d *decoder) decodeBool(name string, node ast.Node, result reflect.Value) e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("%s: unknown type %t", name, node)
|
return fmt.Errorf("%s: unknown type %T", name, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value) error {
|
func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value) error {
|
||||||
@ -121,7 +123,7 @@ func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("%s: unknown type %t", name, node)
|
return fmt.Errorf("%s: unknown type %T", name, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) error {
|
func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) error {
|
||||||
@ -129,26 +131,39 @@ func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) er
|
|||||||
case *ast.LiteralType:
|
case *ast.LiteralType:
|
||||||
switch n.Token.Type {
|
switch n.Token.Type {
|
||||||
case token.NUMBER:
|
case token.NUMBER:
|
||||||
fallthrough
|
v, err := strconv.ParseInt(n.Token.Text, 0, 0)
|
||||||
case token.STRING:
|
|
||||||
v, err := strconv.ParseInt(n.Token.Text, 0, 64)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result.SetInt(int64(v))
|
result.Set(reflect.ValueOf(int(v)))
|
||||||
|
return nil
|
||||||
|
case token.STRING:
|
||||||
|
v, err := strconv.ParseInt(n.Token.Value().(string), 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Set(reflect.ValueOf(int(v)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("%s: unknown type %t", name, node)
|
return fmt.Errorf("%s: unknown type %T", name, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Value) error {
|
func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Value) error {
|
||||||
var set reflect.Value
|
var set reflect.Value
|
||||||
redecode := true
|
redecode := true
|
||||||
|
|
||||||
switch n := node.(type) {
|
// For testing types, ObjectType should just be treated as a list. We
|
||||||
|
// set this to a temporary var because we want to pass in the real node.
|
||||||
|
testNode := node
|
||||||
|
if ot, ok := node.(*ast.ObjectType); ok {
|
||||||
|
testNode = ot.List
|
||||||
|
}
|
||||||
|
|
||||||
|
switch n := testNode.(type) {
|
||||||
case *ast.ObjectList:
|
case *ast.ObjectList:
|
||||||
// If we're at the root or we're directly within a slice, then we
|
// If we're at the root or we're directly within a slice, then we
|
||||||
// decode objects into map[string]interface{}, otherwise we decode
|
// decode objects into map[string]interface{}, otherwise we decode
|
||||||
@ -170,11 +185,25 @@ func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Val
|
|||||||
set = result
|
set = result
|
||||||
}
|
}
|
||||||
case *ast.ObjectType:
|
case *ast.ObjectType:
|
||||||
|
// 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 len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice {
|
||||||
|
var temp map[string]interface{}
|
||||||
|
tempVal := reflect.ValueOf(temp)
|
||||||
|
result := reflect.MakeMap(
|
||||||
|
reflect.MapOf(
|
||||||
|
reflect.TypeOf(""),
|
||||||
|
tempVal.Type().Elem()))
|
||||||
|
|
||||||
|
set = result
|
||||||
|
} else {
|
||||||
var temp []map[string]interface{}
|
var temp []map[string]interface{}
|
||||||
tempVal := reflect.ValueOf(temp)
|
tempVal := reflect.ValueOf(temp)
|
||||||
result := reflect.MakeSlice(
|
result := reflect.MakeSlice(
|
||||||
reflect.SliceOf(tempVal.Type().Elem()), 0, 1)
|
reflect.SliceOf(tempVal.Type().Elem()), 0, 1)
|
||||||
set = result
|
set = result
|
||||||
|
}
|
||||||
case *ast.ListType:
|
case *ast.ListType:
|
||||||
var temp []interface{}
|
var temp []interface{}
|
||||||
tempVal := reflect.ValueOf(temp)
|
tempVal := reflect.ValueOf(temp)
|
||||||
@ -347,6 +376,8 @@ func (d *decoder) decodeSlice(name string, node ast.Node, result reflect.Value)
|
|||||||
}
|
}
|
||||||
case *ast.ObjectType:
|
case *ast.ObjectType:
|
||||||
items = []ast.Node{n}
|
items = []ast.Node{n}
|
||||||
|
case *ast.ListType:
|
||||||
|
items = n.List
|
||||||
}
|
}
|
||||||
|
|
||||||
if items == nil {
|
if items == nil {
|
||||||
@ -375,27 +406,31 @@ func (d *decoder) decodeString(name string, node ast.Node, result reflect.Value)
|
|||||||
case *ast.LiteralType:
|
case *ast.LiteralType:
|
||||||
switch n.Token.Type {
|
switch n.Token.Type {
|
||||||
case token.NUMBER:
|
case token.NUMBER:
|
||||||
fallthrough
|
result.Set(reflect.ValueOf(n.Token.Text).Convert(result.Type()))
|
||||||
|
return nil
|
||||||
case token.STRING:
|
case token.STRING:
|
||||||
result.Set(reflect.ValueOf(n.Token.Value()))
|
result.Set(reflect.ValueOf(n.Token.Value()).Convert(result.Type()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("%s: unknown type %t", name, node)
|
return fmt.Errorf("%s: unknown type %T", name, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) error {
|
func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) error {
|
||||||
return nil
|
var item *ast.ObjectItem
|
||||||
/*
|
if it, ok := node.(*ast.ObjectItem); ok {
|
||||||
item, ok := node.(*ast.ObjectItem)
|
item = it
|
||||||
if !ok {
|
node = it.Val
|
||||||
return fmt.Errorf("%s: not an object type for map (%t)", name, node)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val, ok := node.(*ast.ObjectList)
|
if ot, ok := node.(*ast.ObjectType); ok {
|
||||||
|
node = ot.List
|
||||||
|
}
|
||||||
|
|
||||||
|
list, ok := node.(*ast.ObjectList)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%s: not an object type for map (%t)", name, node)
|
return fmt.Errorf("%s: not an object type for struct (%T)", name, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This slice will keep track of all the structs we'll be decoding.
|
// This slice will keep track of all the structs we'll be decoding.
|
||||||
@ -464,21 +499,21 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
|||||||
|
|
||||||
fieldName := fieldType.Name
|
fieldName := fieldType.Name
|
||||||
|
|
||||||
// This is whether or not we expand the object into its children
|
|
||||||
// later.
|
|
||||||
expand := false
|
|
||||||
|
|
||||||
tagValue := fieldType.Tag.Get(tagName)
|
tagValue := fieldType.Tag.Get(tagName)
|
||||||
tagParts := strings.SplitN(tagValue, ",", 2)
|
tagParts := strings.SplitN(tagValue, ",", 2)
|
||||||
if len(tagParts) >= 2 {
|
if len(tagParts) >= 2 {
|
||||||
switch tagParts[1] {
|
switch tagParts[1] {
|
||||||
case "expand":
|
|
||||||
expand = true
|
|
||||||
case "decodedFields":
|
case "decodedFields":
|
||||||
decodedFieldsVal = append(decodedFieldsVal, field)
|
decodedFieldsVal = append(decodedFieldsVal, field)
|
||||||
continue
|
continue
|
||||||
case "key":
|
case "key":
|
||||||
field.SetString(item.Keys[0].Token.Text)
|
if item == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%s: %s asked for 'key', impossible",
|
||||||
|
name, fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
field.SetString(item.Keys[0].Token.Value().(string))
|
||||||
continue
|
continue
|
||||||
case "unusedKeys":
|
case "unusedKeys":
|
||||||
unusedKeysVal = append(unusedKeysVal, field)
|
unusedKeysVal = append(unusedKeysVal, field)
|
||||||
@ -490,13 +525,20 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
|||||||
fieldName = tagParts[0]
|
fieldName = tagParts[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the element matching this name
|
// Determine the element we'll use to decode. If it is a single
|
||||||
continue
|
// match (only object with the field), then we decode it exactly.
|
||||||
/*
|
// If it is a prefix match, then we decode the matches.
|
||||||
obj := o.Get(fieldName, true)
|
var decodeNode ast.Node
|
||||||
if obj == nil {
|
matches := list.Prefix(fieldName)
|
||||||
|
if len(matches.Items) == 0 {
|
||||||
|
item := list.Get(fieldName)
|
||||||
|
if item == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
decodeNode = item.Val
|
||||||
|
} else {
|
||||||
|
decodeNode = matches
|
||||||
|
}
|
||||||
|
|
||||||
// Track the used key
|
// Track the used key
|
||||||
usedKeys[fieldName] = struct{}{}
|
usedKeys[fieldName] = struct{}{}
|
||||||
@ -504,11 +546,9 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
|||||||
// 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.
|
||||||
fieldName = fmt.Sprintf("%s.%s", name, fieldName)
|
fieldName = fmt.Sprintf("%s.%s", name, fieldName)
|
||||||
for _, obj := range obj.Elem(expand) {
|
if err := d.decode(fieldName, decodeNode, field); err != nil {
|
||||||
if err := d.decode(fieldName, obj, field); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
decodedFields = append(decodedFields, fieldType.Name)
|
decodedFields = append(decodedFields, fieldType.Name)
|
||||||
}
|
}
|
||||||
@ -522,7 +562,5 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ func TestDecode_interface(t *testing.T) {
|
|||||||
"a": 1.02,
|
"a": 1.02,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
{
|
{
|
||||||
"multiline_bad.hcl",
|
"multiline_bad.hcl",
|
||||||
false,
|
false,
|
||||||
@ -67,6 +68,7 @@ func TestDecode_interface(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
map[string]interface{}{"foo": "bar\nbaz"},
|
map[string]interface{}{"foo": "bar\nbaz"},
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"scientific.json",
|
"scientific.json",
|
||||||
false,
|
false,
|
||||||
@ -205,6 +207,7 @@ func TestDecode_interface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
t.Logf("Testing: %s", tc.File)
|
||||||
d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.File))
|
d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.File))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
@ -217,7 +220,7 @@ func TestDecode_interface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(out, tc.Out) {
|
if !reflect.DeepEqual(out, tc.Out) {
|
||||||
t.Fatalf("Input: %s\n\nActual: %#v\n\nExpected: %#v", tc.File, out, tc.Out)
|
t.Fatalf("Input: %s. Actual, Expected.\n\n%#v\n\n%#v", tc.File, out, tc.Out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,7 +399,7 @@ func TestDecode_structureArray(t *testing.T) {
|
|||||||
|
|
||||||
err := Decode(&actual, testReadFile(t, f))
|
err := Decode(&actual, testReadFile(t, f))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("Input: %s\n\nerr: %s", f, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/hcl/token"
|
"github.com/hashicorp/hcl/hcl/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,6 +45,21 @@ func (o *ObjectList) Add(item *ObjectItem) {
|
|||||||
o.Items = append(o.Items, item)
|
o.Items = append(o.Items, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *ObjectList) Get(key string) *ObjectItem {
|
||||||
|
for _, item := range o.Items {
|
||||||
|
if len(item.Keys) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
text := item.Keys[0].Token.Text
|
||||||
|
if text == key || strings.EqualFold(text, key) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (o *ObjectList) Prefix(keys ...string) *ObjectList {
|
func (o *ObjectList) Prefix(keys ...string) *ObjectList {
|
||||||
var result ObjectList
|
var result ObjectList
|
||||||
for _, item := range o.Items {
|
for _, item := range o.Items {
|
||||||
@ -53,7 +70,8 @@ func (o *ObjectList) Prefix(keys ...string) *ObjectList {
|
|||||||
|
|
||||||
match := true
|
match := true
|
||||||
for i, key := range item.Keys[:len(keys)] {
|
for i, key := range item.Keys[:len(keys)] {
|
||||||
if key.Token.Text != keys[i] {
|
key := key.Token.Text
|
||||||
|
if key != keys[i] && !strings.EqualFold(key, keys[i]) {
|
||||||
match = false
|
match = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user