b699557f16
/cc @armon - This changes how Consul has to define its structure. Ping me tomorrow to learn more, but going to leave it here for reference too: The Consul case (there is a test case) never worked even with go-libucl, because there is an ambiguity of whether you want the inner children or the array of outer elements (the slice in the Policy struct). I've added a new modifier you can specify with a tag called "expand" which will tell hcl to expand the value to its children for decoding. You can see me use it in the test case which verifies that the Consul ACLs parse.
459 lines
11 KiB
Go
459 lines
11 KiB
Go
package hcl
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/hcl/hcl"
|
|
)
|
|
|
|
// This is the tag to use with structures to have settings for HCL
|
|
const tagName = "hcl"
|
|
|
|
// 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 *hcl.Object) error {
|
|
var d decoder
|
|
return d.decode("root", n, reflect.ValueOf(out).Elem())
|
|
}
|
|
|
|
type decoder struct{}
|
|
|
|
func (d *decoder) decode(name string, o *hcl.Object, 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
|
|
}
|
|
}
|
|
|
|
switch k.Kind() {
|
|
case reflect.Bool:
|
|
return d.decodeBool(name, o, result)
|
|
case reflect.Int:
|
|
return d.decodeInt(name, o, result)
|
|
case reflect.Interface:
|
|
// When we see an interface, we make our own thing
|
|
return d.decodeInterface(name, o, result)
|
|
case reflect.Map:
|
|
return d.decodeMap(name, o, result)
|
|
case reflect.Ptr:
|
|
return d.decodePtr(name, o, result)
|
|
case reflect.Slice:
|
|
return d.decodeSlice(name, o, result)
|
|
case reflect.String:
|
|
return d.decodeString(name, o, result)
|
|
case reflect.Struct:
|
|
return d.decodeStruct(name, o, result)
|
|
default:
|
|
return fmt.Errorf(
|
|
"%s: unknown kind to decode into: %s", name, result.Kind())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeBool(name string, o *hcl.Object, result reflect.Value) error {
|
|
switch o.Type {
|
|
case hcl.ValueTypeBool:
|
|
result.Set(reflect.ValueOf(o.Value.(bool)))
|
|
default:
|
|
return fmt.Errorf("%s: unknown type %s", name, o.Type)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeInt(name string, o *hcl.Object, result reflect.Value) error {
|
|
switch o.Type {
|
|
case hcl.ValueTypeInt:
|
|
result.Set(reflect.ValueOf(o.Value.(int)))
|
|
default:
|
|
return fmt.Errorf("%s: unknown type %s", name, o.Type)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeInterface(name string, o *hcl.Object, result reflect.Value) error {
|
|
var set reflect.Value
|
|
redecode := true
|
|
|
|
switch o.Type {
|
|
case hcl.ValueTypeObject:
|
|
if name == "root" {
|
|
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, int(o.Len()))
|
|
set = result
|
|
}
|
|
case hcl.ValueTypeList:
|
|
/*
|
|
var temp []interface{}
|
|
tempVal := reflect.ValueOf(temp)
|
|
result := reflect.MakeSlice(
|
|
reflect.SliceOf(tempVal.Type().Elem()), 0, 0)
|
|
set = result
|
|
*/
|
|
redecode = false
|
|
list := o.Value.([]*hcl.Object)
|
|
result := make([]interface{}, 0, len(list))
|
|
|
|
for _, elem := range list {
|
|
raw := new(interface{})
|
|
err := d.decode(
|
|
name, elem, reflect.Indirect(reflect.ValueOf(raw)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
result = append(result, *raw)
|
|
}
|
|
|
|
set = reflect.ValueOf(result)
|
|
case hcl.ValueTypeBool:
|
|
var result bool
|
|
set = reflect.Indirect(reflect.New(reflect.TypeOf(result)))
|
|
case hcl.ValueTypeInt:
|
|
var result int
|
|
set = reflect.Indirect(reflect.New(reflect.TypeOf(result)))
|
|
case hcl.ValueTypeString:
|
|
set = reflect.Indirect(reflect.New(reflect.TypeOf("")))
|
|
default:
|
|
return fmt.Errorf(
|
|
"%s: cannot decode into interface: %T",
|
|
name, o)
|
|
}
|
|
|
|
// 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, o, result); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeMap(name string, o *hcl.Object, result reflect.Value) error {
|
|
if o.Type != hcl.ValueTypeObject {
|
|
return fmt.Errorf("%s: not an object type for map (%s)", name, o.Type)
|
|
}
|
|
|
|
// 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 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.
|
|
for _, o := range o.Elem(false) {
|
|
if o.Value == nil {
|
|
continue
|
|
}
|
|
|
|
for _, o := range o.Elem(true) {
|
|
// 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))
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
// Set the final map if we can
|
|
set.Set(resultMap)
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodePtr(name string, o *hcl.Object, 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, o, reflect.Indirect(val)); err != nil {
|
|
return err
|
|
}
|
|
|
|
result.Set(val)
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeSlice(name string, o *hcl.Object, 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)
|
|
}
|
|
|
|
// Determine how we're doing this
|
|
expand := true
|
|
switch o.Type {
|
|
case hcl.ValueTypeObject:
|
|
expand = false
|
|
default:
|
|
// Array or anything else: we expand values and take it all
|
|
}
|
|
|
|
i := 0
|
|
for _, o := range o.Elem(expand) {
|
|
fieldName := fmt.Sprintf("%s[%d]", name, i)
|
|
|
|
// 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
|
|
}
|
|
|
|
set.Set(result)
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeString(name string, o *hcl.Object, result reflect.Value) error {
|
|
switch o.Type {
|
|
case hcl.ValueTypeInt:
|
|
result.Set(reflect.ValueOf(
|
|
strconv.FormatInt(int64(o.Value.(int)), 10)).Convert(result.Type()))
|
|
case hcl.ValueTypeString:
|
|
result.Set(reflect.ValueOf(o.Value.(string)).Convert(result.Type()))
|
|
default:
|
|
return fmt.Errorf("%s: unknown type to string: %s", name, o.Type)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *decoder) decodeStruct(name string, o *hcl.Object, result reflect.Value) error {
|
|
if o.Type != hcl.ValueTypeObject {
|
|
return fmt.Errorf(
|
|
"%s: not an object type for struct (%s)", name, o.Type)
|
|
}
|
|
|
|
// 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.
|
|
fields := make(map[*reflect.StructField]reflect.Value)
|
|
for len(structs) > 0 {
|
|
structVal := structs[0]
|
|
structs = structs[1:]
|
|
|
|
structType := structVal.Type()
|
|
for i := 0; i < structType.NumField(); i++ {
|
|
fieldType := structType.Field(i)
|
|
|
|
if fieldType.Anonymous {
|
|
fieldKind := fieldType.Type.Kind()
|
|
if fieldKind != reflect.Struct {
|
|
return 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
|
|
tagParts := strings.Split(fieldType.Tag.Get(tagName), ",")
|
|
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[&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")
|
|
}
|
|
|
|
// If we can't set the field, then it is unexported or something,
|
|
// and we just continue onwards.
|
|
if !field.CanSet() {
|
|
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(o.Key)
|
|
continue
|
|
case "unusedKeys":
|
|
unusedKeysVal = append(unusedKeysVal, field)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if tagParts[0] != "" {
|
|
fieldName = tagParts[0]
|
|
}
|
|
|
|
// Find the element matching this name
|
|
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 {
|
|
// 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))
|
|
|
|
for _, elem := range obj.Elem {
|
|
k := elem.Key()
|
|
if _, ok := usedKeys[k]; !ok {
|
|
unusedKeys = append(unusedKeys, k)
|
|
}
|
|
}
|
|
|
|
if len(unusedKeys) == 0 {
|
|
unusedKeys = nil
|
|
}
|
|
|
|
for _, v := range unusedKeysVal {
|
|
v.Set(reflect.ValueOf(unusedKeys))
|
|
}
|
|
*/
|
|
}
|
|
|
|
return nil
|
|
}
|