-
Notifications
You must be signed in to change notification settings - Fork 3
/
get.go
135 lines (108 loc) · 2.28 KB
/
get.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package reflectutils
import (
"errors"
"fmt"
"reflect"
"strings"
)
// MustGet get value of a struct by path using reflect, return nil if anything in the path is nil
func MustGet(i interface{}, name string) (value interface{}) {
var err error
value, err = Get(i, name)
if err != nil {
panic(fmt.Sprintf("%s: %s of %+v", err, name, i))
}
return
}
func IsNil(i interface{}) bool {
v := reflect.ValueOf(i)
switch v.Kind() {
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface, reflect.Func, reflect.Chan, reflect.UnsafePointer:
return v.IsNil()
default:
}
return false
}
// Get value of a struct by path using reflect.
func Get(i interface{}, name string) (value interface{}, err error) {
// printv(i, name)
defer func() {
if r := recover(); r != nil {
err = errors.New(fmt.Sprint(r))
}
}()
if IsNil(i) {
return
}
v := reflect.ValueOf(i)
if name == "" {
value = v.Interface()
return
}
var token *dotToken
token, err = nextDot(name)
if err != nil {
return
}
sv := v
if sv.Kind() == reflect.Map {
// map must have string type
mv := sv
if mv.Type().Key() != reflect.TypeOf("") {
err = fmt.Errorf("map key %s must be string type", name)
return
}
if mv.IsNil() {
return
}
keyValue := reflect.ValueOf(token.Field)
elemType := mv.Type().Elem()
mapElem := reflect.New(elemType).Elem()
existElem := mv.MapIndex(keyValue)
if existElem.IsValid() {
mapElem.Set(existElem)
}
value, err = Get(mapElem.Interface(), token.Left)
if err != nil {
return
}
return
}
if sv.Kind() == reflect.Slice {
av := sv
if token.IsAppendingArray {
err = fmt.Errorf("array index is empty: %s", name)
return
}
if av.Len() <= token.ArrayIndex {
return
}
arrayElem := av.Index(token.ArrayIndex)
if !arrayElem.IsValid() {
return
}
value, err = Get(arrayElem.Interface(), token.Left)
if err != nil {
return
}
return
}
if sv.Kind() != reflect.Struct {
for sv.Elem().Kind() == reflect.Ptr {
sv = sv.Elem()
}
sv = sv.Elem()
}
if sv.Kind() == reflect.Struct {
fv := sv.FieldByNameFunc(func(fname string) bool {
return strings.EqualFold(fname, token.Field)
})
if !fv.IsValid() {
err = NoSuchFieldError
return
}
value, err = Get(fv.Interface(), token.Left)
return
}
return
}