diff options
Diffstat (limited to 'vendor/github.com/mitchellh/mapstructure/decode_hooks.go')
-rw-r--r-- | vendor/github.com/mitchellh/mapstructure/decode_hooks.go | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go new file mode 100644 index 0000000..115ae67 --- /dev/null +++ b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go | |||
@@ -0,0 +1,154 @@ | |||
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 | // 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) | ||
46 | |||
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) | ||
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( | ||
64 | f reflect.Type, | ||
65 | t reflect.Type, | ||
66 | data interface{}) (interface{}, error) { | ||
67 | var err error | ||
68 | for _, f1 := range fs { | ||
69 | data, err = DecodeHookExec(f1, f, t, data) | ||
70 | if err != nil { | ||
71 | return nil, err | ||
72 | } | ||
73 | |||
74 | // Modify the from kind to be correct with the new data | ||
75 | f = nil | ||
76 | if val := reflect.ValueOf(data); val.IsValid() { | ||
77 | f = val.Type() | ||
78 | } | ||
79 | } | ||
80 | |||
81 | return data, nil | ||
82 | } | ||
83 | } | ||
84 | |||
85 | // StringToSliceHookFunc returns a DecodeHookFunc that converts | ||
86 | // string to []string by splitting on the given sep. | ||
87 | func StringToSliceHookFunc(sep string) DecodeHookFunc { | ||
88 | return func( | ||
89 | f reflect.Kind, | ||
90 | t reflect.Kind, | ||
91 | data interface{}) (interface{}, error) { | ||
92 | if f != reflect.String || t != reflect.Slice { | ||
93 | return data, nil | ||
94 | } | ||
95 | |||
96 | raw := data.(string) | ||
97 | if raw == "" { | ||
98 | return []string{}, nil | ||
99 | } | ||
100 | |||
101 | return strings.Split(raw, sep), nil | ||
102 | } | ||
103 | } | ||
104 | |||
105 | // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts | ||
106 | // strings to time.Duration. | ||
107 | func StringToTimeDurationHookFunc() DecodeHookFunc { | ||
108 | return func( | ||
109 | f reflect.Type, | ||
110 | t reflect.Type, | ||
111 | data interface{}) (interface{}, error) { | ||
112 | if f.Kind() != reflect.String { | ||
113 | return data, nil | ||
114 | } | ||
115 | if t != reflect.TypeOf(time.Duration(5)) { | ||
116 | return data, nil | ||
117 | } | ||
118 | |||
119 | // Convert it by parsing | ||
120 | return time.ParseDuration(data.(string)) | ||
121 | } | ||
122 | } | ||
123 | |||
124 | func WeaklyTypedHook( | ||
125 | f reflect.Kind, | ||
126 | t reflect.Kind, | ||
127 | data interface{}) (interface{}, error) { | ||
128 | dataVal := reflect.ValueOf(data) | ||
129 | switch t { | ||
130 | case reflect.String: | ||
131 | switch f { | ||
132 | case reflect.Bool: | ||
133 | if dataVal.Bool() { | ||
134 | return "1", nil | ||
135 | } else { | ||
136 | return "0", nil | ||
137 | } | ||
138 | case reflect.Float32: | ||
139 | return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil | ||
140 | case reflect.Int: | ||
141 | return strconv.FormatInt(dataVal.Int(), 10), nil | ||
142 | case reflect.Slice: | ||
143 | dataType := dataVal.Type() | ||
144 | elemKind := dataType.Elem().Kind() | ||
145 | if elemKind == reflect.Uint8 { | ||
146 | return string(dataVal.Interface().([]uint8)), nil | ||
147 | } | ||
148 | case reflect.Uint: | ||
149 | return strconv.FormatUint(dataVal.Uint(), 10), nil | ||
150 | } | ||
151 | } | ||
152 | |||
153 | return data, nil | ||
154 | } | ||