OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / mitchellh / mapstructure / decode_hooks.go
1 package mapstructure
2
3 import (
4         "errors"
5         "reflect"
6         "strconv"
7         "strings"
8         "time"
9 )
10
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
17
18         // Fill in the variables into this interface and the rest is done
19         // automatically using the reflect package.
20         potential := []interface{}{f1, f2}
21
22         v := reflect.ValueOf(h)
23         vt := v.Type()
24         for _, raw := range potential {
25                 pt := reflect.ValueOf(raw).Type()
26                 if vt.ConvertibleTo(pt) {
27                         return v.Convert(pt).Interface()
28                 }
29         }
30
31         return nil
32 }
33
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.
37 func DecodeHookExec(
38         raw DecodeHookFunc,
39         from reflect.Type, to reflect.Type,
40         data interface{}) (interface{}, error) {
41         switch f := typedDecodeHook(raw).(type) {
42         case DecodeHookFuncType:
43                 return f(from, to, data)
44         case DecodeHookFuncKind:
45                 return f(from.Kind(), to.Kind(), data)
46         default:
47                 return nil, errors.New("invalid decode hook signature")
48         }
49 }
50
51 // ComposeDecodeHookFunc creates a single DecodeHookFunc that
52 // automatically composes multiple DecodeHookFuncs.
53 //
54 // The composed funcs are called in order, with the result of the
55 // previous transformation.
56 func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
57         return func(
58                 f reflect.Type,
59                 t reflect.Type,
60                 data interface{}) (interface{}, error) {
61                 var err error
62                 for _, f1 := range fs {
63                         data, err = DecodeHookExec(f1, f, t, data)
64                         if err != nil {
65                                 return nil, err
66                         }
67
68                         // Modify the from kind to be correct with the new data
69                         f = nil
70                         if val := reflect.ValueOf(data); val.IsValid() {
71                                 f = val.Type()
72                         }
73                 }
74
75                 return data, nil
76         }
77 }
78
79 // StringToSliceHookFunc returns a DecodeHookFunc that converts
80 // string to []string by splitting on the given sep.
81 func StringToSliceHookFunc(sep string) DecodeHookFunc {
82         return func(
83                 f reflect.Kind,
84                 t reflect.Kind,
85                 data interface{}) (interface{}, error) {
86                 if f != reflect.String || t != reflect.Slice {
87                         return data, nil
88                 }
89
90                 raw := data.(string)
91                 if raw == "" {
92                         return []string{}, nil
93                 }
94
95                 return strings.Split(raw, sep), nil
96         }
97 }
98
99 // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
100 // strings to time.Duration.
101 func StringToTimeDurationHookFunc() DecodeHookFunc {
102         return func(
103                 f reflect.Type,
104                 t reflect.Type,
105                 data interface{}) (interface{}, error) {
106                 if f.Kind() != reflect.String {
107                         return data, nil
108                 }
109                 if t != reflect.TypeOf(time.Duration(5)) {
110                         return data, nil
111                 }
112
113                 // Convert it by parsing
114                 return time.ParseDuration(data.(string))
115         }
116 }
117
118 // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
119 // the decoder.
120 //
121 // Note that this is significantly different from the WeaklyTypedInput option
122 // of the DecoderConfig.
123 func WeaklyTypedHook(
124         f reflect.Kind,
125         t reflect.Kind,
126         data interface{}) (interface{}, error) {
127         dataVal := reflect.ValueOf(data)
128         switch t {
129         case reflect.String:
130                 switch f {
131                 case reflect.Bool:
132                         if dataVal.Bool() {
133                                 return "1", nil
134                         }
135                         return "0", nil
136                 case reflect.Float32:
137                         return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
138                 case reflect.Int:
139                         return strconv.FormatInt(dataVal.Int(), 10), nil
140                 case reflect.Slice:
141                         dataType := dataVal.Type()
142                         elemKind := dataType.Elem().Kind()
143                         if elemKind == reflect.Uint8 {
144                                 return string(dataVal.Interface().([]uint8)), nil
145                         }
146                 case reflect.Uint:
147                         return strconv.FormatUint(dataVal.Uint(), 10), nil
148                 }
149         }
150
151         return data, nil
152 }