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
15
16 func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
17
18 var f1 DecodeHookFuncType
19 var f2 DecodeHookFuncKind
20 var f3 DecodeHookFuncValue
21
22
23
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
39
40
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
58
59
60
61
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
81
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
103
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
123
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
137 return time.ParseDuration(data.(string))
138 }
139 }
140
141
142
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
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
166
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
180 _, net, err := net.ParseCIDR(data.(string))
181 return net, err
182 }
183 }
184
185
186
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
200 return time.Parse(layout, data.(string))
201 }
202 }
203
204
205
206
207
208
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
259
260
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 }
280
View as plain text