mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-06 09:06:44 +00:00
![Paddy](/assets/img/avatar_default.png)
Switch to using Go modules. This migrates our vendor.json to use Go 1.11's modules system, and replaces the vendor folder with the output of go mod vendor. The vendored code should remain basically the same; I believe some tree shaking of packages and support scripts/licenses/READMEs/etc. happened. This also fixes Travis and our Makefile to no longer use govendor.
730 lines
18 KiB
Go
730 lines
18 KiB
Go
package hcl
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/hcl/hcl/ast"
|
|
"github.com/hashicorp/hcl/hcl/parser"
|
|
"github.com/hashicorp/hcl/hcl/token"
|
|
)
|
|
|
|
// This is the tag to use with structures to have settings for HCL
|
|
const tagName = "hcl"
|
|
|
|
var (
|
|
// nodeType holds a reference to the type of ast.Node
|
|
nodeType reflect.Type = findNodeType()
|
|
)
|
|
|
|
// Unmarshal accepts a byte slice as input and writes the
|
|
// data to the value pointed to by v.
|
|
func Unmarshal(bs []byte, v interface{}) error {
|
|
root, err := parse(bs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return DecodeObject(v, root)
|
|
}
|
|
|
|
// Decode reads the given input and decodes it into the structure
|
|
// given by `out`.
|
|
func Decode(out interface{}, in string) error {
|
|
obj, err := Parse(in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return DecodeObject(out, obj)
|
|
}
|
|
|
|
// DecodeObject is a lower-level version of Decode. It decodes a
|
|
// raw Object into the given output.
|
|
func DecodeObject(out interface{}, n ast.Node) error {
|
|
val := reflect.ValueOf(out)
|
|
if val.Kind() != reflect.Ptr {
|
|
return errors.New("result must be a pointer")
|
|
}
|
|
|
|
// If we have the file, we really decode the root node
|
|
if f, ok := n.(*ast.File); ok {
|
|
n = f.Node
|
|
}
|
|
|
|
var d decoder
|
|
return d.decode("root", n, val.Elem())
|
|
}
|
|
|
|
type decoder struct {
|
|
stack []reflect.Kind
|
|
}
|
|
|
|
func (d *decoder) decode(name string, node ast.Node, result reflect.Value) error {
|
|
k := result
|
|
|
|
// If we have an interface with a valid value, we use that
|
|
// for the check.
|
|
if result.Kind() == reflect.Interface {
|
|
elem := result.Elem()
|
|
if elem.IsValid() {
|
|
k = elem
|
|
}
|
|
}
|
|
|
|
// 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:
|
|
return d.decodeBool(name, node, result)
|
|
case reflect.Float32, reflect.Float64:
|
|
return d.decodeFloat(name, node, result)
|
|
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
return d.decodeInt(name, node, result)
|
|
case reflect.Interface:
|
|
// When we see an interface, we make our own thing
|
|
return d.decodeInterface(name, node, result)
|
|
case reflect.Map:
|
|
return d.decodeMap(name, node, result)
|
|
case reflect.Ptr:
|
|
return d.decodePtr(name, node, result)
|
|
case reflect.Slice:
|
|
return d.decodeSlice(name, node, result)
|
|
case reflect.String:
|
|
return d.decodeString(name, node, result)
|
|
case reflect.Struct:
|
|
return d.decodeStruct(name, node, result)
|
|
default:
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: unknown kind to decode into: %s", name, k.Kind()),
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *decoder) decodeBool(name string, node ast.Node, result reflect.Value) error {
|
|
switch n := node.(type) {
|
|
case *ast.LiteralType:
|
|
if n.Token.Type == token.BOOL {
|
|
v, err := strconv.ParseBool(n.Token.Text)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
result.Set(reflect.ValueOf(v))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: unknown type %T", name, node),
|
|
}
|
|
}
|
|
|
|
func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value) error {
|
|
switch n := node.(type) {
|
|
case *ast.LiteralType:
|
|
if n.Token.Type == token.FLOAT || n.Token.Type == token.NUMBER {
|
|
v, err := strconv.ParseFloat(n.Token.Text, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
result.Set(reflect.ValueOf(v).Convert(result.Type()))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: unknown type %T", name, node),
|
|
}
|
|
}
|
|
|
|
func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) error {
|
|
switch n := node.(type) {
|
|
case *ast.LiteralType:
|
|
switch n.Token.Type {
|
|
case token.NUMBER:
|
|
v, err := strconv.ParseInt(n.Token.Text, 0, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if result.Kind() == reflect.Interface {
|
|
result.Set(reflect.ValueOf(int(v)))
|
|
} else {
|
|
result.SetInt(v)
|
|
}
|
|
return nil
|
|
case token.STRING:
|
|
v, err := strconv.ParseInt(n.Token.Value().(string), 0, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if result.Kind() == reflect.Interface {
|
|
result.Set(reflect.ValueOf(int(v)))
|
|
} else {
|
|
result.SetInt(v)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: unknown type %T", name, node),
|
|
}
|
|
}
|
|
|
|
func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Value) error {
|
|
// When we see an ast.Node, we retain the value to enable deferred decoding.
|
|
// Very useful in situations where we want to preserve ast.Node information
|
|
// like Pos
|
|
if result.Type() == nodeType && result.CanSet() {
|
|
result.Set(reflect.ValueOf(node))
|
|
return nil
|
|
}
|
|
|
|
var set reflect.Value
|
|
redecode := true
|
|
|
|
// 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:
|
|
// 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{}
|
|
tempVal := reflect.ValueOf(temp)
|
|
result := reflect.MakeSlice(
|
|
reflect.SliceOf(tempVal.Type().Elem()), 0, len(n.Items))
|
|
set = result
|
|
}
|
|
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{}
|
|
tempVal := reflect.ValueOf(temp)
|
|
result := reflect.MakeSlice(
|
|
reflect.SliceOf(tempVal.Type().Elem()), 0, 1)
|
|
set = result
|
|
}
|
|
case *ast.ListType:
|
|
var temp []interface{}
|
|
tempVal := reflect.ValueOf(temp)
|
|
result := reflect.MakeSlice(
|
|
reflect.SliceOf(tempVal.Type().Elem()), 0, 0)
|
|
set = result
|
|
case *ast.LiteralType:
|
|
switch n.Token.Type {
|
|
case token.BOOL:
|
|
var result bool
|
|
set = reflect.Indirect(reflect.New(reflect.TypeOf(result)))
|
|
case token.FLOAT:
|
|
var result float64
|
|
set = reflect.Indirect(reflect.New(reflect.TypeOf(result)))
|
|
case token.NUMBER:
|
|
var result int
|
|
set = reflect.Indirect(reflect.New(reflect.TypeOf(result)))
|
|
case token.STRING, token.HEREDOC:
|
|
set = reflect.Indirect(reflect.New(reflect.TypeOf("")))
|
|
default:
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: cannot decode into interface: %T", name, node),
|
|
}
|
|
}
|
|
default:
|
|
return fmt.Errorf(
|
|
"%s: cannot decode into interface: %T",
|
|
name, node)
|
|
}
|
|
|
|
// Set the result to what its supposed to be, then reset
|
|
// result so we don't reflect into this method anymore.
|
|
result.Set(set)
|
|
|
|
if redecode {
|
|
// Revisit the node so that we can use the newly instantiated
|
|
// thing and populate it.
|
|
if err := d.decode(name, node, result); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeMap(name string, node ast.Node, result reflect.Value) error {
|
|
if item, ok := node.(*ast.ObjectItem); ok {
|
|
node = &ast.ObjectList{Items: []*ast.ObjectItem{item}}
|
|
}
|
|
|
|
if ot, ok := node.(*ast.ObjectType); ok {
|
|
node = ot.List
|
|
}
|
|
|
|
n, ok := node.(*ast.ObjectList)
|
|
if !ok {
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: not an object type for map (%T)", name, node),
|
|
}
|
|
}
|
|
|
|
// If we have an interface, then we can address the interface,
|
|
// but not the slice itself, so get the element but set the interface
|
|
set := result
|
|
if result.Kind() == reflect.Interface {
|
|
result = result.Elem()
|
|
}
|
|
|
|
resultType := result.Type()
|
|
resultElemType := resultType.Elem()
|
|
resultKeyType := resultType.Key()
|
|
if resultKeyType.Kind() != reflect.String {
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: map must have string keys", name),
|
|
}
|
|
}
|
|
|
|
// Make a map if it is nil
|
|
resultMap := result
|
|
if result.IsNil() {
|
|
resultMap = reflect.MakeMap(
|
|
reflect.MapOf(resultKeyType, resultElemType))
|
|
}
|
|
|
|
// Go through each element and decode it.
|
|
done := make(map[string]struct{})
|
|
for _, item := range n.Items {
|
|
if item.Val == nil {
|
|
continue
|
|
}
|
|
|
|
// github.com/hashicorp/terraform/issue/5740
|
|
if len(item.Keys) == 0 {
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: map must have string keys", name),
|
|
}
|
|
}
|
|
|
|
// Get the key we're dealing with, which is the first item
|
|
keyStr := item.Keys[0].Token.Value().(string)
|
|
|
|
// If we've already processed this key, then ignore it
|
|
if _, ok := done[keyStr]; ok {
|
|
continue
|
|
}
|
|
|
|
// Determine the value. If we have more than one key, then we
|
|
// get the objectlist of only these keys.
|
|
itemVal := item.Val
|
|
if len(item.Keys) > 1 {
|
|
itemVal = n.Filter(keyStr)
|
|
done[keyStr] = struct{}{}
|
|
}
|
|
|
|
// Make the field name
|
|
fieldName := fmt.Sprintf("%s.%s", name, keyStr)
|
|
|
|
// Get the key/value as reflection values
|
|
key := reflect.ValueOf(keyStr)
|
|
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)
|
|
}
|
|
|
|
// Decode!
|
|
if err := d.decode(fieldName, itemVal, val); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set the value on the map
|
|
resultMap.SetMapIndex(key, val)
|
|
}
|
|
|
|
// Set the final map if we can
|
|
set.Set(resultMap)
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodePtr(name string, node ast.Node, result reflect.Value) error {
|
|
// Create an element of the concrete (non pointer) type and decode
|
|
// into that. Then set the value of the pointer to this type.
|
|
resultType := result.Type()
|
|
resultElemType := resultType.Elem()
|
|
val := reflect.New(resultElemType)
|
|
if err := d.decode(name, node, reflect.Indirect(val)); err != nil {
|
|
return err
|
|
}
|
|
|
|
result.Set(val)
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeSlice(name string, node ast.Node, result reflect.Value) error {
|
|
// If we have an interface, then we can address the interface,
|
|
// but not the slice itself, so get the element but set the interface
|
|
set := result
|
|
if result.Kind() == reflect.Interface {
|
|
result = result.Elem()
|
|
}
|
|
// Create the slice if it isn't nil
|
|
resultType := result.Type()
|
|
resultElemType := resultType.Elem()
|
|
if result.IsNil() {
|
|
resultSliceType := reflect.SliceOf(resultElemType)
|
|
result = reflect.MakeSlice(
|
|
resultSliceType, 0, 0)
|
|
}
|
|
|
|
// Figure out the items we'll be copying into the slice
|
|
var items []ast.Node
|
|
switch n := node.(type) {
|
|
case *ast.ObjectList:
|
|
items = make([]ast.Node, len(n.Items))
|
|
for i, item := range n.Items {
|
|
items[i] = item
|
|
}
|
|
case *ast.ObjectType:
|
|
items = []ast.Node{n}
|
|
case *ast.ListType:
|
|
items = n.List
|
|
default:
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("unknown slice type: %T", node),
|
|
}
|
|
}
|
|
|
|
for i, item := range items {
|
|
fieldName := fmt.Sprintf("%s[%d]", name, i)
|
|
|
|
// Decode
|
|
val := reflect.Indirect(reflect.New(resultElemType))
|
|
|
|
// if item is an object that was decoded from ambiguous JSON and
|
|
// flattened, make sure it's expanded if it needs to decode into a
|
|
// defined structure.
|
|
item := expandObject(item, val)
|
|
|
|
if err := d.decode(fieldName, item, val); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Append it onto the slice
|
|
result = reflect.Append(result, val)
|
|
}
|
|
|
|
set.Set(result)
|
|
return nil
|
|
}
|
|
|
|
// expandObject detects if an ambiguous JSON object was flattened to a List which
|
|
// should be decoded into a struct, and expands the ast to properly deocode.
|
|
func expandObject(node ast.Node, result reflect.Value) ast.Node {
|
|
item, ok := node.(*ast.ObjectItem)
|
|
if !ok {
|
|
return node
|
|
}
|
|
|
|
elemType := result.Type()
|
|
|
|
// our target type must be a struct
|
|
switch elemType.Kind() {
|
|
case reflect.Ptr:
|
|
switch elemType.Elem().Kind() {
|
|
case reflect.Struct:
|
|
//OK
|
|
default:
|
|
return node
|
|
}
|
|
case reflect.Struct:
|
|
//OK
|
|
default:
|
|
return node
|
|
}
|
|
|
|
// A list value will have a key and field name. If it had more fields,
|
|
// it wouldn't have been flattened.
|
|
if len(item.Keys) != 2 {
|
|
return node
|
|
}
|
|
|
|
keyToken := item.Keys[0].Token
|
|
item.Keys = item.Keys[1:]
|
|
|
|
// we need to un-flatten the ast enough to decode
|
|
newNode := &ast.ObjectItem{
|
|
Keys: []*ast.ObjectKey{
|
|
&ast.ObjectKey{
|
|
Token: keyToken,
|
|
},
|
|
},
|
|
Val: &ast.ObjectType{
|
|
List: &ast.ObjectList{
|
|
Items: []*ast.ObjectItem{item},
|
|
},
|
|
},
|
|
}
|
|
|
|
return newNode
|
|
}
|
|
|
|
func (d *decoder) decodeString(name string, node ast.Node, result reflect.Value) error {
|
|
switch n := node.(type) {
|
|
case *ast.LiteralType:
|
|
switch n.Token.Type {
|
|
case token.NUMBER:
|
|
result.Set(reflect.ValueOf(n.Token.Text).Convert(result.Type()))
|
|
return nil
|
|
case token.STRING, token.HEREDOC:
|
|
result.Set(reflect.ValueOf(n.Token.Value()).Convert(result.Type()))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: unknown type for string %T", name, node),
|
|
}
|
|
}
|
|
|
|
func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) error {
|
|
var item *ast.ObjectItem
|
|
if it, ok := node.(*ast.ObjectItem); ok {
|
|
item = it
|
|
node = it.Val
|
|
}
|
|
|
|
if ot, ok := node.(*ast.ObjectType); ok {
|
|
node = ot.List
|
|
}
|
|
|
|
// Handle the special case where the object itself is a literal. Previously
|
|
// the yacc parser would always ensure top-level elements were arrays. The new
|
|
// parser does not make the same guarantees, thus we need to convert any
|
|
// top-level literal elements into a list.
|
|
if _, ok := node.(*ast.LiteralType); ok && item != nil {
|
|
node = &ast.ObjectList{Items: []*ast.ObjectItem{item}}
|
|
}
|
|
|
|
list, ok := node.(*ast.ObjectList)
|
|
if !ok {
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: 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.
|
|
// There can be more than one struct if there are embedded structs
|
|
// that are squashed.
|
|
structs := make([]reflect.Value, 1, 5)
|
|
structs[0] = result
|
|
|
|
// Compile the list of all the fields that we're going to be decoding
|
|
// from all the structs.
|
|
type field struct {
|
|
field reflect.StructField
|
|
val reflect.Value
|
|
}
|
|
fields := []field{}
|
|
for len(structs) > 0 {
|
|
structVal := structs[0]
|
|
structs = structs[1:]
|
|
|
|
structType := structVal.Type()
|
|
for i := 0; i < structType.NumField(); i++ {
|
|
fieldType := structType.Field(i)
|
|
tagParts := strings.Split(fieldType.Tag.Get(tagName), ",")
|
|
|
|
// Ignore fields with tag name "-"
|
|
if tagParts[0] == "-" {
|
|
continue
|
|
}
|
|
|
|
if fieldType.Anonymous {
|
|
fieldKind := fieldType.Type.Kind()
|
|
if fieldKind != reflect.Struct {
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: unsupported type to struct: %s",
|
|
fieldType.Name, fieldKind),
|
|
}
|
|
}
|
|
|
|
// We have an embedded field. We "squash" the fields down
|
|
// if specified in the tag.
|
|
squash := false
|
|
for _, tag := range tagParts[1:] {
|
|
if tag == "squash" {
|
|
squash = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if squash {
|
|
structs = append(
|
|
structs, result.FieldByName(fieldType.Name))
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Normal struct field, store it away
|
|
fields = append(fields, field{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 _, f := range fields {
|
|
field, fieldValue := f.field, f.val
|
|
if !fieldValue.IsValid() {
|
|
// This should never happen
|
|
panic("field is not valid")
|
|
}
|
|
|
|
// If we can't set the field, then it is unexported or something,
|
|
// and we just continue onwards.
|
|
if !fieldValue.CanSet() {
|
|
continue
|
|
}
|
|
|
|
fieldName := field.Name
|
|
|
|
tagValue := field.Tag.Get(tagName)
|
|
tagParts := strings.SplitN(tagValue, ",", 2)
|
|
if len(tagParts) >= 2 {
|
|
switch tagParts[1] {
|
|
case "decodedFields":
|
|
decodedFieldsVal = append(decodedFieldsVal, fieldValue)
|
|
continue
|
|
case "key":
|
|
if item == nil {
|
|
return &parser.PosError{
|
|
Pos: node.Pos(),
|
|
Err: fmt.Errorf("%s: %s asked for 'key', impossible",
|
|
name, fieldName),
|
|
}
|
|
}
|
|
|
|
fieldValue.SetString(item.Keys[0].Token.Value().(string))
|
|
continue
|
|
case "unusedKeys":
|
|
unusedKeysVal = append(unusedKeysVal, fieldValue)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if tagParts[0] != "" {
|
|
fieldName = tagParts[0]
|
|
}
|
|
|
|
// 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.
|
|
filter := list.Filter(fieldName)
|
|
|
|
prefixMatches := filter.Children()
|
|
matches := filter.Elem()
|
|
if len(matches.Items) == 0 && len(prefixMatches.Items) == 0 {
|
|
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)
|
|
if len(prefixMatches.Items) > 0 {
|
|
if err := d.decode(fieldName, prefixMatches, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, match := range matches.Items {
|
|
var decodeNode ast.Node = match.Val
|
|
if ot, ok := decodeNode.(*ast.ObjectType); ok {
|
|
decodeNode = &ast.ObjectList{Items: ot.List.Items}
|
|
}
|
|
|
|
if err := d.decode(fieldName, decodeNode, fieldValue); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
decodedFields = append(decodedFields, field.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
|
|
}
|
|
|
|
// findNodeType returns the type of ast.Node
|
|
func findNodeType() reflect.Type {
|
|
var nodeContainer struct {
|
|
Node ast.Node
|
|
}
|
|
value := reflect.ValueOf(nodeContainer).FieldByName("Node")
|
|
return value.Type()
|
|
}
|