-
Notifications
You must be signed in to change notification settings - Fork 273
/
retrieve.go
146 lines (116 loc) · 3.4 KB
/
retrieve.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
136
137
138
139
140
141
142
143
144
145
146
package funk
import (
"reflect"
"strings"
)
// Get retrieves the value from given path, retriever can be modified with available RetrieverOptions
func Get(out interface{}, path string, opts ...option) interface{} {
options := newOptions(opts...)
result := get(reflect.ValueOf(out), path, opts...)
// valid kind and we can return a result.Interface() without panic
if result.Kind() != reflect.Invalid && result.CanInterface() {
// if we don't allow zero and the result is a zero value return nil
if !options.allowZero && result.IsZero() {
return nil
}
// if the result kind is a pointer and its nil return nil
if result.Kind() == reflect.Ptr && result.IsNil() {
return nil
}
// return the result interface (i.e the zero value of it)
return result.Interface()
}
return nil
}
// GetOrElse retrieves the value of the pointer or default.
func GetOrElse(v interface{}, def interface{}) interface{} {
val := reflect.ValueOf(v)
if v == nil || (val.Kind() == reflect.Ptr && val.IsNil()) {
return def
} else if val.Kind() != reflect.Ptr {
return v
}
return val.Elem().Interface()
}
func get(value reflect.Value, path string, opts ...option) reflect.Value {
options := newOptions(opts...)
if value.Kind() == reflect.Slice || value.Kind() == reflect.Array {
var resultSlice reflect.Value
length := value.Len()
if length == 0 {
zeroElement := reflect.Zero(value.Type().Elem())
pathValue := get(zeroElement, path)
value = reflect.MakeSlice(reflect.SliceOf(pathValue.Type()), 0, 0)
return value
}
for i := 0; i < length; i++ {
item := value.Index(i)
resultValue := get(item, path)
if resultValue.Kind() == reflect.Invalid || (resultValue.IsZero() && !options.allowZero) {
continue
}
resultType := resultValue.Type()
if resultSlice.Kind() == reflect.Invalid {
resultType := reflect.SliceOf(resultType)
resultSlice = reflect.MakeSlice(resultType, 0, 0)
}
resultSlice = reflect.Append(resultSlice, resultValue)
}
// if the result is a slice of a slice, we need to flatten it
if resultSlice.Kind() != reflect.Invalid && resultSlice.Type().Elem().Kind() == reflect.Slice {
return flattenDeep(resultSlice)
}
return resultSlice
}
quoted := false
parts := strings.FieldsFunc(path, func(r rune) bool {
if r == '"' {
quoted = !quoted
}
return !quoted && r == '.'
})
for i, part := range parts {
parts[i] = strings.Trim(part, "\"")
}
for _, part := range parts {
value = redirectValue(value)
kind := value.Kind()
switch kind {
case reflect.Invalid:
continue
case reflect.Struct:
if isNilIndirection(value, part) {
return reflect.ValueOf(nil)
}
value = value.FieldByName(part)
case reflect.Map:
value = value.MapIndex(reflect.ValueOf(part))
case reflect.Slice, reflect.Array:
value = get(value, part)
default:
return reflect.ValueOf(nil)
}
}
return value
}
func isNilIndirection(v reflect.Value, name string) bool {
vType := v.Type()
for i := 0; i < vType.NumField(); i++ {
field := vType.Field(i)
if !isEmbeddedStructPointerField(field) {
return false
}
fieldType := field.Type.Elem()
_, found := fieldType.FieldByName(name)
if found {
return v.Field(i).IsNil()
}
}
return false
}
func isEmbeddedStructPointerField(field reflect.StructField) bool {
if !field.Anonymous {
return false
}
return field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct
}