b54c956c5e
Co-authored-by: Julien Salleyron <julien@containo.us>
306 lines
6.6 KiB
Go
306 lines
6.6 KiB
Go
package internal
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containous/flaeg/parse"
|
|
)
|
|
|
|
type initializer interface {
|
|
SetDefaults()
|
|
}
|
|
|
|
// Fill the fields of the element.
|
|
// nodes -> element
|
|
func Fill(element interface{}, node *Node) error {
|
|
if element == nil || node == nil {
|
|
return nil
|
|
}
|
|
|
|
if node.Kind == 0 {
|
|
return fmt.Errorf("missing node type: %s", node.Name)
|
|
}
|
|
|
|
elem := reflect.ValueOf(element)
|
|
if elem.Kind() == reflect.Struct {
|
|
return fmt.Errorf("struct are not supported, use pointer instead")
|
|
}
|
|
|
|
return fill(elem.Elem(), node)
|
|
}
|
|
|
|
func fill(field reflect.Value, node *Node) error {
|
|
// related to allow-empty tag
|
|
if node.Disabled {
|
|
return nil
|
|
}
|
|
|
|
switch field.Kind() {
|
|
case reflect.String:
|
|
field.SetString(node.Value)
|
|
return nil
|
|
case reflect.Bool:
|
|
val, err := strconv.ParseBool(node.Value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
field.SetBool(val)
|
|
return nil
|
|
case reflect.Int8:
|
|
return setInt(field, node.Value, 8)
|
|
case reflect.Int16:
|
|
return setInt(field, node.Value, 16)
|
|
case reflect.Int32:
|
|
return setInt(field, node.Value, 32)
|
|
case reflect.Int64, reflect.Int:
|
|
return setInt(field, node.Value, 64)
|
|
case reflect.Uint8:
|
|
return setUint(field, node.Value, 8)
|
|
case reflect.Uint16:
|
|
return setUint(field, node.Value, 16)
|
|
case reflect.Uint32:
|
|
return setUint(field, node.Value, 32)
|
|
case reflect.Uint64, reflect.Uint:
|
|
return setUint(field, node.Value, 64)
|
|
case reflect.Float32:
|
|
return setFloat(field, node.Value, 32)
|
|
case reflect.Float64:
|
|
return setFloat(field, node.Value, 64)
|
|
case reflect.Struct:
|
|
return setStruct(field, node)
|
|
case reflect.Ptr:
|
|
return setPtr(field, node)
|
|
case reflect.Map:
|
|
return setMap(field, node)
|
|
case reflect.Slice:
|
|
return setSlice(field, node)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func setPtr(field reflect.Value, node *Node) error {
|
|
if field.IsNil() {
|
|
field.Set(reflect.New(field.Type().Elem()))
|
|
|
|
if field.Type().Implements(reflect.TypeOf((*initializer)(nil)).Elem()) {
|
|
method := field.MethodByName("SetDefaults")
|
|
if method.IsValid() {
|
|
method.Call([]reflect.Value{})
|
|
}
|
|
}
|
|
}
|
|
|
|
return fill(field.Elem(), node)
|
|
}
|
|
|
|
func setStruct(field reflect.Value, node *Node) error {
|
|
for _, child := range node.Children {
|
|
fd := field.FieldByName(child.FieldName)
|
|
|
|
zeroValue := reflect.Value{}
|
|
if fd == zeroValue {
|
|
return fmt.Errorf("field not found, node: %s (%s)", child.Name, child.FieldName)
|
|
}
|
|
|
|
err := fill(fd, child)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func setSlice(field reflect.Value, node *Node) error {
|
|
if field.Type().Elem().Kind() == reflect.Struct {
|
|
return setSliceAsStruct(field, node)
|
|
}
|
|
|
|
if len(node.Value) == 0 {
|
|
return nil
|
|
}
|
|
|
|
values := strings.Split(node.Value, ",")
|
|
|
|
slice := reflect.MakeSlice(field.Type(), len(values), len(values))
|
|
field.Set(slice)
|
|
|
|
for i := 0; i < len(values); i++ {
|
|
value := strings.TrimSpace(values[i])
|
|
|
|
switch field.Type().Elem().Kind() {
|
|
case reflect.String:
|
|
field.Index(i).Set(reflect.ValueOf(value))
|
|
case reflect.Int:
|
|
val, err := strconv.ParseInt(value, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
field.Index(i).SetInt(val)
|
|
case reflect.Int8:
|
|
err := setInt(field.Index(i), value, 8)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Int16:
|
|
err := setInt(field.Index(i), value, 16)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Int32:
|
|
err := setInt(field.Index(i), value, 32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Int64:
|
|
err := setInt(field.Index(i), value, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Uint:
|
|
val, err := strconv.ParseUint(value, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
field.Index(i).SetUint(val)
|
|
case reflect.Uint8:
|
|
err := setUint(field.Index(i), value, 8)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Uint16:
|
|
err := setUint(field.Index(i), value, 16)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Uint32:
|
|
err := setUint(field.Index(i), value, 32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Uint64:
|
|
err := setUint(field.Index(i), value, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Float32:
|
|
err := setFloat(field.Index(i), value, 32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Float64:
|
|
err := setFloat(field.Index(i), value, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case reflect.Bool:
|
|
val, err := strconv.ParseBool(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
field.Index(i).SetBool(val)
|
|
default:
|
|
return fmt.Errorf("unsupported type: %s", field.Type().Elem())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setSliceAsStruct(field reflect.Value, node *Node) error {
|
|
if len(node.Children) == 0 {
|
|
return fmt.Errorf("invalid slice: node %s", node.Name)
|
|
}
|
|
|
|
// use Ptr to allow "SetDefaults"
|
|
value := reflect.New(reflect.PtrTo(field.Type().Elem()))
|
|
err := setPtr(value, node)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
elem := value.Elem().Elem()
|
|
|
|
field.Set(reflect.MakeSlice(field.Type(), 1, 1))
|
|
field.Index(0).Set(elem)
|
|
|
|
return nil
|
|
}
|
|
|
|
func setMap(field reflect.Value, node *Node) error {
|
|
if field.IsNil() {
|
|
field.Set(reflect.MakeMap(field.Type()))
|
|
}
|
|
|
|
for _, child := range node.Children {
|
|
ptrValue := reflect.New(reflect.PtrTo(field.Type().Elem()))
|
|
|
|
err := fill(ptrValue, child)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
value := ptrValue.Elem().Elem()
|
|
|
|
key := reflect.ValueOf(child.Name)
|
|
field.SetMapIndex(key, value)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setInt(field reflect.Value, value string, bitSize int) error {
|
|
switch field.Type() {
|
|
case reflect.TypeOf(parse.Duration(0)):
|
|
return setDuration(field, value, bitSize, time.Second)
|
|
case reflect.TypeOf(time.Duration(0)):
|
|
return setDuration(field, value, bitSize, time.Nanosecond)
|
|
default:
|
|
val, err := strconv.ParseInt(value, 10, bitSize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func setDuration(field reflect.Value, value string, bitSize int, defaultUnit time.Duration) error {
|
|
val, err := strconv.ParseInt(value, 10, bitSize)
|
|
if err == nil {
|
|
field.Set(reflect.ValueOf(time.Duration(val) * defaultUnit).Convert(field.Type()))
|
|
return nil
|
|
}
|
|
|
|
duration, err := time.ParseDuration(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
field.Set(reflect.ValueOf(duration).Convert(field.Type()))
|
|
return nil
|
|
}
|
|
|
|
func setUint(field reflect.Value, value string, bitSize int) error {
|
|
val, err := strconv.ParseUint(value, 10, bitSize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
|
return nil
|
|
}
|
|
|
|
func setFloat(field reflect.Value, value string, bitSize int) error {
|
|
val, err := strconv.ParseFloat(value, bitSize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
|
return nil
|
|
}
|