]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/mitchellh/mapstructure/decode_hooks.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.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 // 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 }