11 // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
12 // it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
13 func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
14 // Create variables here so we can reference them with the reflect pkg
15 var f1 DecodeHookFuncType
16 var f2 DecodeHookFuncKind
18 // Fill in the variables into this interface and the rest is done
19 // automatically using the reflect package.
20 potential := []interface{}{f1, f2}
22 v := reflect.ValueOf(h)
24 for _, raw := range potential {
25 pt := reflect.ValueOf(raw).Type()
26 if vt.ConvertibleTo(pt) {
27 return v.Convert(pt).Interface()
34 // DecodeHookExec executes the given decode hook. This should be used
35 // since it'll naturally degrade to the older backwards compatible DecodeHookFunc
36 // that took reflect.Kind instead of reflect.Type.
39 from reflect.Type, to reflect.Type,
40 data interface{}) (interface{}, error) {
41 // Build our arguments that reflect expects
42 argVals := make([]reflect.Value, 3)
43 argVals[0] = reflect.ValueOf(from)
44 argVals[1] = reflect.ValueOf(to)
45 argVals[2] = reflect.ValueOf(data)
47 switch f := typedDecodeHook(raw).(type) {
48 case DecodeHookFuncType:
49 return f(from, to, data)
50 case DecodeHookFuncKind:
51 return f(from.Kind(), to.Kind(), data)
53 return nil, errors.New("invalid decode hook signature")
57 // ComposeDecodeHookFunc creates a single DecodeHookFunc that
58 // automatically composes multiple DecodeHookFuncs.
60 // The composed funcs are called in order, with the result of the
61 // previous transformation.
62 func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
66 data interface{}) (interface{}, error) {
68 for _, f1 := range fs {
69 data, err = DecodeHookExec(f1, f, t, data)
74 // Modify the from kind to be correct with the new data
76 if val := reflect.ValueOf(data); val.IsValid() {
85 // StringToSliceHookFunc returns a DecodeHookFunc that converts
86 // string to []string by splitting on the given sep.
87 func StringToSliceHookFunc(sep string) DecodeHookFunc {
91 data interface{}) (interface{}, error) {
92 if f != reflect.String || t != reflect.Slice {
98 return []string{}, nil
101 return strings.Split(raw, sep), nil
105 // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
106 // strings to time.Duration.
107 func StringToTimeDurationHookFunc() DecodeHookFunc {
111 data interface{}) (interface{}, error) {
112 if f.Kind() != reflect.String {
115 if t != reflect.TypeOf(time.Duration(5)) {
119 // Convert it by parsing
120 return time.ParseDuration(data.(string))
124 func WeaklyTypedHook(
127 data interface{}) (interface{}, error) {
128 dataVal := reflect.ValueOf(data)
138 case reflect.Float32:
139 return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
141 return strconv.FormatInt(dataVal.Int(), 10), nil
143 dataType := dataVal.Type()
144 elemKind := dataType.Elem().Kind()
145 if elemKind == reflect.Uint8 {
146 return string(dataVal.Interface().([]uint8)), nil
149 return strconv.FormatUint(dataVal.Uint(), 10), nil