Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go
2880 views
1
package mapstructure
2
3
import (
4
"encoding"
5
"errors"
6
"fmt"
7
"net"
8
"net/netip"
9
"net/url"
10
"reflect"
11
"strconv"
12
"strings"
13
"time"
14
)
15
16
// typedDecodeHook takes a raw DecodeHookFunc (an any) and turns
17
// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
18
func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
19
// Create variables here so we can reference them with the reflect pkg
20
var f1 DecodeHookFuncType
21
var f2 DecodeHookFuncKind
22
var f3 DecodeHookFuncValue
23
24
// Fill in the variables into this interface and the rest is done
25
// automatically using the reflect package.
26
potential := []any{f1, f2, f3}
27
28
v := reflect.ValueOf(h)
29
vt := v.Type()
30
for _, raw := range potential {
31
pt := reflect.ValueOf(raw).Type()
32
if vt.ConvertibleTo(pt) {
33
return v.Convert(pt).Interface()
34
}
35
}
36
37
return nil
38
}
39
40
// cachedDecodeHook takes a raw DecodeHookFunc (an any) and turns
41
// it into a closure to be used directly
42
// if the type fails to convert we return a closure always erroring to keep the previous behaviour
43
func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Value) (any, error) {
44
switch f := typedDecodeHook(raw).(type) {
45
case DecodeHookFuncType:
46
return func(from reflect.Value, to reflect.Value) (any, error) {
47
return f(from.Type(), to.Type(), from.Interface())
48
}
49
case DecodeHookFuncKind:
50
return func(from reflect.Value, to reflect.Value) (any, error) {
51
return f(from.Kind(), to.Kind(), from.Interface())
52
}
53
case DecodeHookFuncValue:
54
return func(from reflect.Value, to reflect.Value) (any, error) {
55
return f(from, to)
56
}
57
default:
58
return func(from reflect.Value, to reflect.Value) (any, error) {
59
return nil, errors.New("invalid decode hook signature")
60
}
61
}
62
}
63
64
// DecodeHookExec executes the given decode hook. This should be used
65
// since it'll naturally degrade to the older backwards compatible DecodeHookFunc
66
// that took reflect.Kind instead of reflect.Type.
67
func DecodeHookExec(
68
raw DecodeHookFunc,
69
from reflect.Value, to reflect.Value,
70
) (any, error) {
71
switch f := typedDecodeHook(raw).(type) {
72
case DecodeHookFuncType:
73
return f(from.Type(), to.Type(), from.Interface())
74
case DecodeHookFuncKind:
75
return f(from.Kind(), to.Kind(), from.Interface())
76
case DecodeHookFuncValue:
77
return f(from, to)
78
default:
79
return nil, errors.New("invalid decode hook signature")
80
}
81
}
82
83
// ComposeDecodeHookFunc creates a single DecodeHookFunc that
84
// automatically composes multiple DecodeHookFuncs.
85
//
86
// The composed funcs are called in order, with the result of the
87
// previous transformation.
88
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
89
cached := make([]func(from reflect.Value, to reflect.Value) (any, error), 0, len(fs))
90
for _, f := range fs {
91
cached = append(cached, cachedDecodeHook(f))
92
}
93
return func(f reflect.Value, t reflect.Value) (any, error) {
94
var err error
95
data := f.Interface()
96
97
newFrom := f
98
for _, c := range cached {
99
data, err = c(newFrom, t)
100
if err != nil {
101
return nil, err
102
}
103
if v, ok := data.(reflect.Value); ok {
104
newFrom = v
105
} else {
106
newFrom = reflect.ValueOf(data)
107
}
108
}
109
110
return data, nil
111
}
112
}
113
114
// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
115
// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
116
func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
117
cached := make([]func(from reflect.Value, to reflect.Value) (any, error), 0, len(ff))
118
for _, f := range ff {
119
cached = append(cached, cachedDecodeHook(f))
120
}
121
return func(a, b reflect.Value) (any, error) {
122
var allErrs string
123
var out any
124
var err error
125
126
for _, c := range cached {
127
out, err = c(a, b)
128
if err != nil {
129
allErrs += err.Error() + "\n"
130
continue
131
}
132
133
return out, nil
134
}
135
136
return nil, errors.New(allErrs)
137
}
138
}
139
140
// StringToSliceHookFunc returns a DecodeHookFunc that converts
141
// string to []string by splitting on the given sep.
142
func StringToSliceHookFunc(sep string) DecodeHookFunc {
143
return func(
144
f reflect.Type,
145
t reflect.Type,
146
data any,
147
) (any, error) {
148
if f.Kind() != reflect.String {
149
return data, nil
150
}
151
if t != reflect.SliceOf(f) {
152
return data, nil
153
}
154
155
raw := data.(string)
156
if raw == "" {
157
return []string{}, nil
158
}
159
160
return strings.Split(raw, sep), nil
161
}
162
}
163
164
// StringToWeakSliceHookFunc brings back the old (pre-v2) behavior of [StringToSliceHookFunc].
165
//
166
// As of mapstructure v2.0.0 [StringToSliceHookFunc] checks if the return type is a string slice.
167
// This function removes that check.
168
func StringToWeakSliceHookFunc(sep string) DecodeHookFunc {
169
return func(
170
f reflect.Type,
171
t reflect.Type,
172
data any,
173
) (any, error) {
174
if f.Kind() != reflect.String || t.Kind() != reflect.Slice {
175
return data, nil
176
}
177
178
raw := data.(string)
179
if raw == "" {
180
return []string{}, nil
181
}
182
183
return strings.Split(raw, sep), nil
184
}
185
}
186
187
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
188
// strings to time.Duration.
189
func StringToTimeDurationHookFunc() DecodeHookFunc {
190
return func(
191
f reflect.Type,
192
t reflect.Type,
193
data any,
194
) (any, error) {
195
if f.Kind() != reflect.String {
196
return data, nil
197
}
198
if t != reflect.TypeOf(time.Duration(5)) {
199
return data, nil
200
}
201
202
// Convert it by parsing
203
d, err := time.ParseDuration(data.(string))
204
205
return d, wrapTimeParseDurationError(err)
206
}
207
}
208
209
// StringToTimeLocationHookFunc returns a DecodeHookFunc that converts
210
// strings to *time.Location.
211
func StringToTimeLocationHookFunc() DecodeHookFunc {
212
return func(
213
f reflect.Type,
214
t reflect.Type,
215
data any,
216
) (any, error) {
217
if f.Kind() != reflect.String {
218
return data, nil
219
}
220
if t != reflect.TypeOf(time.Local) {
221
return data, nil
222
}
223
d, err := time.LoadLocation(data.(string))
224
225
return d, wrapTimeParseLocationError(err)
226
}
227
}
228
229
// StringToURLHookFunc returns a DecodeHookFunc that converts
230
// strings to *url.URL.
231
func StringToURLHookFunc() DecodeHookFunc {
232
return func(
233
f reflect.Type,
234
t reflect.Type,
235
data any,
236
) (any, error) {
237
if f.Kind() != reflect.String {
238
return data, nil
239
}
240
if t != reflect.TypeOf(&url.URL{}) {
241
return data, nil
242
}
243
244
// Convert it by parsing
245
u, err := url.Parse(data.(string))
246
247
return u, wrapUrlError(err)
248
}
249
}
250
251
// StringToIPHookFunc returns a DecodeHookFunc that converts
252
// strings to net.IP
253
func StringToIPHookFunc() DecodeHookFunc {
254
return func(
255
f reflect.Type,
256
t reflect.Type,
257
data any,
258
) (any, error) {
259
if f.Kind() != reflect.String {
260
return data, nil
261
}
262
if t != reflect.TypeOf(net.IP{}) {
263
return data, nil
264
}
265
266
// Convert it by parsing
267
ip := net.ParseIP(data.(string))
268
if ip == nil {
269
return net.IP{}, fmt.Errorf("failed parsing ip")
270
}
271
272
return ip, nil
273
}
274
}
275
276
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
277
// strings to net.IPNet
278
func StringToIPNetHookFunc() DecodeHookFunc {
279
return func(
280
f reflect.Type,
281
t reflect.Type,
282
data any,
283
) (any, error) {
284
if f.Kind() != reflect.String {
285
return data, nil
286
}
287
if t != reflect.TypeOf(net.IPNet{}) {
288
return data, nil
289
}
290
291
// Convert it by parsing
292
_, net, err := net.ParseCIDR(data.(string))
293
return net, wrapNetParseError(err)
294
}
295
}
296
297
// StringToTimeHookFunc returns a DecodeHookFunc that converts
298
// strings to time.Time.
299
func StringToTimeHookFunc(layout string) DecodeHookFunc {
300
return func(
301
f reflect.Type,
302
t reflect.Type,
303
data any,
304
) (any, error) {
305
if f.Kind() != reflect.String {
306
return data, nil
307
}
308
if t != reflect.TypeOf(time.Time{}) {
309
return data, nil
310
}
311
312
// Convert it by parsing
313
ti, err := time.Parse(layout, data.(string))
314
315
return ti, wrapTimeParseError(err)
316
}
317
}
318
319
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
320
// the decoder.
321
//
322
// Note that this is significantly different from the WeaklyTypedInput option
323
// of the DecoderConfig.
324
func WeaklyTypedHook(
325
f reflect.Kind,
326
t reflect.Kind,
327
data any,
328
) (any, error) {
329
dataVal := reflect.ValueOf(data)
330
switch t {
331
case reflect.String:
332
switch f {
333
case reflect.Bool:
334
if dataVal.Bool() {
335
return "1", nil
336
}
337
return "0", nil
338
case reflect.Float32:
339
return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
340
case reflect.Int:
341
return strconv.FormatInt(dataVal.Int(), 10), nil
342
case reflect.Slice:
343
dataType := dataVal.Type()
344
elemKind := dataType.Elem().Kind()
345
if elemKind == reflect.Uint8 {
346
return string(dataVal.Interface().([]uint8)), nil
347
}
348
case reflect.Uint:
349
return strconv.FormatUint(dataVal.Uint(), 10), nil
350
}
351
}
352
353
return data, nil
354
}
355
356
func RecursiveStructToMapHookFunc() DecodeHookFunc {
357
return func(f reflect.Value, t reflect.Value) (any, error) {
358
if f.Kind() != reflect.Struct {
359
return f.Interface(), nil
360
}
361
362
var i any = struct{}{}
363
if t.Type() != reflect.TypeOf(&i).Elem() {
364
return f.Interface(), nil
365
}
366
367
m := make(map[string]any)
368
t.Set(reflect.ValueOf(m))
369
370
return f.Interface(), nil
371
}
372
}
373
374
// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
375
// strings to the UnmarshalText function, when the target type
376
// implements the encoding.TextUnmarshaler interface
377
func TextUnmarshallerHookFunc() DecodeHookFuncType {
378
return func(
379
f reflect.Type,
380
t reflect.Type,
381
data any,
382
) (any, error) {
383
if f.Kind() != reflect.String {
384
return data, nil
385
}
386
result := reflect.New(t).Interface()
387
unmarshaller, ok := result.(encoding.TextUnmarshaler)
388
if !ok {
389
return data, nil
390
}
391
str, ok := data.(string)
392
if !ok {
393
str = reflect.Indirect(reflect.ValueOf(&data)).Elem().String()
394
}
395
if err := unmarshaller.UnmarshalText([]byte(str)); err != nil {
396
return nil, err
397
}
398
return result, nil
399
}
400
}
401
402
// StringToNetIPAddrHookFunc returns a DecodeHookFunc that converts
403
// strings to netip.Addr.
404
func StringToNetIPAddrHookFunc() DecodeHookFunc {
405
return func(
406
f reflect.Type,
407
t reflect.Type,
408
data any,
409
) (any, error) {
410
if f.Kind() != reflect.String {
411
return data, nil
412
}
413
if t != reflect.TypeOf(netip.Addr{}) {
414
return data, nil
415
}
416
417
// Convert it by parsing
418
addr, err := netip.ParseAddr(data.(string))
419
420
return addr, wrapNetIPParseAddrError(err)
421
}
422
}
423
424
// StringToNetIPAddrPortHookFunc returns a DecodeHookFunc that converts
425
// strings to netip.AddrPort.
426
func StringToNetIPAddrPortHookFunc() DecodeHookFunc {
427
return func(
428
f reflect.Type,
429
t reflect.Type,
430
data any,
431
) (any, error) {
432
if f.Kind() != reflect.String {
433
return data, nil
434
}
435
if t != reflect.TypeOf(netip.AddrPort{}) {
436
return data, nil
437
}
438
439
// Convert it by parsing
440
addrPort, err := netip.ParseAddrPort(data.(string))
441
442
return addrPort, wrapNetIPParseAddrPortError(err)
443
}
444
}
445
446
// StringToNetIPPrefixHookFunc returns a DecodeHookFunc that converts
447
// strings to netip.Prefix.
448
func StringToNetIPPrefixHookFunc() DecodeHookFunc {
449
return func(
450
f reflect.Type,
451
t reflect.Type,
452
data any,
453
) (any, error) {
454
if f.Kind() != reflect.String {
455
return data, nil
456
}
457
if t != reflect.TypeOf(netip.Prefix{}) {
458
return data, nil
459
}
460
461
// Convert it by parsing
462
prefix, err := netip.ParsePrefix(data.(string))
463
464
return prefix, wrapNetIPParsePrefixError(err)
465
}
466
}
467
468
// StringToBasicTypeHookFunc returns a DecodeHookFunc that converts
469
// strings to basic types.
470
// int8, uint8, int16, uint16, int32, uint32, int64, uint64, int, uint, float32, float64, bool, byte, rune, complex64, complex128
471
func StringToBasicTypeHookFunc() DecodeHookFunc {
472
return ComposeDecodeHookFunc(
473
StringToInt8HookFunc(),
474
StringToUint8HookFunc(),
475
StringToInt16HookFunc(),
476
StringToUint16HookFunc(),
477
StringToInt32HookFunc(),
478
StringToUint32HookFunc(),
479
StringToInt64HookFunc(),
480
StringToUint64HookFunc(),
481
StringToIntHookFunc(),
482
StringToUintHookFunc(),
483
StringToFloat32HookFunc(),
484
StringToFloat64HookFunc(),
485
StringToBoolHookFunc(),
486
// byte and rune are aliases for uint8 and int32 respectively
487
// StringToByteHookFunc(),
488
// StringToRuneHookFunc(),
489
StringToComplex64HookFunc(),
490
StringToComplex128HookFunc(),
491
)
492
}
493
494
// StringToInt8HookFunc returns a DecodeHookFunc that converts
495
// strings to int8.
496
func StringToInt8HookFunc() DecodeHookFunc {
497
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
498
if f.Kind() != reflect.String || t.Kind() != reflect.Int8 {
499
return data, nil
500
}
501
502
// Convert it by parsing
503
i64, err := strconv.ParseInt(data.(string), 0, 8)
504
return int8(i64), wrapStrconvNumError(err)
505
}
506
}
507
508
// StringToUint8HookFunc returns a DecodeHookFunc that converts
509
// strings to uint8.
510
func StringToUint8HookFunc() DecodeHookFunc {
511
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
512
if f.Kind() != reflect.String || t.Kind() != reflect.Uint8 {
513
return data, nil
514
}
515
516
// Convert it by parsing
517
u64, err := strconv.ParseUint(data.(string), 0, 8)
518
return uint8(u64), wrapStrconvNumError(err)
519
}
520
}
521
522
// StringToInt16HookFunc returns a DecodeHookFunc that converts
523
// strings to int16.
524
func StringToInt16HookFunc() DecodeHookFunc {
525
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
526
if f.Kind() != reflect.String || t.Kind() != reflect.Int16 {
527
return data, nil
528
}
529
530
// Convert it by parsing
531
i64, err := strconv.ParseInt(data.(string), 0, 16)
532
return int16(i64), wrapStrconvNumError(err)
533
}
534
}
535
536
// StringToUint16HookFunc returns a DecodeHookFunc that converts
537
// strings to uint16.
538
func StringToUint16HookFunc() DecodeHookFunc {
539
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
540
if f.Kind() != reflect.String || t.Kind() != reflect.Uint16 {
541
return data, nil
542
}
543
544
// Convert it by parsing
545
u64, err := strconv.ParseUint(data.(string), 0, 16)
546
return uint16(u64), wrapStrconvNumError(err)
547
}
548
}
549
550
// StringToInt32HookFunc returns a DecodeHookFunc that converts
551
// strings to int32.
552
func StringToInt32HookFunc() DecodeHookFunc {
553
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
554
if f.Kind() != reflect.String || t.Kind() != reflect.Int32 {
555
return data, nil
556
}
557
558
// Convert it by parsing
559
i64, err := strconv.ParseInt(data.(string), 0, 32)
560
return int32(i64), wrapStrconvNumError(err)
561
}
562
}
563
564
// StringToUint32HookFunc returns a DecodeHookFunc that converts
565
// strings to uint32.
566
func StringToUint32HookFunc() DecodeHookFunc {
567
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
568
if f.Kind() != reflect.String || t.Kind() != reflect.Uint32 {
569
return data, nil
570
}
571
572
// Convert it by parsing
573
u64, err := strconv.ParseUint(data.(string), 0, 32)
574
return uint32(u64), wrapStrconvNumError(err)
575
}
576
}
577
578
// StringToInt64HookFunc returns a DecodeHookFunc that converts
579
// strings to int64.
580
func StringToInt64HookFunc() DecodeHookFunc {
581
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
582
if f.Kind() != reflect.String || t.Kind() != reflect.Int64 {
583
return data, nil
584
}
585
586
// Convert it by parsing
587
i64, err := strconv.ParseInt(data.(string), 0, 64)
588
return int64(i64), wrapStrconvNumError(err)
589
}
590
}
591
592
// StringToUint64HookFunc returns a DecodeHookFunc that converts
593
// strings to uint64.
594
func StringToUint64HookFunc() DecodeHookFunc {
595
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
596
if f.Kind() != reflect.String || t.Kind() != reflect.Uint64 {
597
return data, nil
598
}
599
600
// Convert it by parsing
601
u64, err := strconv.ParseUint(data.(string), 0, 64)
602
return uint64(u64), wrapStrconvNumError(err)
603
}
604
}
605
606
// StringToIntHookFunc returns a DecodeHookFunc that converts
607
// strings to int.
608
func StringToIntHookFunc() DecodeHookFunc {
609
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
610
if f.Kind() != reflect.String || t.Kind() != reflect.Int {
611
return data, nil
612
}
613
614
// Convert it by parsing
615
i64, err := strconv.ParseInt(data.(string), 0, 0)
616
return int(i64), wrapStrconvNumError(err)
617
}
618
}
619
620
// StringToUintHookFunc returns a DecodeHookFunc that converts
621
// strings to uint.
622
func StringToUintHookFunc() DecodeHookFunc {
623
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
624
if f.Kind() != reflect.String || t.Kind() != reflect.Uint {
625
return data, nil
626
}
627
628
// Convert it by parsing
629
u64, err := strconv.ParseUint(data.(string), 0, 0)
630
return uint(u64), wrapStrconvNumError(err)
631
}
632
}
633
634
// StringToFloat32HookFunc returns a DecodeHookFunc that converts
635
// strings to float32.
636
func StringToFloat32HookFunc() DecodeHookFunc {
637
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
638
if f.Kind() != reflect.String || t.Kind() != reflect.Float32 {
639
return data, nil
640
}
641
642
// Convert it by parsing
643
f64, err := strconv.ParseFloat(data.(string), 32)
644
return float32(f64), wrapStrconvNumError(err)
645
}
646
}
647
648
// StringToFloat64HookFunc returns a DecodeHookFunc that converts
649
// strings to float64.
650
func StringToFloat64HookFunc() DecodeHookFunc {
651
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
652
if f.Kind() != reflect.String || t.Kind() != reflect.Float64 {
653
return data, nil
654
}
655
656
// Convert it by parsing
657
f64, err := strconv.ParseFloat(data.(string), 64)
658
return f64, wrapStrconvNumError(err)
659
}
660
}
661
662
// StringToBoolHookFunc returns a DecodeHookFunc that converts
663
// strings to bool.
664
func StringToBoolHookFunc() DecodeHookFunc {
665
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
666
if f.Kind() != reflect.String || t.Kind() != reflect.Bool {
667
return data, nil
668
}
669
670
// Convert it by parsing
671
b, err := strconv.ParseBool(data.(string))
672
return b, wrapStrconvNumError(err)
673
}
674
}
675
676
// StringToByteHookFunc returns a DecodeHookFunc that converts
677
// strings to byte.
678
func StringToByteHookFunc() DecodeHookFunc {
679
return StringToUint8HookFunc()
680
}
681
682
// StringToRuneHookFunc returns a DecodeHookFunc that converts
683
// strings to rune.
684
func StringToRuneHookFunc() DecodeHookFunc {
685
return StringToInt32HookFunc()
686
}
687
688
// StringToComplex64HookFunc returns a DecodeHookFunc that converts
689
// strings to complex64.
690
func StringToComplex64HookFunc() DecodeHookFunc {
691
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
692
if f.Kind() != reflect.String || t.Kind() != reflect.Complex64 {
693
return data, nil
694
}
695
696
// Convert it by parsing
697
c128, err := strconv.ParseComplex(data.(string), 64)
698
return complex64(c128), wrapStrconvNumError(err)
699
}
700
}
701
702
// StringToComplex128HookFunc returns a DecodeHookFunc that converts
703
// strings to complex128.
704
func StringToComplex128HookFunc() DecodeHookFunc {
705
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
706
if f.Kind() != reflect.String || t.Kind() != reflect.Complex128 {
707
return data, nil
708
}
709
710
// Convert it by parsing
711
c128, err := strconv.ParseComplex(data.(string), 128)
712
return c128, wrapStrconvNumError(err)
713
}
714
}
715
716