hcl/decoder.go

421 lines
10 KiB
Go
Raw Normal View History

2014-08-02 22:44:45 +00:00
package hcl
import (
"fmt"
"reflect"
"strconv"
2014-08-03 21:06:18 +00:00
"strings"
2014-08-02 22:44:45 +00:00
2014-08-11 23:33:28 +00:00
"github.com/hashicorp/hcl/hcl"
2014-08-02 22:44:45 +00:00
)
2014-08-03 21:06:18 +00:00
// This is the tag to use with structures to have settings for HCL
const tagName = "hcl"
2014-08-02 22:44:45 +00:00
// 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
}
2014-08-11 23:33:28 +00:00
return DecodeObject(out, obj)
2014-08-02 22:44:45 +00:00
}
2014-08-11 23:33:28 +00:00
// 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 {
2014-08-03 05:37:58 +00:00
var d decoder
2014-08-03 20:19:20 +00:00
return d.decode("root", n, reflect.ValueOf(out).Elem())
2014-08-02 22:44:45 +00:00
}
2014-08-03 05:37:58 +00:00
type decoder struct{}
2014-08-11 23:33:28 +00:00
func (d *decoder) decode(name string, o *hcl.Object, result reflect.Value) error {
2014-08-03 05:05:21 +00:00
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() {
2014-08-05 05:25:59 +00:00
case reflect.Bool:
2014-08-11 23:33:28 +00:00
return d.decodeBool(name, o, result)
2014-08-02 23:07:54 +00:00
case reflect.Int:
2014-08-11 23:33:28 +00:00
return d.decodeInt(name, o, result)
2014-08-02 22:44:45 +00:00
case reflect.Interface:
// When we see an interface, we make our own thing
2014-08-11 23:33:28 +00:00
return d.decodeInterface(name, o, result)
2014-08-02 22:44:45 +00:00
case reflect.Map:
2014-08-11 23:33:28 +00:00
return d.decodeMap(name, o, result)
2014-08-04 00:17:17 +00:00
case reflect.Ptr:
2014-08-11 23:33:28 +00:00
return d.decodePtr(name, o, result)
2014-08-02 23:07:54 +00:00
case reflect.Slice:
2014-08-11 23:33:28 +00:00
return d.decodeSlice(name, o, result)
2014-08-02 22:44:45 +00:00
case reflect.String:
2014-08-11 23:33:28 +00:00
return d.decodeString(name, o, result)
2014-08-03 21:06:18 +00:00
case reflect.Struct:
2014-08-11 23:33:28 +00:00
return d.decodeStruct(name, o, result)
2014-08-02 22:44:45 +00:00
default:
2014-08-03 21:06:18 +00:00
return fmt.Errorf(
"%s: unknown kind to decode into: %s", name, result.Kind())
2014-08-02 22:44:45 +00:00
}
return nil
}
2014-08-11 23:33:28 +00:00
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)))
2014-08-05 05:25:59 +00:00
default:
2014-08-11 23:33:28 +00:00
return fmt.Errorf("%s: unknown type %s", name, o.Type)
2014-08-05 05:25:59 +00:00
}
return nil
}
2014-08-11 23:33:28 +00:00
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)))
2014-08-02 23:07:54 +00:00
default:
2014-08-11 23:33:28 +00:00
return fmt.Errorf("%s: unknown type %s", name, o.Type)
2014-08-02 23:07:54 +00:00
}
return nil
}
2014-08-11 23:33:28 +00:00
func (d *decoder) decodeInterface(name string, o *hcl.Object, result reflect.Value) error {
2014-08-02 22:44:45 +00:00
var set reflect.Value
2014-08-02 23:07:54 +00:00
redecode := true
2014-08-02 22:44:45 +00:00
2014-08-11 23:33:28 +00:00
switch o.Type {
case hcl.ValueTypeObject:
var temp map[string]interface{}
tempVal := reflect.ValueOf(temp)
result := reflect.MakeMap(
reflect.MapOf(
reflect.TypeOf(""),
tempVal.Type().Elem()))
set = result
2014-08-11 23:33:28 +00:00
case hcl.ValueTypeList:
/*
var temp []interface{}
tempVal := reflect.ValueOf(temp)
result := reflect.MakeSlice(
reflect.SliceOf(tempVal.Type().Elem()), 0, 0)
set = result
*/
2014-08-02 23:07:54 +00:00
redecode = false
2014-08-11 23:33:28 +00:00
list := o.Value.([]*hcl.Object)
result := make([]interface{}, 0, len(list))
2014-08-02 23:07:54 +00:00
2014-08-11 23:33:28 +00:00
for _, elem := range list {
2014-08-02 23:07:54 +00:00
raw := new(interface{})
2014-08-03 05:37:58 +00:00
err := d.decode(
2014-08-02 23:07:54 +00:00
name, elem, reflect.Indirect(reflect.ValueOf(raw)))
if err != nil {
return err
}
result = append(result, *raw)
}
2014-08-02 22:44:45 +00:00
set = reflect.ValueOf(result)
2014-08-11 23:33:28 +00:00
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("")))
2014-08-02 22:44:45 +00:00
default:
return fmt.Errorf(
"%s: cannot decode into interface: %T",
2014-08-11 23:33:28 +00:00
name, o)
2014-08-02 22:44:45 +00:00
}
// Set the result to what its supposed to be, then reset
// result so we don't reflect into this method anymore.
result.Set(set)
2014-08-02 23:07:54 +00:00
if redecode {
// Revisit the node so that we can use the newly instantiated
// thing and populate it.
2014-08-11 23:33:28 +00:00
if err := d.decode(name, o, result); err != nil {
2014-08-02 23:07:54 +00:00
return err
}
2014-08-02 22:44:45 +00:00
}
return nil
}
2014-08-11 23:33:28 +00:00
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 (%s)", name, o.Type)
2014-08-02 22:44:45 +00:00
}
// 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()
}
2014-08-02 22:44:45 +00:00
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.
2014-08-11 23:38:36 +00:00
m := o.Value.(map[string]*hcl.Object)
for _, o := range m {
2014-08-02 22:44:45 +00:00
// Make the field name
2014-08-11 23:38:36 +00:00
fieldName := fmt.Sprintf("%s.%s", name, o.Key)
2014-08-02 22:44:45 +00:00
// Get the key/value as reflection values
2014-08-11 23:38:36 +00:00
key := reflect.ValueOf(o.Key)
2014-08-02 22:44:45 +00:00
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!
2014-08-11 23:38:36 +00:00
if err := d.decode(fieldName, o, val); err != nil {
2014-08-02 22:44:45 +00:00
return err
}
// Set the value on the map
resultMap.SetMapIndex(key, val)
}
// Set the final map if we can
set.Set(resultMap)
2014-08-02 22:44:45 +00:00
return nil
}
2014-08-11 23:33:28 +00:00
func (d *decoder) decodePtr(name string, o *hcl.Object, result reflect.Value) error {
2014-08-04 00:17:17 +00:00
// 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)
2014-08-11 23:33:28 +00:00
if err := d.decode(name, o, reflect.Indirect(val)); err != nil {
2014-08-04 00:17:17 +00:00
return err
}
result.Set(val)
return nil
}
2014-08-11 23:33:28 +00:00
func (d *decoder) decodeSlice(name string, o *hcl.Object, result reflect.Value) error {
2014-08-03 05:05:21 +00:00
// 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
2014-08-02 23:07:54 +00:00
resultType := result.Type()
resultElemType := resultType.Elem()
2014-08-03 05:05:21 +00:00
if result.IsNil() {
resultSliceType := reflect.SliceOf(resultElemType)
result = reflect.MakeSlice(
resultSliceType, 0, 0)
}
2014-08-11 23:33:28 +00:00
/*
2014-08-03 05:05:21 +00:00
for i, elem := range n.Elem {
fieldName := fmt.Sprintf("%s[%d]", name, i)
// Decode
val := reflect.Indirect(reflect.New(resultElemType))
2014-08-03 05:37:58 +00:00
if err := d.decode(fieldName, elem, val); err != nil {
2014-08-03 05:05:21 +00:00
return err
}
2014-08-02 23:07:54 +00:00
2014-08-03 05:05:21 +00:00
// Append it onto the slice
result = reflect.Append(result, val)
}
2014-08-11 23:33:28 +00:00
*/
2014-08-02 23:07:54 +00:00
2014-08-03 05:05:21 +00:00
set.Set(result)
2014-08-02 23:07:54 +00:00
return nil
}
2014-08-11 23:33:28 +00:00
func (d *decoder) decodeString(name string, o *hcl.Object, result reflect.Value) error {
switch o.Type {
case hcl.ValueTypeInt:
result.Set(reflect.ValueOf(
2014-08-11 23:33:28 +00:00
strconv.FormatInt(int64(o.Value.(int)), 10)).Convert(result.Type()))
case hcl.ValueTypeString:
result.Set(reflect.ValueOf(o.Value.(string)).Convert(result.Type()))
2014-08-02 22:44:45 +00:00
default:
2014-08-11 23:33:28 +00:00
return fmt.Errorf("%s: unknown type to string: %s", name, o.Type)
2014-08-02 22:44:45 +00:00
}
return nil
}
2014-08-03 21:06:18 +00:00
2014-08-11 23:33:28 +00:00
func (d *decoder) decodeStruct(name string, o *hcl.Object, result reflect.Value) error {
if o.Type != hcl.ValueTypeObject {
2014-08-04 00:24:37 +00:00
return fmt.Errorf(
2014-08-11 23:33:28 +00:00
"%s: not an object type for struct (%T)", name, o)
2014-08-03 21:06:18 +00:00
}
// 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)
}
}
2014-08-11 23:51:52 +00:00
usedKeys := make(map[string]struct{})
2014-08-03 21:06:18 +00:00
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
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":
2014-08-11 23:33:28 +00:00
field.SetString(o.Key)
2014-08-03 21:06:18 +00:00
continue
case "unusedKeys":
unusedKeysVal = append(unusedKeysVal, field)
continue
}
}
if tagParts[0] != "" {
fieldName = tagParts[0]
}
// Find the element matching this name
2014-08-11 23:51:52 +00:00
obj := o.Get(fieldName, true)
if obj == nil {
2014-08-04 00:57:54 +00:00
continue
2014-08-03 21:06:18 +00:00
}
// Track the used key
2014-08-04 00:57:54 +00:00
usedKeys[fieldName] = struct{}{}
2014-08-03 21:06:18 +00:00
// Create the field name and decode
fieldName = fmt.Sprintf("%s.%s", name, fieldName)
2014-08-11 23:51:52 +00:00
if err := d.decode(fieldName, obj, field); err != nil {
return err
2014-08-03 21:06:18 +00:00
}
decodedFields = append(decodedFields, fieldType.Name)
}
for _, v := range decodedFieldsVal {
v.Set(reflect.ValueOf(decodedFields))
}
// If we want to know what keys are unused, compile that
if len(unusedKeysVal) > 0 {
2014-08-11 23:33:28 +00:00
/*
2014-08-03 21:06:18 +00:00
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))
}
2014-08-11 23:33:28 +00:00
*/
2014-08-03 21:06:18 +00:00
}
return nil
}