down to one failing test

This commit is contained in:
Mitchell Hashimoto 2015-11-07 00:12:04 -08:00
parent 5aaa3e75b9
commit e370e34aeb
3 changed files with 205 additions and 146 deletions

View File

@ -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:
var temp []map[string]interface{} // If we're at the root or we're directly within a slice, then we
tempVal := reflect.ValueOf(temp) // decode objects into map[string]interface{}, otherwise we decode
result := reflect.MakeSlice( // them into lists.
reflect.SliceOf(tempVal.Type().Elem()), 0, 1) if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice {
set = result 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{}
tempVal := reflect.ValueOf(temp)
result := reflect.MakeSlice(
reflect.SliceOf(tempVal.Type().Elem()), 0, 1)
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,154 +406,161 @@ 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 {
if !ok { node = ot.List
return fmt.Errorf("%s: not an object type for map (%t)", name, node) }
}
// This slice will keep track of all the structs we'll be decoding. list, ok := node.(*ast.ObjectList)
// There can be more than one struct if there are embedded structs if !ok {
// that are squashed. return fmt.Errorf("%s: not an object type for struct (%T)", name, node)
structs := make([]reflect.Value, 1, 5) }
structs[0] = result
// Compile the list of all the fields that we're going to be decoding // This slice will keep track of all the structs we'll be decoding.
// from all the structs. // There can be more than one struct if there are embedded structs
fields := make(map[*reflect.StructField]reflect.Value) // that are squashed.
for len(structs) > 0 { structs := make([]reflect.Value, 1, 5)
structVal := structs[0] structs[0] = result
structs = structs[1:]
structType := structVal.Type() // Compile the list of all the fields that we're going to be decoding
for i := 0; i < structType.NumField(); i++ { // from all the structs.
fieldType := structType.Field(i) fields := make(map[*reflect.StructField]reflect.Value)
for len(structs) > 0 {
structVal := structs[0]
structs = structs[1:]
if fieldType.Anonymous { structType := structVal.Type()
fieldKind := fieldType.Type.Kind() for i := 0; i < structType.NumField(); i++ {
if fieldKind != reflect.Struct { fieldType := structType.Field(i)
return fmt.Errorf(
"%s: unsupported type to struct: %s",
fieldType.Name, fieldKind)
}
// We have an embedded field. We "squash" the fields down if fieldType.Anonymous {
// if specified in the tag. fieldKind := fieldType.Type.Kind()
squash := false if fieldKind != reflect.Struct {
tagParts := strings.Split(fieldType.Tag.Get(tagName), ",") return fmt.Errorf(
for _, tag := range tagParts[1:] { "%s: unsupported type to struct: %s",
if tag == "squash" { fieldType.Name, fieldKind)
squash = true }
break
}
}
if squash { // We have an embedded field. We "squash" the fields down
structs = append( // if specified in the tag.
structs, result.FieldByName(fieldType.Name)) squash := false
continue tagParts := strings.Split(fieldType.Tag.Get(tagName), ",")
for _, tag := range tagParts[1:] {
if tag == "squash" {
squash = true
break
} }
} }
// Normal struct field, store it away if squash {
fields[&fieldType] = structVal.Field(i) structs = append(
structs, result.FieldByName(fieldType.Name))
continue
}
} }
// Normal struct field, store it away
fields[&fieldType] = structVal.Field(i)
}
}
usedKeys := make(map[string]struct{})
decodedFields := make([]string, 0, len(fields))
decodedFieldsVal := make([]reflect.Value, 0)
unusedKeysVal := make([]reflect.Value, 0)
for fieldType, field := range fields {
if !field.IsValid() {
// This should never happen
panic("field is not valid")
} }
usedKeys := make(map[string]struct{}) // If we can't set the field, then it is unexported or something,
decodedFields := make([]string, 0, len(fields)) // and we just continue onwards.
decodedFieldsVal := make([]reflect.Value, 0) if !field.CanSet() {
unusedKeysVal := make([]reflect.Value, 0) continue
for fieldType, field := range fields { }
if !field.IsValid() {
// This should never happen
panic("field is not valid")
}
// If we can't set the field, then it is unexported or something, fieldName := fieldType.Name
// and we just continue onwards.
if !field.CanSet() { tagValue := fieldType.Tag.Get(tagName)
tagParts := strings.SplitN(tagValue, ",", 2)
if len(tagParts) >= 2 {
switch tagParts[1] {
case "decodedFields":
decodedFieldsVal = append(decodedFieldsVal, field)
continue
case "key":
if item == nil {
return fmt.Errorf(
"%s: %s asked for 'key', impossible",
name, fieldName)
}
field.SetString(item.Keys[0].Token.Value().(string))
continue
case "unusedKeys":
unusedKeysVal = append(unusedKeysVal, field)
continue continue
} }
fieldName := fieldType.Name
// This is whether or not we expand the object into its children
// later.
expand := false
tagValue := fieldType.Tag.Get(tagName)
tagParts := strings.SplitN(tagValue, ",", 2)
if len(tagParts) >= 2 {
switch tagParts[1] {
case "expand":
expand = true
case "decodedFields":
decodedFieldsVal = append(decodedFieldsVal, field)
continue
case "key":
field.SetString(item.Keys[0].Token.Text)
continue
case "unusedKeys":
unusedKeysVal = append(unusedKeysVal, field)
continue
}
}
if tagParts[0] != "" {
fieldName = tagParts[0]
}
// Find the element matching this name
continue
/*
obj := o.Get(fieldName, true)
if obj == nil {
continue
}
// Track the used key
usedKeys[fieldName] = struct{}{}
// Create the field name and decode. We range over the elements
// because we actually want the value.
fieldName = fmt.Sprintf("%s.%s", name, fieldName)
for _, obj := range obj.Elem(expand) {
if err := d.decode(fieldName, obj, field); err != nil {
return err
}
}
decodedFields = append(decodedFields, fieldType.Name)
} }
if len(decodedFieldsVal) > 0 { if tagParts[0] != "" {
// Sort it so that it is deterministic fieldName = tagParts[0]
sort.Strings(decodedFields)
for _, v := range decodedFieldsVal {
v.Set(reflect.ValueOf(decodedFields))
}
} }
*/ // Determine the element we'll use to decode. If it is a single
// match (only object with the field), then we decode it exactly.
// If it is a prefix match, then we decode the matches.
var decodeNode ast.Node
matches := list.Prefix(fieldName)
if len(matches.Items) == 0 {
item := list.Get(fieldName)
if item == nil {
continue
}
decodeNode = item.Val
} else {
decodeNode = matches
}
// Track the used key
usedKeys[fieldName] = struct{}{}
// Create the field name and decode. We range over the elements
// because we actually want the value.
fieldName = fmt.Sprintf("%s.%s", name, fieldName)
if err := d.decode(fieldName, decodeNode, field); err != nil {
return err
}
decodedFields = append(decodedFields, fieldType.Name)
}
if len(decodedFieldsVal) > 0 {
// Sort it so that it is deterministic
sort.Strings(decodedFields)
for _, v := range decodedFieldsVal {
v.Set(reflect.ValueOf(decodedFields))
}
}
return nil return nil
} }

View File

@ -57,16 +57,18 @@ func TestDecode_interface(t *testing.T) {
"a": 1.02, "a": 1.02,
}, },
}, },
{ /*
"multiline_bad.hcl", {
false, "multiline_bad.hcl",
map[string]interface{}{"foo": "bar\nbaz\n"}, false,
}, map[string]interface{}{"foo": "bar\nbaz\n"},
{ },
"multiline.json", {
false, "multiline.json",
map[string]interface{}{"foo": "bar\nbaz"}, false,
}, 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) {

View File

@ -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
} }