]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | // Package mapstructure exposes functionality to convert an arbitrary |
2 | // map[string]interface{} into a native Go structure. | |
bae9f6d2 JC |
3 | // |
4 | // The Go structure can be arbitrarily complex, containing slices, | |
5 | // other structs, etc. and the decoder will properly decode nested | |
6 | // maps and so on into the proper structures in the native Go struct. | |
7 | // See the examples to see what the decoder is capable of. | |
8 | package mapstructure | |
9 | ||
10 | import ( | |
11 | "encoding/json" | |
12 | "errors" | |
13 | "fmt" | |
14 | "reflect" | |
15 | "sort" | |
16 | "strconv" | |
17 | "strings" | |
18 | ) | |
19 | ||
20 | // DecodeHookFunc is the callback function that can be used for | |
21 | // data transformations. See "DecodeHook" in the DecoderConfig | |
22 | // struct. | |
23 | // | |
24 | // The type should be DecodeHookFuncType or DecodeHookFuncKind. | |
25 | // Either is accepted. Types are a superset of Kinds (Types can return | |
26 | // Kinds) and are generally a richer thing to use, but Kinds are simpler | |
27 | // if you only need those. | |
28 | // | |
29 | // The reason DecodeHookFunc is multi-typed is for backwards compatibility: | |
30 | // we started with Kinds and then realized Types were the better solution, | |
31 | // but have a promise to not break backwards compat so we now support | |
32 | // both. | |
33 | type DecodeHookFunc interface{} | |
34 | ||
107c1cdb ND |
35 | // DecodeHookFuncType is a DecodeHookFunc which has complete information about |
36 | // the source and target types. | |
bae9f6d2 | 37 | type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error) |
107c1cdb ND |
38 | |
39 | // DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the | |
40 | // source and target types. | |
bae9f6d2 JC |
41 | type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) |
42 | ||
43 | // DecoderConfig is the configuration that is used to create a new decoder | |
44 | // and allows customization of various aspects of decoding. | |
45 | type DecoderConfig struct { | |
46 | // DecodeHook, if set, will be called before any decoding and any | |
47 | // type conversion (if WeaklyTypedInput is on). This lets you modify | |
48 | // the values before they're set down onto the resulting struct. | |
49 | // | |
50 | // If an error is returned, the entire decode will fail with that | |
51 | // error. | |
52 | DecodeHook DecodeHookFunc | |
53 | ||
54 | // If ErrorUnused is true, then it is an error for there to exist | |
55 | // keys in the original map that were unused in the decoding process | |
56 | // (extra keys). | |
57 | ErrorUnused bool | |
58 | ||
59 | // ZeroFields, if set to true, will zero fields before writing them. | |
60 | // For example, a map will be emptied before decoded values are put in | |
61 | // it. If this is false, a map will be merged. | |
62 | ZeroFields bool | |
63 | ||
64 | // If WeaklyTypedInput is true, the decoder will make the following | |
65 | // "weak" conversions: | |
66 | // | |
67 | // - bools to string (true = "1", false = "0") | |
68 | // - numbers to string (base 10) | |
69 | // - bools to int/uint (true = 1, false = 0) | |
70 | // - strings to int/uint (base implied by prefix) | |
71 | // - int to bool (true if value != 0) | |
72 | // - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F, | |
73 | // FALSE, false, False. Anything else is an error) | |
74 | // - empty array = empty map and vice versa | |
75 | // - negative numbers to overflowed uint values (base 10) | |
76 | // - slice of maps to a merged map | |
77 | // - single values are converted to slices if required. Each | |
78 | // element is weakly decoded. For example: "4" can become []int{4} | |
79 | // if the target type is an int slice. | |
80 | // | |
81 | WeaklyTypedInput bool | |
82 | ||
83 | // Metadata is the struct that will contain extra metadata about | |
84 | // the decoding. If this is nil, then no metadata will be tracked. | |
85 | Metadata *Metadata | |
86 | ||
87 | // Result is a pointer to the struct that will contain the decoded | |
88 | // value. | |
89 | Result interface{} | |
90 | ||
91 | // The tag name that mapstructure reads for field names. This | |
92 | // defaults to "mapstructure" | |
93 | TagName string | |
94 | } | |
95 | ||
96 | // A Decoder takes a raw interface value and turns it into structured | |
97 | // data, keeping track of rich error information along the way in case | |
98 | // anything goes wrong. Unlike the basic top-level Decode method, you can | |
99 | // more finely control how the Decoder behaves using the DecoderConfig | |
100 | // structure. The top-level Decode method is just a convenience that sets | |
101 | // up the most basic Decoder. | |
102 | type Decoder struct { | |
103 | config *DecoderConfig | |
104 | } | |
105 | ||
106 | // Metadata contains information about decoding a structure that | |
107 | // is tedious or difficult to get otherwise. | |
108 | type Metadata struct { | |
109 | // Keys are the keys of the structure which were successfully decoded | |
110 | Keys []string | |
111 | ||
112 | // Unused is a slice of keys that were found in the raw value but | |
113 | // weren't decoded since there was no matching field in the result interface | |
114 | Unused []string | |
115 | } | |
116 | ||
107c1cdb ND |
117 | // Decode takes an input structure and uses reflection to translate it to |
118 | // the output structure. output must be a pointer to a map or struct. | |
119 | func Decode(input interface{}, output interface{}) error { | |
bae9f6d2 JC |
120 | config := &DecoderConfig{ |
121 | Metadata: nil, | |
107c1cdb | 122 | Result: output, |
bae9f6d2 JC |
123 | } |
124 | ||
125 | decoder, err := NewDecoder(config) | |
126 | if err != nil { | |
127 | return err | |
128 | } | |
129 | ||
107c1cdb | 130 | return decoder.Decode(input) |
bae9f6d2 JC |
131 | } |
132 | ||
133 | // WeakDecode is the same as Decode but is shorthand to enable | |
134 | // WeaklyTypedInput. See DecoderConfig for more info. | |
135 | func WeakDecode(input, output interface{}) error { | |
136 | config := &DecoderConfig{ | |
137 | Metadata: nil, | |
138 | Result: output, | |
139 | WeaklyTypedInput: true, | |
140 | } | |
141 | ||
142 | decoder, err := NewDecoder(config) | |
143 | if err != nil { | |
144 | return err | |
145 | } | |
146 | ||
147 | return decoder.Decode(input) | |
148 | } | |
149 | ||
107c1cdb ND |
150 | // DecodeMetadata is the same as Decode, but is shorthand to |
151 | // enable metadata collection. See DecoderConfig for more info. | |
152 | func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { | |
153 | config := &DecoderConfig{ | |
154 | Metadata: metadata, | |
155 | Result: output, | |
156 | } | |
157 | ||
158 | decoder, err := NewDecoder(config) | |
159 | if err != nil { | |
160 | return err | |
161 | } | |
162 | ||
163 | return decoder.Decode(input) | |
164 | } | |
165 | ||
166 | // WeakDecodeMetadata is the same as Decode, but is shorthand to | |
167 | // enable both WeaklyTypedInput and metadata collection. See | |
168 | // DecoderConfig for more info. | |
169 | func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { | |
170 | config := &DecoderConfig{ | |
171 | Metadata: metadata, | |
172 | Result: output, | |
173 | WeaklyTypedInput: true, | |
174 | } | |
175 | ||
176 | decoder, err := NewDecoder(config) | |
177 | if err != nil { | |
178 | return err | |
179 | } | |
180 | ||
181 | return decoder.Decode(input) | |
182 | } | |
183 | ||
bae9f6d2 JC |
184 | // NewDecoder returns a new decoder for the given configuration. Once |
185 | // a decoder has been returned, the same configuration must not be used | |
186 | // again. | |
187 | func NewDecoder(config *DecoderConfig) (*Decoder, error) { | |
188 | val := reflect.ValueOf(config.Result) | |
189 | if val.Kind() != reflect.Ptr { | |
190 | return nil, errors.New("result must be a pointer") | |
191 | } | |
192 | ||
193 | val = val.Elem() | |
194 | if !val.CanAddr() { | |
195 | return nil, errors.New("result must be addressable (a pointer)") | |
196 | } | |
197 | ||
198 | if config.Metadata != nil { | |
199 | if config.Metadata.Keys == nil { | |
200 | config.Metadata.Keys = make([]string, 0) | |
201 | } | |
202 | ||
203 | if config.Metadata.Unused == nil { | |
204 | config.Metadata.Unused = make([]string, 0) | |
205 | } | |
206 | } | |
207 | ||
208 | if config.TagName == "" { | |
209 | config.TagName = "mapstructure" | |
210 | } | |
211 | ||
212 | result := &Decoder{ | |
213 | config: config, | |
214 | } | |
215 | ||
216 | return result, nil | |
217 | } | |
218 | ||
219 | // Decode decodes the given raw interface to the target pointer specified | |
220 | // by the configuration. | |
107c1cdb ND |
221 | func (d *Decoder) Decode(input interface{}) error { |
222 | return d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) | |
bae9f6d2 JC |
223 | } |
224 | ||
225 | // Decodes an unknown data type into a specific reflection value. | |
107c1cdb ND |
226 | func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error { |
227 | var inputVal reflect.Value | |
228 | if input != nil { | |
229 | inputVal = reflect.ValueOf(input) | |
230 | ||
231 | // We need to check here if input is a typed nil. Typed nils won't | |
232 | // match the "input == nil" below so we check that here. | |
233 | if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() { | |
234 | input = nil | |
235 | } | |
236 | } | |
237 | ||
238 | if input == nil { | |
239 | // If the data is nil, then we don't set anything, unless ZeroFields is set | |
240 | // to true. | |
241 | if d.config.ZeroFields { | |
242 | outVal.Set(reflect.Zero(outVal.Type())) | |
243 | ||
244 | if d.config.Metadata != nil && name != "" { | |
245 | d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) | |
246 | } | |
247 | } | |
bae9f6d2 JC |
248 | return nil |
249 | } | |
250 | ||
107c1cdb ND |
251 | if !inputVal.IsValid() { |
252 | // If the input value is invalid, then we just set the value | |
bae9f6d2 | 253 | // to be the zero value. |
107c1cdb ND |
254 | outVal.Set(reflect.Zero(outVal.Type())) |
255 | if d.config.Metadata != nil && name != "" { | |
256 | d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) | |
257 | } | |
bae9f6d2 JC |
258 | return nil |
259 | } | |
260 | ||
261 | if d.config.DecodeHook != nil { | |
107c1cdb | 262 | // We have a DecodeHook, so let's pre-process the input. |
bae9f6d2 | 263 | var err error |
107c1cdb | 264 | input, err = DecodeHookExec( |
bae9f6d2 | 265 | d.config.DecodeHook, |
107c1cdb | 266 | inputVal.Type(), outVal.Type(), input) |
bae9f6d2 JC |
267 | if err != nil { |
268 | return fmt.Errorf("error decoding '%s': %s", name, err) | |
269 | } | |
270 | } | |
271 | ||
272 | var err error | |
107c1cdb ND |
273 | outputKind := getKind(outVal) |
274 | switch outputKind { | |
bae9f6d2 | 275 | case reflect.Bool: |
107c1cdb | 276 | err = d.decodeBool(name, input, outVal) |
bae9f6d2 | 277 | case reflect.Interface: |
107c1cdb | 278 | err = d.decodeBasic(name, input, outVal) |
bae9f6d2 | 279 | case reflect.String: |
107c1cdb | 280 | err = d.decodeString(name, input, outVal) |
bae9f6d2 | 281 | case reflect.Int: |
107c1cdb | 282 | err = d.decodeInt(name, input, outVal) |
bae9f6d2 | 283 | case reflect.Uint: |
107c1cdb | 284 | err = d.decodeUint(name, input, outVal) |
bae9f6d2 | 285 | case reflect.Float32: |
107c1cdb | 286 | err = d.decodeFloat(name, input, outVal) |
bae9f6d2 | 287 | case reflect.Struct: |
107c1cdb | 288 | err = d.decodeStruct(name, input, outVal) |
bae9f6d2 | 289 | case reflect.Map: |
107c1cdb | 290 | err = d.decodeMap(name, input, outVal) |
bae9f6d2 | 291 | case reflect.Ptr: |
107c1cdb | 292 | err = d.decodePtr(name, input, outVal) |
bae9f6d2 | 293 | case reflect.Slice: |
107c1cdb ND |
294 | err = d.decodeSlice(name, input, outVal) |
295 | case reflect.Array: | |
296 | err = d.decodeArray(name, input, outVal) | |
bae9f6d2 | 297 | case reflect.Func: |
107c1cdb | 298 | err = d.decodeFunc(name, input, outVal) |
bae9f6d2 JC |
299 | default: |
300 | // If we reached this point then we weren't able to decode it | |
107c1cdb | 301 | return fmt.Errorf("%s: unsupported type: %s", name, outputKind) |
bae9f6d2 JC |
302 | } |
303 | ||
304 | // If we reached here, then we successfully decoded SOMETHING, so | |
107c1cdb | 305 | // mark the key as used if we're tracking metainput. |
bae9f6d2 JC |
306 | if d.config.Metadata != nil && name != "" { |
307 | d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) | |
308 | } | |
309 | ||
310 | return err | |
311 | } | |
312 | ||
313 | // This decodes a basic type (bool, int, string, etc.) and sets the | |
314 | // value to "data" of that type. | |
315 | func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { | |
107c1cdb ND |
316 | if val.IsValid() && val.Elem().IsValid() { |
317 | return d.decode(name, data, val.Elem()) | |
318 | } | |
319 | ||
bae9f6d2 | 320 | dataVal := reflect.ValueOf(data) |
107c1cdb ND |
321 | |
322 | // If the input data is a pointer, and the assigned type is the dereference | |
323 | // of that exact pointer, then indirect it so that we can assign it. | |
324 | // Example: *string to string | |
325 | if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() { | |
326 | dataVal = reflect.Indirect(dataVal) | |
327 | } | |
328 | ||
bae9f6d2 JC |
329 | if !dataVal.IsValid() { |
330 | dataVal = reflect.Zero(val.Type()) | |
331 | } | |
332 | ||
333 | dataValType := dataVal.Type() | |
334 | if !dataValType.AssignableTo(val.Type()) { | |
335 | return fmt.Errorf( | |
336 | "'%s' expected type '%s', got '%s'", | |
337 | name, val.Type(), dataValType) | |
338 | } | |
339 | ||
340 | val.Set(dataVal) | |
341 | return nil | |
342 | } | |
343 | ||
344 | func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error { | |
107c1cdb | 345 | dataVal := reflect.Indirect(reflect.ValueOf(data)) |
bae9f6d2 JC |
346 | dataKind := getKind(dataVal) |
347 | ||
348 | converted := true | |
349 | switch { | |
350 | case dataKind == reflect.String: | |
351 | val.SetString(dataVal.String()) | |
352 | case dataKind == reflect.Bool && d.config.WeaklyTypedInput: | |
353 | if dataVal.Bool() { | |
354 | val.SetString("1") | |
355 | } else { | |
356 | val.SetString("0") | |
357 | } | |
358 | case dataKind == reflect.Int && d.config.WeaklyTypedInput: | |
359 | val.SetString(strconv.FormatInt(dataVal.Int(), 10)) | |
360 | case dataKind == reflect.Uint && d.config.WeaklyTypedInput: | |
361 | val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) | |
362 | case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: | |
363 | val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64)) | |
107c1cdb ND |
364 | case dataKind == reflect.Slice && d.config.WeaklyTypedInput, |
365 | dataKind == reflect.Array && d.config.WeaklyTypedInput: | |
bae9f6d2 JC |
366 | dataType := dataVal.Type() |
367 | elemKind := dataType.Elem().Kind() | |
107c1cdb ND |
368 | switch elemKind { |
369 | case reflect.Uint8: | |
370 | var uints []uint8 | |
371 | if dataKind == reflect.Array { | |
372 | uints = make([]uint8, dataVal.Len(), dataVal.Len()) | |
373 | for i := range uints { | |
374 | uints[i] = dataVal.Index(i).Interface().(uint8) | |
375 | } | |
376 | } else { | |
377 | uints = dataVal.Interface().([]uint8) | |
378 | } | |
379 | val.SetString(string(uints)) | |
bae9f6d2 JC |
380 | default: |
381 | converted = false | |
382 | } | |
383 | default: | |
384 | converted = false | |
385 | } | |
386 | ||
387 | if !converted { | |
388 | return fmt.Errorf( | |
389 | "'%s' expected type '%s', got unconvertible type '%s'", | |
390 | name, val.Type(), dataVal.Type()) | |
391 | } | |
392 | ||
393 | return nil | |
394 | } | |
395 | ||
396 | func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { | |
107c1cdb | 397 | dataVal := reflect.Indirect(reflect.ValueOf(data)) |
bae9f6d2 JC |
398 | dataKind := getKind(dataVal) |
399 | dataType := dataVal.Type() | |
400 | ||
401 | switch { | |
402 | case dataKind == reflect.Int: | |
403 | val.SetInt(dataVal.Int()) | |
404 | case dataKind == reflect.Uint: | |
405 | val.SetInt(int64(dataVal.Uint())) | |
406 | case dataKind == reflect.Float32: | |
407 | val.SetInt(int64(dataVal.Float())) | |
408 | case dataKind == reflect.Bool && d.config.WeaklyTypedInput: | |
409 | if dataVal.Bool() { | |
410 | val.SetInt(1) | |
411 | } else { | |
412 | val.SetInt(0) | |
413 | } | |
414 | case dataKind == reflect.String && d.config.WeaklyTypedInput: | |
415 | i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits()) | |
416 | if err == nil { | |
417 | val.SetInt(i) | |
418 | } else { | |
419 | return fmt.Errorf("cannot parse '%s' as int: %s", name, err) | |
420 | } | |
421 | case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": | |
422 | jn := data.(json.Number) | |
423 | i, err := jn.Int64() | |
424 | if err != nil { | |
425 | return fmt.Errorf( | |
426 | "error decoding json.Number into %s: %s", name, err) | |
427 | } | |
428 | val.SetInt(i) | |
429 | default: | |
430 | return fmt.Errorf( | |
431 | "'%s' expected type '%s', got unconvertible type '%s'", | |
432 | name, val.Type(), dataVal.Type()) | |
433 | } | |
434 | ||
435 | return nil | |
436 | } | |
437 | ||
438 | func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { | |
107c1cdb | 439 | dataVal := reflect.Indirect(reflect.ValueOf(data)) |
bae9f6d2 JC |
440 | dataKind := getKind(dataVal) |
441 | ||
442 | switch { | |
443 | case dataKind == reflect.Int: | |
444 | i := dataVal.Int() | |
445 | if i < 0 && !d.config.WeaklyTypedInput { | |
446 | return fmt.Errorf("cannot parse '%s', %d overflows uint", | |
447 | name, i) | |
448 | } | |
449 | val.SetUint(uint64(i)) | |
450 | case dataKind == reflect.Uint: | |
451 | val.SetUint(dataVal.Uint()) | |
452 | case dataKind == reflect.Float32: | |
453 | f := dataVal.Float() | |
454 | if f < 0 && !d.config.WeaklyTypedInput { | |
455 | return fmt.Errorf("cannot parse '%s', %f overflows uint", | |
456 | name, f) | |
457 | } | |
458 | val.SetUint(uint64(f)) | |
459 | case dataKind == reflect.Bool && d.config.WeaklyTypedInput: | |
460 | if dataVal.Bool() { | |
461 | val.SetUint(1) | |
462 | } else { | |
463 | val.SetUint(0) | |
464 | } | |
465 | case dataKind == reflect.String && d.config.WeaklyTypedInput: | |
466 | i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits()) | |
467 | if err == nil { | |
468 | val.SetUint(i) | |
469 | } else { | |
470 | return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) | |
471 | } | |
472 | default: | |
473 | return fmt.Errorf( | |
474 | "'%s' expected type '%s', got unconvertible type '%s'", | |
475 | name, val.Type(), dataVal.Type()) | |
476 | } | |
477 | ||
478 | return nil | |
479 | } | |
480 | ||
481 | func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error { | |
107c1cdb | 482 | dataVal := reflect.Indirect(reflect.ValueOf(data)) |
bae9f6d2 JC |
483 | dataKind := getKind(dataVal) |
484 | ||
485 | switch { | |
486 | case dataKind == reflect.Bool: | |
487 | val.SetBool(dataVal.Bool()) | |
488 | case dataKind == reflect.Int && d.config.WeaklyTypedInput: | |
489 | val.SetBool(dataVal.Int() != 0) | |
490 | case dataKind == reflect.Uint && d.config.WeaklyTypedInput: | |
491 | val.SetBool(dataVal.Uint() != 0) | |
492 | case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: | |
493 | val.SetBool(dataVal.Float() != 0) | |
494 | case dataKind == reflect.String && d.config.WeaklyTypedInput: | |
495 | b, err := strconv.ParseBool(dataVal.String()) | |
496 | if err == nil { | |
497 | val.SetBool(b) | |
498 | } else if dataVal.String() == "" { | |
499 | val.SetBool(false) | |
500 | } else { | |
501 | return fmt.Errorf("cannot parse '%s' as bool: %s", name, err) | |
502 | } | |
503 | default: | |
504 | return fmt.Errorf( | |
505 | "'%s' expected type '%s', got unconvertible type '%s'", | |
506 | name, val.Type(), dataVal.Type()) | |
507 | } | |
508 | ||
509 | return nil | |
510 | } | |
511 | ||
512 | func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { | |
107c1cdb | 513 | dataVal := reflect.Indirect(reflect.ValueOf(data)) |
bae9f6d2 JC |
514 | dataKind := getKind(dataVal) |
515 | dataType := dataVal.Type() | |
516 | ||
517 | switch { | |
518 | case dataKind == reflect.Int: | |
519 | val.SetFloat(float64(dataVal.Int())) | |
520 | case dataKind == reflect.Uint: | |
521 | val.SetFloat(float64(dataVal.Uint())) | |
522 | case dataKind == reflect.Float32: | |
107c1cdb | 523 | val.SetFloat(dataVal.Float()) |
bae9f6d2 JC |
524 | case dataKind == reflect.Bool && d.config.WeaklyTypedInput: |
525 | if dataVal.Bool() { | |
526 | val.SetFloat(1) | |
527 | } else { | |
528 | val.SetFloat(0) | |
529 | } | |
530 | case dataKind == reflect.String && d.config.WeaklyTypedInput: | |
531 | f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits()) | |
532 | if err == nil { | |
533 | val.SetFloat(f) | |
534 | } else { | |
535 | return fmt.Errorf("cannot parse '%s' as float: %s", name, err) | |
536 | } | |
537 | case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": | |
538 | jn := data.(json.Number) | |
539 | i, err := jn.Float64() | |
540 | if err != nil { | |
541 | return fmt.Errorf( | |
542 | "error decoding json.Number into %s: %s", name, err) | |
543 | } | |
544 | val.SetFloat(i) | |
545 | default: | |
546 | return fmt.Errorf( | |
547 | "'%s' expected type '%s', got unconvertible type '%s'", | |
548 | name, val.Type(), dataVal.Type()) | |
549 | } | |
550 | ||
551 | return nil | |
552 | } | |
553 | ||
554 | func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error { | |
555 | valType := val.Type() | |
556 | valKeyType := valType.Key() | |
557 | valElemType := valType.Elem() | |
558 | ||
559 | // By default we overwrite keys in the current map | |
560 | valMap := val | |
561 | ||
562 | // If the map is nil or we're purposely zeroing fields, make a new map | |
563 | if valMap.IsNil() || d.config.ZeroFields { | |
564 | // Make a new map to hold our result | |
565 | mapType := reflect.MapOf(valKeyType, valElemType) | |
566 | valMap = reflect.MakeMap(mapType) | |
567 | } | |
568 | ||
107c1cdb | 569 | // Check input type and based on the input type jump to the proper func |
bae9f6d2 | 570 | dataVal := reflect.Indirect(reflect.ValueOf(data)) |
107c1cdb ND |
571 | switch dataVal.Kind() { |
572 | case reflect.Map: | |
573 | return d.decodeMapFromMap(name, dataVal, val, valMap) | |
bae9f6d2 | 574 | |
107c1cdb ND |
575 | case reflect.Struct: |
576 | return d.decodeMapFromStruct(name, dataVal, val, valMap) | |
bae9f6d2 | 577 | |
107c1cdb ND |
578 | case reflect.Array, reflect.Slice: |
579 | if d.config.WeaklyTypedInput { | |
580 | return d.decodeMapFromSlice(name, dataVal, val, valMap) | |
bae9f6d2 JC |
581 | } |
582 | ||
107c1cdb ND |
583 | fallthrough |
584 | ||
585 | default: | |
bae9f6d2 JC |
586 | return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) |
587 | } | |
107c1cdb ND |
588 | } |
589 | ||
590 | func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { | |
591 | // Special case for BC reasons (covered by tests) | |
592 | if dataVal.Len() == 0 { | |
593 | val.Set(valMap) | |
594 | return nil | |
595 | } | |
596 | ||
597 | for i := 0; i < dataVal.Len(); i++ { | |
598 | err := d.decode( | |
599 | fmt.Sprintf("%s[%d]", name, i), | |
600 | dataVal.Index(i).Interface(), val) | |
601 | if err != nil { | |
602 | return err | |
603 | } | |
604 | } | |
605 | ||
606 | return nil | |
607 | } | |
608 | ||
609 | func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { | |
610 | valType := val.Type() | |
611 | valKeyType := valType.Key() | |
612 | valElemType := valType.Elem() | |
bae9f6d2 JC |
613 | |
614 | // Accumulate errors | |
615 | errors := make([]string, 0) | |
616 | ||
107c1cdb ND |
617 | // If the input data is empty, then we just match what the input data is. |
618 | if dataVal.Len() == 0 { | |
619 | if dataVal.IsNil() { | |
620 | if !val.IsNil() { | |
621 | val.Set(dataVal) | |
622 | } | |
623 | } else { | |
624 | // Set to empty allocated value | |
625 | val.Set(valMap) | |
626 | } | |
627 | ||
628 | return nil | |
629 | } | |
630 | ||
bae9f6d2 JC |
631 | for _, k := range dataVal.MapKeys() { |
632 | fieldName := fmt.Sprintf("%s[%s]", name, k) | |
633 | ||
634 | // First decode the key into the proper type | |
635 | currentKey := reflect.Indirect(reflect.New(valKeyType)) | |
636 | if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { | |
637 | errors = appendErrors(errors, err) | |
638 | continue | |
639 | } | |
640 | ||
641 | // Next decode the data into the proper type | |
642 | v := dataVal.MapIndex(k).Interface() | |
643 | currentVal := reflect.Indirect(reflect.New(valElemType)) | |
644 | if err := d.decode(fieldName, v, currentVal); err != nil { | |
645 | errors = appendErrors(errors, err) | |
646 | continue | |
647 | } | |
648 | ||
649 | valMap.SetMapIndex(currentKey, currentVal) | |
650 | } | |
651 | ||
652 | // Set the built up map to the value | |
653 | val.Set(valMap) | |
654 | ||
655 | // If we had errors, return those | |
656 | if len(errors) > 0 { | |
657 | return &Error{errors} | |
658 | } | |
659 | ||
660 | return nil | |
661 | } | |
662 | ||
107c1cdb ND |
663 | func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { |
664 | typ := dataVal.Type() | |
665 | for i := 0; i < typ.NumField(); i++ { | |
666 | // Get the StructField first since this is a cheap operation. If the | |
667 | // field is unexported, then ignore it. | |
668 | f := typ.Field(i) | |
669 | if f.PkgPath != "" { | |
670 | continue | |
671 | } | |
672 | ||
673 | // Next get the actual value of this field and verify it is assignable | |
674 | // to the map value. | |
675 | v := dataVal.Field(i) | |
676 | if !v.Type().AssignableTo(valMap.Type().Elem()) { | |
677 | return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem()) | |
678 | } | |
679 | ||
680 | tagValue := f.Tag.Get(d.config.TagName) | |
681 | tagParts := strings.Split(tagValue, ",") | |
682 | ||
683 | // Determine the name of the key in the map | |
684 | keyName := f.Name | |
685 | if tagParts[0] != "" { | |
686 | if tagParts[0] == "-" { | |
687 | continue | |
688 | } | |
689 | keyName = tagParts[0] | |
690 | } | |
691 | ||
692 | // If "squash" is specified in the tag, we squash the field down. | |
693 | squash := false | |
694 | for _, tag := range tagParts[1:] { | |
695 | if tag == "squash" { | |
696 | squash = true | |
697 | break | |
698 | } | |
699 | } | |
700 | if squash && v.Kind() != reflect.Struct { | |
701 | return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) | |
702 | } | |
703 | ||
704 | switch v.Kind() { | |
705 | // this is an embedded struct, so handle it differently | |
706 | case reflect.Struct: | |
707 | x := reflect.New(v.Type()) | |
708 | x.Elem().Set(v) | |
709 | ||
710 | vType := valMap.Type() | |
711 | vKeyType := vType.Key() | |
712 | vElemType := vType.Elem() | |
713 | mType := reflect.MapOf(vKeyType, vElemType) | |
714 | vMap := reflect.MakeMap(mType) | |
715 | ||
716 | err := d.decode(keyName, x.Interface(), vMap) | |
717 | if err != nil { | |
718 | return err | |
719 | } | |
720 | ||
721 | if squash { | |
722 | for _, k := range vMap.MapKeys() { | |
723 | valMap.SetMapIndex(k, vMap.MapIndex(k)) | |
724 | } | |
725 | } else { | |
726 | valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) | |
727 | } | |
728 | ||
729 | default: | |
730 | valMap.SetMapIndex(reflect.ValueOf(keyName), v) | |
731 | } | |
732 | } | |
733 | ||
734 | if val.CanAddr() { | |
735 | val.Set(valMap) | |
736 | } | |
737 | ||
738 | return nil | |
739 | } | |
740 | ||
bae9f6d2 | 741 | func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error { |
107c1cdb ND |
742 | // If the input data is nil, then we want to just set the output |
743 | // pointer to be nil as well. | |
744 | isNil := data == nil | |
745 | if !isNil { | |
746 | switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() { | |
747 | case reflect.Chan, | |
748 | reflect.Func, | |
749 | reflect.Interface, | |
750 | reflect.Map, | |
751 | reflect.Ptr, | |
752 | reflect.Slice: | |
753 | isNil = v.IsNil() | |
754 | } | |
755 | } | |
756 | if isNil { | |
757 | if !val.IsNil() && val.CanSet() { | |
758 | nilValue := reflect.New(val.Type()).Elem() | |
759 | val.Set(nilValue) | |
760 | } | |
761 | ||
762 | return nil | |
763 | } | |
764 | ||
bae9f6d2 JC |
765 | // Create an element of the concrete (non pointer) type and decode |
766 | // into that. Then set the value of the pointer to this type. | |
767 | valType := val.Type() | |
768 | valElemType := valType.Elem() | |
107c1cdb ND |
769 | if val.CanSet() { |
770 | realVal := val | |
771 | if realVal.IsNil() || d.config.ZeroFields { | |
772 | realVal = reflect.New(valElemType) | |
773 | } | |
bae9f6d2 | 774 | |
107c1cdb ND |
775 | if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { |
776 | return err | |
777 | } | |
bae9f6d2 | 778 | |
107c1cdb ND |
779 | val.Set(realVal) |
780 | } else { | |
781 | if err := d.decode(name, data, reflect.Indirect(val)); err != nil { | |
782 | return err | |
783 | } | |
bae9f6d2 | 784 | } |
bae9f6d2 JC |
785 | return nil |
786 | } | |
787 | ||
788 | func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { | |
789 | // Create an element of the concrete (non pointer) type and decode | |
790 | // into that. Then set the value of the pointer to this type. | |
791 | dataVal := reflect.Indirect(reflect.ValueOf(data)) | |
792 | if val.Type() != dataVal.Type() { | |
793 | return fmt.Errorf( | |
794 | "'%s' expected type '%s', got unconvertible type '%s'", | |
795 | name, val.Type(), dataVal.Type()) | |
796 | } | |
797 | val.Set(dataVal) | |
798 | return nil | |
799 | } | |
800 | ||
801 | func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error { | |
802 | dataVal := reflect.Indirect(reflect.ValueOf(data)) | |
803 | dataValKind := dataVal.Kind() | |
804 | valType := val.Type() | |
805 | valElemType := valType.Elem() | |
806 | sliceType := reflect.SliceOf(valElemType) | |
807 | ||
808 | valSlice := val | |
809 | if valSlice.IsNil() || d.config.ZeroFields { | |
107c1cdb ND |
810 | if d.config.WeaklyTypedInput { |
811 | switch { | |
812 | // Slice and array we use the normal logic | |
813 | case dataValKind == reflect.Slice, dataValKind == reflect.Array: | |
814 | break | |
815 | ||
816 | // Empty maps turn into empty slices | |
817 | case dataValKind == reflect.Map: | |
818 | if dataVal.Len() == 0 { | |
819 | val.Set(reflect.MakeSlice(sliceType, 0, 0)) | |
820 | return nil | |
821 | } | |
822 | // Create slice of maps of other sizes | |
823 | return d.decodeSlice(name, []interface{}{data}, val) | |
824 | ||
825 | case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: | |
826 | return d.decodeSlice(name, []byte(dataVal.String()), val) | |
827 | ||
828 | // All other types we try to convert to the slice type | |
829 | // and "lift" it into it. i.e. a string becomes a string slice. | |
830 | default: | |
831 | // Just re-try this function with data as a slice. | |
832 | return d.decodeSlice(name, []interface{}{data}, val) | |
833 | } | |
834 | } | |
835 | ||
836 | // Check input type | |
837 | if dataValKind != reflect.Array && dataValKind != reflect.Slice { | |
838 | return fmt.Errorf( | |
839 | "'%s': source data must be an array or slice, got %s", name, dataValKind) | |
840 | ||
841 | } | |
842 | ||
843 | // If the input value is empty, then don't allocate since non-nil != nil | |
844 | if dataVal.Len() == 0 { | |
845 | return nil | |
846 | } | |
847 | ||
848 | // Make a new slice to hold our result, same size as the original data. | |
849 | valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) | |
850 | } | |
851 | ||
852 | // Accumulate any errors | |
853 | errors := make([]string, 0) | |
854 | ||
855 | for i := 0; i < dataVal.Len(); i++ { | |
856 | currentData := dataVal.Index(i).Interface() | |
857 | for valSlice.Len() <= i { | |
858 | valSlice = reflect.Append(valSlice, reflect.Zero(valElemType)) | |
859 | } | |
860 | currentField := valSlice.Index(i) | |
861 | ||
862 | fieldName := fmt.Sprintf("%s[%d]", name, i) | |
863 | if err := d.decode(fieldName, currentData, currentField); err != nil { | |
864 | errors = appendErrors(errors, err) | |
865 | } | |
866 | } | |
867 | ||
868 | // Finally, set the value to the slice we built up | |
869 | val.Set(valSlice) | |
870 | ||
871 | // If there were errors, we return those | |
872 | if len(errors) > 0 { | |
873 | return &Error{errors} | |
874 | } | |
875 | ||
876 | return nil | |
877 | } | |
878 | ||
879 | func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error { | |
880 | dataVal := reflect.Indirect(reflect.ValueOf(data)) | |
881 | dataValKind := dataVal.Kind() | |
882 | valType := val.Type() | |
883 | valElemType := valType.Elem() | |
884 | arrayType := reflect.ArrayOf(valType.Len(), valElemType) | |
885 | ||
886 | valArray := val | |
887 | ||
888 | if valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { | |
bae9f6d2 JC |
889 | // Check input type |
890 | if dataValKind != reflect.Array && dataValKind != reflect.Slice { | |
891 | if d.config.WeaklyTypedInput { | |
892 | switch { | |
107c1cdb | 893 | // Empty maps turn into empty arrays |
bae9f6d2 JC |
894 | case dataValKind == reflect.Map: |
895 | if dataVal.Len() == 0 { | |
107c1cdb | 896 | val.Set(reflect.Zero(arrayType)) |
bae9f6d2 JC |
897 | return nil |
898 | } | |
899 | ||
107c1cdb ND |
900 | // All other types we try to convert to the array type |
901 | // and "lift" it into it. i.e. a string becomes a string array. | |
bae9f6d2 JC |
902 | default: |
903 | // Just re-try this function with data as a slice. | |
107c1cdb | 904 | return d.decodeArray(name, []interface{}{data}, val) |
bae9f6d2 JC |
905 | } |
906 | } | |
907 | ||
908 | return fmt.Errorf( | |
909 | "'%s': source data must be an array or slice, got %s", name, dataValKind) | |
910 | ||
911 | } | |
107c1cdb ND |
912 | if dataVal.Len() > arrayType.Len() { |
913 | return fmt.Errorf( | |
914 | "'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len()) | |
bae9f6d2 | 915 | |
107c1cdb ND |
916 | } |
917 | ||
918 | // Make a new array to hold our result, same size as the original data. | |
919 | valArray = reflect.New(arrayType).Elem() | |
bae9f6d2 JC |
920 | } |
921 | ||
922 | // Accumulate any errors | |
923 | errors := make([]string, 0) | |
924 | ||
925 | for i := 0; i < dataVal.Len(); i++ { | |
926 | currentData := dataVal.Index(i).Interface() | |
107c1cdb | 927 | currentField := valArray.Index(i) |
bae9f6d2 JC |
928 | |
929 | fieldName := fmt.Sprintf("%s[%d]", name, i) | |
930 | if err := d.decode(fieldName, currentData, currentField); err != nil { | |
931 | errors = appendErrors(errors, err) | |
932 | } | |
933 | } | |
934 | ||
107c1cdb ND |
935 | // Finally, set the value to the array we built up |
936 | val.Set(valArray) | |
bae9f6d2 JC |
937 | |
938 | // If there were errors, we return those | |
939 | if len(errors) > 0 { | |
940 | return &Error{errors} | |
941 | } | |
942 | ||
943 | return nil | |
944 | } | |
945 | ||
946 | func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error { | |
947 | dataVal := reflect.Indirect(reflect.ValueOf(data)) | |
948 | ||
949 | // If the type of the value to write to and the data match directly, | |
950 | // then we just set it directly instead of recursing into the structure. | |
951 | if dataVal.Type() == val.Type() { | |
952 | val.Set(dataVal) | |
953 | return nil | |
954 | } | |
955 | ||
956 | dataValKind := dataVal.Kind() | |
107c1cdb ND |
957 | switch dataValKind { |
958 | case reflect.Map: | |
959 | return d.decodeStructFromMap(name, dataVal, val) | |
960 | ||
961 | case reflect.Struct: | |
962 | // Not the most efficient way to do this but we can optimize later if | |
963 | // we want to. To convert from struct to struct we go to map first | |
964 | // as an intermediary. | |
965 | m := make(map[string]interface{}) | |
966 | mval := reflect.Indirect(reflect.ValueOf(&m)) | |
967 | if err := d.decodeMapFromStruct(name, dataVal, mval, mval); err != nil { | |
968 | return err | |
969 | } | |
970 | ||
971 | result := d.decodeStructFromMap(name, mval, val) | |
972 | return result | |
973 | ||
974 | default: | |
975 | return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) | |
bae9f6d2 | 976 | } |
107c1cdb | 977 | } |
bae9f6d2 | 978 | |
107c1cdb | 979 | func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error { |
bae9f6d2 JC |
980 | dataValType := dataVal.Type() |
981 | if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { | |
982 | return fmt.Errorf( | |
983 | "'%s' needs a map with string keys, has '%s' keys", | |
984 | name, dataValType.Key().Kind()) | |
985 | } | |
986 | ||
987 | dataValKeys := make(map[reflect.Value]struct{}) | |
988 | dataValKeysUnused := make(map[interface{}]struct{}) | |
989 | for _, dataValKey := range dataVal.MapKeys() { | |
990 | dataValKeys[dataValKey] = struct{}{} | |
991 | dataValKeysUnused[dataValKey.Interface()] = struct{}{} | |
992 | } | |
993 | ||
994 | errors := make([]string, 0) | |
995 | ||
996 | // This slice will keep track of all the structs we'll be decoding. | |
997 | // There can be more than one struct if there are embedded structs | |
998 | // that are squashed. | |
999 | structs := make([]reflect.Value, 1, 5) | |
1000 | structs[0] = val | |
1001 | ||
1002 | // Compile the list of all the fields that we're going to be decoding | |
1003 | // from all the structs. | |
107c1cdb ND |
1004 | type field struct { |
1005 | field reflect.StructField | |
1006 | val reflect.Value | |
1007 | } | |
1008 | fields := []field{} | |
bae9f6d2 JC |
1009 | for len(structs) > 0 { |
1010 | structVal := structs[0] | |
1011 | structs = structs[1:] | |
1012 | ||
1013 | structType := structVal.Type() | |
1014 | ||
1015 | for i := 0; i < structType.NumField(); i++ { | |
1016 | fieldType := structType.Field(i) | |
1017 | fieldKind := fieldType.Type.Kind() | |
1018 | ||
1019 | // If "squash" is specified in the tag, we squash the field down. | |
1020 | squash := false | |
1021 | tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") | |
1022 | for _, tag := range tagParts[1:] { | |
1023 | if tag == "squash" { | |
1024 | squash = true | |
1025 | break | |
1026 | } | |
1027 | } | |
1028 | ||
1029 | if squash { | |
1030 | if fieldKind != reflect.Struct { | |
1031 | errors = appendErrors(errors, | |
1032 | fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldKind)) | |
1033 | } else { | |
107c1cdb | 1034 | structs = append(structs, structVal.FieldByName(fieldType.Name)) |
bae9f6d2 JC |
1035 | } |
1036 | continue | |
1037 | } | |
1038 | ||
1039 | // Normal struct field, store it away | |
107c1cdb | 1040 | fields = append(fields, field{fieldType, structVal.Field(i)}) |
bae9f6d2 JC |
1041 | } |
1042 | } | |
1043 | ||
107c1cdb ND |
1044 | // for fieldType, field := range fields { |
1045 | for _, f := range fields { | |
1046 | field, fieldValue := f.field, f.val | |
1047 | fieldName := field.Name | |
bae9f6d2 | 1048 | |
107c1cdb | 1049 | tagValue := field.Tag.Get(d.config.TagName) |
bae9f6d2 JC |
1050 | tagValue = strings.SplitN(tagValue, ",", 2)[0] |
1051 | if tagValue != "" { | |
1052 | fieldName = tagValue | |
1053 | } | |
1054 | ||
1055 | rawMapKey := reflect.ValueOf(fieldName) | |
1056 | rawMapVal := dataVal.MapIndex(rawMapKey) | |
1057 | if !rawMapVal.IsValid() { | |
1058 | // Do a slower search by iterating over each key and | |
1059 | // doing case-insensitive search. | |
1060 | for dataValKey := range dataValKeys { | |
1061 | mK, ok := dataValKey.Interface().(string) | |
1062 | if !ok { | |
1063 | // Not a string key | |
1064 | continue | |
1065 | } | |
1066 | ||
1067 | if strings.EqualFold(mK, fieldName) { | |
1068 | rawMapKey = dataValKey | |
1069 | rawMapVal = dataVal.MapIndex(dataValKey) | |
1070 | break | |
1071 | } | |
1072 | } | |
1073 | ||
1074 | if !rawMapVal.IsValid() { | |
1075 | // There was no matching key in the map for the value in | |
1076 | // the struct. Just ignore. | |
1077 | continue | |
1078 | } | |
1079 | } | |
1080 | ||
1081 | // Delete the key we're using from the unused map so we stop tracking | |
1082 | delete(dataValKeysUnused, rawMapKey.Interface()) | |
1083 | ||
107c1cdb | 1084 | if !fieldValue.IsValid() { |
bae9f6d2 JC |
1085 | // This should never happen |
1086 | panic("field is not valid") | |
1087 | } | |
1088 | ||
1089 | // If we can't set the field, then it is unexported or something, | |
1090 | // and we just continue onwards. | |
107c1cdb | 1091 | if !fieldValue.CanSet() { |
bae9f6d2 JC |
1092 | continue |
1093 | } | |
1094 | ||
1095 | // If the name is empty string, then we're at the root, and we | |
1096 | // don't dot-join the fields. | |
1097 | if name != "" { | |
1098 | fieldName = fmt.Sprintf("%s.%s", name, fieldName) | |
1099 | } | |
1100 | ||
107c1cdb | 1101 | if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { |
bae9f6d2 JC |
1102 | errors = appendErrors(errors, err) |
1103 | } | |
1104 | } | |
1105 | ||
1106 | if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { | |
1107 | keys := make([]string, 0, len(dataValKeysUnused)) | |
1108 | for rawKey := range dataValKeysUnused { | |
1109 | keys = append(keys, rawKey.(string)) | |
1110 | } | |
1111 | sort.Strings(keys) | |
1112 | ||
1113 | err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", ")) | |
1114 | errors = appendErrors(errors, err) | |
1115 | } | |
1116 | ||
1117 | if len(errors) > 0 { | |
1118 | return &Error{errors} | |
1119 | } | |
1120 | ||
1121 | // Add the unused keys to the list of unused keys if we're tracking metadata | |
1122 | if d.config.Metadata != nil { | |
1123 | for rawKey := range dataValKeysUnused { | |
1124 | key := rawKey.(string) | |
1125 | if name != "" { | |
1126 | key = fmt.Sprintf("%s.%s", name, key) | |
1127 | } | |
1128 | ||
1129 | d.config.Metadata.Unused = append(d.config.Metadata.Unused, key) | |
1130 | } | |
1131 | } | |
1132 | ||
1133 | return nil | |
1134 | } | |
1135 | ||
1136 | func getKind(val reflect.Value) reflect.Kind { | |
1137 | kind := val.Kind() | |
1138 | ||
1139 | switch { | |
1140 | case kind >= reflect.Int && kind <= reflect.Int64: | |
1141 | return reflect.Int | |
1142 | case kind >= reflect.Uint && kind <= reflect.Uint64: | |
1143 | return reflect.Uint | |
1144 | case kind >= reflect.Float32 && kind <= reflect.Float64: | |
1145 | return reflect.Float32 | |
1146 | default: | |
1147 | return kind | |
1148 | } | |
1149 | } |