forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/mitchellh/mapstructure/decode_hooks.go

483 lines
6.9 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
package mapstructure
import (
"encoding"
"errors"
"fmt"
"net"
"reflect"
"strconv"
"strings"
"time"
)
// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
2024-02-18 10:42:21 +00:00
// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
2024-02-18 10:42:21 +00:00
func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
2024-02-18 10:42:21 +00:00
// Create variables here so we can reference them with the reflect pkg
2024-02-18 10:42:21 +00:00
var f1 DecodeHookFuncType
2024-02-18 10:42:21 +00:00
var f2 DecodeHookFuncKind
2024-02-18 10:42:21 +00:00
var f3 DecodeHookFuncValue
// Fill in the variables into this interface and the rest is done
2024-02-18 10:42:21 +00:00
// automatically using the reflect package.
2024-02-18 10:42:21 +00:00
potential := []interface{}{f1, f2, f3}
v := reflect.ValueOf(h)
2024-02-18 10:42:21 +00:00
vt := v.Type()
2024-02-18 10:42:21 +00:00
for _, raw := range potential {
2024-02-18 10:42:21 +00:00
pt := reflect.ValueOf(raw).Type()
2024-02-18 10:42:21 +00:00
if vt.ConvertibleTo(pt) {
2024-02-18 10:42:21 +00:00
return v.Convert(pt).Interface()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
// DecodeHookExec executes the given decode hook. This should be used
2024-02-18 10:42:21 +00:00
// since it'll naturally degrade to the older backwards compatible DecodeHookFunc
2024-02-18 10:42:21 +00:00
// that took reflect.Kind instead of reflect.Type.
2024-02-18 10:42:21 +00:00
func DecodeHookExec(
2024-02-18 10:42:21 +00:00
raw DecodeHookFunc,
2024-02-18 10:42:21 +00:00
from reflect.Value, to reflect.Value) (interface{}, error) {
switch f := typedDecodeHook(raw).(type) {
2024-02-18 10:42:21 +00:00
case DecodeHookFuncType:
2024-02-18 10:42:21 +00:00
return f(from.Type(), to.Type(), from.Interface())
2024-02-18 10:42:21 +00:00
case DecodeHookFuncKind:
2024-02-18 10:42:21 +00:00
return f(from.Kind(), to.Kind(), from.Interface())
2024-02-18 10:42:21 +00:00
case DecodeHookFuncValue:
2024-02-18 10:42:21 +00:00
return f(from, to)
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return nil, errors.New("invalid decode hook signature")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// ComposeDecodeHookFunc creates a single DecodeHookFunc that
2024-02-18 10:42:21 +00:00
// automatically composes multiple DecodeHookFuncs.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The composed funcs are called in order, with the result of the
2024-02-18 10:42:21 +00:00
// previous transformation.
2024-02-18 10:42:21 +00:00
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
2024-02-18 10:42:21 +00:00
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
data := f.Interface()
newFrom := f
2024-02-18 10:42:21 +00:00
for _, f1 := range fs {
2024-02-18 10:42:21 +00:00
data, err = DecodeHookExec(f1, newFrom, t)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
newFrom = reflect.ValueOf(data)
2024-02-18 10:42:21 +00:00
}
return data, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
2024-02-18 10:42:21 +00:00
// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
2024-02-18 10:42:21 +00:00
func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
2024-02-18 10:42:21 +00:00
return func(a, b reflect.Value) (interface{}, error) {
2024-02-18 10:42:21 +00:00
var allErrs string
2024-02-18 10:42:21 +00:00
var out interface{}
2024-02-18 10:42:21 +00:00
var err error
for _, f := range ff {
2024-02-18 10:42:21 +00:00
out, err = DecodeHookExec(f, a, b)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
allErrs += err.Error() + "\n"
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
return out, nil
2024-02-18 10:42:21 +00:00
}
return nil, errors.New(allErrs)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// StringToSliceHookFunc returns a DecodeHookFunc that converts
2024-02-18 10:42:21 +00:00
// string to []string by splitting on the given sep.
2024-02-18 10:42:21 +00:00
func StringToSliceHookFunc(sep string) DecodeHookFunc {
2024-02-18 10:42:21 +00:00
return func(
2024-02-18 10:42:21 +00:00
f reflect.Kind,
2024-02-18 10:42:21 +00:00
t reflect.Kind,
2024-02-18 10:42:21 +00:00
data interface{}) (interface{}, error) {
2024-02-18 10:42:21 +00:00
if f != reflect.String || t != reflect.Slice {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
raw := data.(string)
2024-02-18 10:42:21 +00:00
if raw == "" {
2024-02-18 10:42:21 +00:00
return []string{}, nil
2024-02-18 10:42:21 +00:00
}
return strings.Split(raw, sep), nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
2024-02-18 10:42:21 +00:00
// strings to time.Duration.
2024-02-18 10:42:21 +00:00
func StringToTimeDurationHookFunc() DecodeHookFunc {
2024-02-18 10:42:21 +00:00
return func(
2024-02-18 10:42:21 +00:00
f reflect.Type,
2024-02-18 10:42:21 +00:00
t reflect.Type,
2024-02-18 10:42:21 +00:00
data interface{}) (interface{}, error) {
2024-02-18 10:42:21 +00:00
if f.Kind() != reflect.String {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if t != reflect.TypeOf(time.Duration(5)) {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
// Convert it by parsing
2024-02-18 10:42:21 +00:00
return time.ParseDuration(data.(string))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// StringToIPHookFunc returns a DecodeHookFunc that converts
2024-02-18 10:42:21 +00:00
// strings to net.IP
2024-02-18 10:42:21 +00:00
func StringToIPHookFunc() DecodeHookFunc {
2024-02-18 10:42:21 +00:00
return func(
2024-02-18 10:42:21 +00:00
f reflect.Type,
2024-02-18 10:42:21 +00:00
t reflect.Type,
2024-02-18 10:42:21 +00:00
data interface{}) (interface{}, error) {
2024-02-18 10:42:21 +00:00
if f.Kind() != reflect.String {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if t != reflect.TypeOf(net.IP{}) {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
// Convert it by parsing
2024-02-18 10:42:21 +00:00
ip := net.ParseIP(data.(string))
2024-02-18 10:42:21 +00:00
if ip == nil {
2024-02-18 10:42:21 +00:00
return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
2024-02-18 10:42:21 +00:00
}
return ip, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
2024-02-18 10:42:21 +00:00
// strings to net.IPNet
2024-02-18 10:42:21 +00:00
func StringToIPNetHookFunc() DecodeHookFunc {
2024-02-18 10:42:21 +00:00
return func(
2024-02-18 10:42:21 +00:00
f reflect.Type,
2024-02-18 10:42:21 +00:00
t reflect.Type,
2024-02-18 10:42:21 +00:00
data interface{}) (interface{}, error) {
2024-02-18 10:42:21 +00:00
if f.Kind() != reflect.String {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if t != reflect.TypeOf(net.IPNet{}) {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
// Convert it by parsing
2024-02-18 10:42:21 +00:00
_, net, err := net.ParseCIDR(data.(string))
2024-02-18 10:42:21 +00:00
return net, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// StringToTimeHookFunc returns a DecodeHookFunc that converts
2024-02-18 10:42:21 +00:00
// strings to time.Time.
2024-02-18 10:42:21 +00:00
func StringToTimeHookFunc(layout string) DecodeHookFunc {
2024-02-18 10:42:21 +00:00
return func(
2024-02-18 10:42:21 +00:00
f reflect.Type,
2024-02-18 10:42:21 +00:00
t reflect.Type,
2024-02-18 10:42:21 +00:00
data interface{}) (interface{}, error) {
2024-02-18 10:42:21 +00:00
if f.Kind() != reflect.String {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if t != reflect.TypeOf(time.Time{}) {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
// Convert it by parsing
2024-02-18 10:42:21 +00:00
return time.Parse(layout, data.(string))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
2024-02-18 10:42:21 +00:00
// the decoder.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Note that this is significantly different from the WeaklyTypedInput option
2024-02-18 10:42:21 +00:00
// of the DecoderConfig.
2024-02-18 10:42:21 +00:00
func WeaklyTypedHook(
2024-02-18 10:42:21 +00:00
f reflect.Kind,
2024-02-18 10:42:21 +00:00
t reflect.Kind,
2024-02-18 10:42:21 +00:00
data interface{}) (interface{}, error) {
2024-02-18 10:42:21 +00:00
dataVal := reflect.ValueOf(data)
2024-02-18 10:42:21 +00:00
switch t {
2024-02-18 10:42:21 +00:00
case reflect.String:
2024-02-18 10:42:21 +00:00
switch f {
2024-02-18 10:42:21 +00:00
case reflect.Bool:
2024-02-18 10:42:21 +00:00
if dataVal.Bool() {
2024-02-18 10:42:21 +00:00
return "1", nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return "0", nil
2024-02-18 10:42:21 +00:00
case reflect.Float32:
2024-02-18 10:42:21 +00:00
return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
2024-02-18 10:42:21 +00:00
case reflect.Int:
2024-02-18 10:42:21 +00:00
return strconv.FormatInt(dataVal.Int(), 10), nil
2024-02-18 10:42:21 +00:00
case reflect.Slice:
2024-02-18 10:42:21 +00:00
dataType := dataVal.Type()
2024-02-18 10:42:21 +00:00
elemKind := dataType.Elem().Kind()
2024-02-18 10:42:21 +00:00
if elemKind == reflect.Uint8 {
2024-02-18 10:42:21 +00:00
return string(dataVal.Interface().([]uint8)), nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case reflect.Uint:
2024-02-18 10:42:21 +00:00
return strconv.FormatUint(dataVal.Uint(), 10), nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return data, nil
2024-02-18 10:42:21 +00:00
}
func RecursiveStructToMapHookFunc() DecodeHookFunc {
2024-02-18 10:42:21 +00:00
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
2024-02-18 10:42:21 +00:00
if f.Kind() != reflect.Struct {
2024-02-18 10:42:21 +00:00
return f.Interface(), nil
2024-02-18 10:42:21 +00:00
}
var i interface{} = struct{}{}
2024-02-18 10:42:21 +00:00
if t.Type() != reflect.TypeOf(&i).Elem() {
2024-02-18 10:42:21 +00:00
return f.Interface(), nil
2024-02-18 10:42:21 +00:00
}
m := make(map[string]interface{})
2024-02-18 10:42:21 +00:00
t.Set(reflect.ValueOf(m))
return f.Interface(), nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
2024-02-18 10:42:21 +00:00
// strings to the UnmarshalText function, when the target type
2024-02-18 10:42:21 +00:00
// implements the encoding.TextUnmarshaler interface
2024-02-18 10:42:21 +00:00
func TextUnmarshallerHookFunc() DecodeHookFuncType {
2024-02-18 10:42:21 +00:00
return func(
2024-02-18 10:42:21 +00:00
f reflect.Type,
2024-02-18 10:42:21 +00:00
t reflect.Type,
2024-02-18 10:42:21 +00:00
data interface{}) (interface{}, error) {
2024-02-18 10:42:21 +00:00
if f.Kind() != reflect.String {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
result := reflect.New(t).Interface()
2024-02-18 10:42:21 +00:00
unmarshaller, ok := result.(encoding.TextUnmarshaler)
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
return data, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return result, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}