Skip to content

Commit e377188

Browse files
author
zhangyu
committed
feat:增加mapstructure -> kmapstructure
1 parent 2990297 commit e377188

File tree

8 files changed

+6402
-0
lines changed

8 files changed

+6402
-0
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
package kmapstructure
2+
3+
import (
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.
16+
func 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.
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+
// 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(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+
// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
81+
// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
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+
// StringToSliceHookFunc returns a DecodeHookFunc that converts
103+
// string to []string by splitting on the given sep.
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+
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
123+
// strings to time.Duration.
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+
// Convert it by parsing
137+
return time.ParseDuration(data.(string))
138+
}
139+
}
140+
141+
// StringToIPHookFunc returns a DecodeHookFunc that converts
142+
// strings to net.IP
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+
// Convert it by parsing
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+
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
166+
// strings to net.IPNet
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+
// Convert it by parsing
180+
_, net, err := net.ParseCIDR(data.(string))
181+
return net, err
182+
}
183+
}
184+
185+
// StringToTimeHookFunc returns a DecodeHookFunc that converts
186+
// strings to time.Time.
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+
// Convert it by parsing
200+
return time.Parse(layout, data.(string))
201+
}
202+
}
203+
204+
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
205+
// the decoder.
206+
//
207+
// Note that this is significantly different from the WeaklyTypedInput option
208+
// of the DecoderConfig.
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+
// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
259+
// strings to the UnmarshalText function, when the target type
260+
// implements the encoding.TextUnmarshaler interface
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+
}

0 commit comments

Comments
 (0)