1package mapstructure 2 3import ( 4 "encoding" 5 "errors" 6 "fmt" 7 "net" 8 "reflect" 9 "strconv" 10 "strings" 11 "time" 12) 13 14// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns 15// it into the proper DecodeHookFunc type, such as DecodeHookFuncType. 16func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { 17 // Create variables here so we can reference them with the reflect pkg 18 var f1 DecodeHookFuncType 19 var f2 DecodeHookFuncKind 20 var f3 DecodeHookFuncValue 21 22 // Fill in the variables into this interface and the rest is done 23 // automatically using the reflect package. 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// DecodeHookExec executes the given decode hook. This should be used 39// since it'll naturally degrade to the older backwards compatible DecodeHookFunc 40// that took reflect.Kind instead of reflect.Type. 41func 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// 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. 62func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { 63 return func(f reflect.Value, t reflect.Value) (interface{}, error) { 64 var err error 65 var data interface{} 66 newFrom := f 67 for _, f1 := range fs { 68 data, err = DecodeHookExec(f1, newFrom, t) 69 if err != nil { 70 return nil, err 71 } 72 newFrom = reflect.ValueOf(data) 73 } 74 75 return data, nil 76 } 77} 78 79// StringToSliceHookFunc returns a DecodeHookFunc that converts 80// string to []string by splitting on the given sep. 81func StringToSliceHookFunc(sep string) DecodeHookFunc { 82 return func( 83 f reflect.Kind, 84 t reflect.Kind, 85 data interface{}) (interface{}, error) { 86 if f != reflect.String || t != reflect.Slice { 87 return data, nil 88 } 89 90 raw := data.(string) 91 if raw == "" { 92 return []string{}, nil 93 } 94 95 return strings.Split(raw, sep), nil 96 } 97} 98 99// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts 100// strings to time.Duration. 101func StringToTimeDurationHookFunc() DecodeHookFunc { 102 return func( 103 f reflect.Type, 104 t reflect.Type, 105 data interface{}) (interface{}, error) { 106 if f.Kind() != reflect.String { 107 return data, nil 108 } 109 if t != reflect.TypeOf(time.Duration(5)) { 110 return data, nil 111 } 112 113 // Convert it by parsing 114 return time.ParseDuration(data.(string)) 115 } 116} 117 118// StringToIPHookFunc returns a DecodeHookFunc that converts 119// strings to net.IP 120func StringToIPHookFunc() DecodeHookFunc { 121 return func( 122 f reflect.Type, 123 t reflect.Type, 124 data interface{}) (interface{}, error) { 125 if f.Kind() != reflect.String { 126 return data, nil 127 } 128 if t != reflect.TypeOf(net.IP{}) { 129 return data, nil 130 } 131 132 // Convert it by parsing 133 ip := net.ParseIP(data.(string)) 134 if ip == nil { 135 return net.IP{}, fmt.Errorf("failed parsing ip %v", data) 136 } 137 138 return ip, nil 139 } 140} 141 142// StringToIPNetHookFunc returns a DecodeHookFunc that converts 143// strings to net.IPNet 144func StringToIPNetHookFunc() DecodeHookFunc { 145 return func( 146 f reflect.Type, 147 t reflect.Type, 148 data interface{}) (interface{}, error) { 149 if f.Kind() != reflect.String { 150 return data, nil 151 } 152 if t != reflect.TypeOf(net.IPNet{}) { 153 return data, nil 154 } 155 156 // Convert it by parsing 157 _, net, err := net.ParseCIDR(data.(string)) 158 return net, err 159 } 160} 161 162// StringToTimeHookFunc returns a DecodeHookFunc that converts 163// strings to time.Time. 164func StringToTimeHookFunc(layout string) DecodeHookFunc { 165 return func( 166 f reflect.Type, 167 t reflect.Type, 168 data interface{}) (interface{}, error) { 169 if f.Kind() != reflect.String { 170 return data, nil 171 } 172 if t != reflect.TypeOf(time.Time{}) { 173 return data, nil 174 } 175 176 // Convert it by parsing 177 return time.Parse(layout, data.(string)) 178 } 179} 180 181// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to 182// the decoder. 183// 184// Note that this is significantly different from the WeaklyTypedInput option 185// of the DecoderConfig. 186func WeaklyTypedHook( 187 f reflect.Kind, 188 t reflect.Kind, 189 data interface{}) (interface{}, error) { 190 dataVal := reflect.ValueOf(data) 191 switch t { 192 case reflect.String: 193 switch f { 194 case reflect.Bool: 195 if dataVal.Bool() { 196 return "1", nil 197 } 198 return "0", nil 199 case reflect.Float32: 200 return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil 201 case reflect.Int: 202 return strconv.FormatInt(dataVal.Int(), 10), nil 203 case reflect.Slice: 204 dataType := dataVal.Type() 205 elemKind := dataType.Elem().Kind() 206 if elemKind == reflect.Uint8 { 207 return string(dataVal.Interface().([]uint8)), nil 208 } 209 case reflect.Uint: 210 return strconv.FormatUint(dataVal.Uint(), 10), nil 211 } 212 } 213 214 return data, nil 215} 216 217func RecursiveStructToMapHookFunc() DecodeHookFunc { 218 return func(f reflect.Value, t reflect.Value) (interface{}, error) { 219 if f.Kind() != reflect.Struct { 220 return f.Interface(), nil 221 } 222 223 var i interface{} = struct{}{} 224 if t.Type() != reflect.TypeOf(&i).Elem() { 225 return f.Interface(), nil 226 } 227 228 m := make(map[string]interface{}) 229 t.Set(reflect.ValueOf(m)) 230 231 return f.Interface(), nil 232 } 233} 234 235// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies 236// strings to the UnmarshalText function, when the target type 237// implements the encoding.TextUnmarshaler interface 238func TextUnmarshallerHookFunc() DecodeHookFuncType { 239 return func( 240 f reflect.Type, 241 t reflect.Type, 242 data interface{}) (interface{}, error) { 243 if f.Kind() != reflect.String { 244 return data, nil 245 } 246 result := reflect.New(t).Interface() 247 unmarshaller, ok := result.(encoding.TextUnmarshaler) 248 if !ok { 249 return data, nil 250 } 251 if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil { 252 return nil, err 253 } 254 return result, nil 255 } 256} 257