gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

decode_hooks.go (6908B)


      1 package mapstructure
      2 
      3 import (
      4 	"encoding"
      5 	"errors"
      6 	"fmt"
      7 	"net"
      8 	"reflect"
      9 	"strconv"
     10 	"strings"
     11 	"time"
     12 )
     13 
     14 // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
     15 // it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
     16 func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
     17 	// Create variables here so we can reference them with the reflect pkg
     18 	var f1 DecodeHookFuncType
     19 	var f2 DecodeHookFuncKind
     20 	var f3 DecodeHookFuncValue
     21 
     22 	// Fill in the variables into this interface and the rest is done
     23 	// automatically using the reflect package.
     24 	potential := []interface{}{f1, f2, f3}
     25 
     26 	v := reflect.ValueOf(h)
     27 	vt := v.Type()
     28 	for _, raw := range potential {
     29 		pt := reflect.ValueOf(raw).Type()
     30 		if vt.ConvertibleTo(pt) {
     31 			return v.Convert(pt).Interface()
     32 		}
     33 	}
     34 
     35 	return nil
     36 }
     37 
     38 // DecodeHookExec executes the given decode hook. This should be used
     39 // since it'll naturally degrade to the older backwards compatible DecodeHookFunc
     40 // that took reflect.Kind instead of reflect.Type.
     41 func DecodeHookExec(
     42 	raw DecodeHookFunc,
     43 	from reflect.Value, to reflect.Value) (interface{}, error) {
     44 
     45 	switch f := typedDecodeHook(raw).(type) {
     46 	case DecodeHookFuncType:
     47 		return f(from.Type(), to.Type(), from.Interface())
     48 	case DecodeHookFuncKind:
     49 		return f(from.Kind(), to.Kind(), from.Interface())
     50 	case DecodeHookFuncValue:
     51 		return f(from, to)
     52 	default:
     53 		return nil, errors.New("invalid decode hook signature")
     54 	}
     55 }
     56 
     57 // ComposeDecodeHookFunc creates a single DecodeHookFunc that
     58 // automatically composes multiple DecodeHookFuncs.
     59 //
     60 // The composed funcs are called in order, with the result of the
     61 // previous transformation.
     62 func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
     63 	return func(f reflect.Value, t reflect.Value) (interface{}, error) {
     64 		var err error
     65 		data := f.Interface()
     66 
     67 		newFrom := f
     68 		for _, f1 := range fs {
     69 			data, err = DecodeHookExec(f1, newFrom, t)
     70 			if err != nil {
     71 				return nil, err
     72 			}
     73 			newFrom = reflect.ValueOf(data)
     74 		}
     75 
     76 		return data, nil
     77 	}
     78 }
     79 
     80 // OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
     81 // If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
     82 func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
     83 	return func(a, b reflect.Value) (interface{}, error) {
     84 		var allErrs string
     85 		var out interface{}
     86 		var err error
     87 
     88 		for _, f := range ff {
     89 			out, err = DecodeHookExec(f, a, b)
     90 			if err != nil {
     91 				allErrs += err.Error() + "\n"
     92 				continue
     93 			}
     94 
     95 			return out, nil
     96 		}
     97 
     98 		return nil, errors.New(allErrs)
     99 	}
    100 }
    101 
    102 // StringToSliceHookFunc returns a DecodeHookFunc that converts
    103 // string to []string by splitting on the given sep.
    104 func StringToSliceHookFunc(sep string) DecodeHookFunc {
    105 	return func(
    106 		f reflect.Kind,
    107 		t reflect.Kind,
    108 		data interface{}) (interface{}, error) {
    109 		if f != reflect.String || t != reflect.Slice {
    110 			return data, nil
    111 		}
    112 
    113 		raw := data.(string)
    114 		if raw == "" {
    115 			return []string{}, nil
    116 		}
    117 
    118 		return strings.Split(raw, sep), nil
    119 	}
    120 }
    121 
    122 // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
    123 // strings to time.Duration.
    124 func StringToTimeDurationHookFunc() DecodeHookFunc {
    125 	return func(
    126 		f reflect.Type,
    127 		t reflect.Type,
    128 		data interface{}) (interface{}, error) {
    129 		if f.Kind() != reflect.String {
    130 			return data, nil
    131 		}
    132 		if t != reflect.TypeOf(time.Duration(5)) {
    133 			return data, nil
    134 		}
    135 
    136 		// Convert it by parsing
    137 		return time.ParseDuration(data.(string))
    138 	}
    139 }
    140 
    141 // StringToIPHookFunc returns a DecodeHookFunc that converts
    142 // strings to net.IP
    143 func StringToIPHookFunc() DecodeHookFunc {
    144 	return func(
    145 		f reflect.Type,
    146 		t reflect.Type,
    147 		data interface{}) (interface{}, error) {
    148 		if f.Kind() != reflect.String {
    149 			return data, nil
    150 		}
    151 		if t != reflect.TypeOf(net.IP{}) {
    152 			return data, nil
    153 		}
    154 
    155 		// Convert it by parsing
    156 		ip := net.ParseIP(data.(string))
    157 		if ip == nil {
    158 			return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
    159 		}
    160 
    161 		return ip, nil
    162 	}
    163 }
    164 
    165 // StringToIPNetHookFunc returns a DecodeHookFunc that converts
    166 // strings to net.IPNet
    167 func StringToIPNetHookFunc() DecodeHookFunc {
    168 	return func(
    169 		f reflect.Type,
    170 		t reflect.Type,
    171 		data interface{}) (interface{}, error) {
    172 		if f.Kind() != reflect.String {
    173 			return data, nil
    174 		}
    175 		if t != reflect.TypeOf(net.IPNet{}) {
    176 			return data, nil
    177 		}
    178 
    179 		// Convert it by parsing
    180 		_, net, err := net.ParseCIDR(data.(string))
    181 		return net, err
    182 	}
    183 }
    184 
    185 // StringToTimeHookFunc returns a DecodeHookFunc that converts
    186 // strings to time.Time.
    187 func StringToTimeHookFunc(layout string) DecodeHookFunc {
    188 	return func(
    189 		f reflect.Type,
    190 		t reflect.Type,
    191 		data interface{}) (interface{}, error) {
    192 		if f.Kind() != reflect.String {
    193 			return data, nil
    194 		}
    195 		if t != reflect.TypeOf(time.Time{}) {
    196 			return data, nil
    197 		}
    198 
    199 		// Convert it by parsing
    200 		return time.Parse(layout, data.(string))
    201 	}
    202 }
    203 
    204 // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
    205 // the decoder.
    206 //
    207 // Note that this is significantly different from the WeaklyTypedInput option
    208 // of the DecoderConfig.
    209 func WeaklyTypedHook(
    210 	f reflect.Kind,
    211 	t reflect.Kind,
    212 	data interface{}) (interface{}, error) {
    213 	dataVal := reflect.ValueOf(data)
    214 	switch t {
    215 	case reflect.String:
    216 		switch f {
    217 		case reflect.Bool:
    218 			if dataVal.Bool() {
    219 				return "1", nil
    220 			}
    221 			return "0", nil
    222 		case reflect.Float32:
    223 			return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
    224 		case reflect.Int:
    225 			return strconv.FormatInt(dataVal.Int(), 10), nil
    226 		case reflect.Slice:
    227 			dataType := dataVal.Type()
    228 			elemKind := dataType.Elem().Kind()
    229 			if elemKind == reflect.Uint8 {
    230 				return string(dataVal.Interface().([]uint8)), nil
    231 			}
    232 		case reflect.Uint:
    233 			return strconv.FormatUint(dataVal.Uint(), 10), nil
    234 		}
    235 	}
    236 
    237 	return data, nil
    238 }
    239 
    240 func RecursiveStructToMapHookFunc() DecodeHookFunc {
    241 	return func(f reflect.Value, t reflect.Value) (interface{}, error) {
    242 		if f.Kind() != reflect.Struct {
    243 			return f.Interface(), nil
    244 		}
    245 
    246 		var i interface{} = struct{}{}
    247 		if t.Type() != reflect.TypeOf(&i).Elem() {
    248 			return f.Interface(), nil
    249 		}
    250 
    251 		m := make(map[string]interface{})
    252 		t.Set(reflect.ValueOf(m))
    253 
    254 		return f.Interface(), nil
    255 	}
    256 }
    257 
    258 // TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
    259 // strings to the UnmarshalText function, when the target type
    260 // implements the encoding.TextUnmarshaler interface
    261 func TextUnmarshallerHookFunc() DecodeHookFuncType {
    262 	return func(
    263 		f reflect.Type,
    264 		t reflect.Type,
    265 		data interface{}) (interface{}, error) {
    266 		if f.Kind() != reflect.String {
    267 			return data, nil
    268 		}
    269 		result := reflect.New(t).Interface()
    270 		unmarshaller, ok := result.(encoding.TextUnmarshaler)
    271 		if !ok {
    272 			return data, nil
    273 		}
    274 		if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil {
    275 			return nil, err
    276 		}
    277 		return result, nil
    278 	}
    279 }