-
Notifications
You must be signed in to change notification settings - Fork 22
/
reflect.go
160 lines (141 loc) · 3.72 KB
/
reflect.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package logrus_fluent
import (
"fmt"
"reflect"
"strings"
)
// ConvertToValue make map data from struct and tags
func ConvertToValue(p interface{}, tagName string) interface{} {
rv := toValue(p)
switch rv.Kind() {
case reflect.Struct:
if err, ok := p.(error); ok {
return err.Error()
}
return convertFromStruct(rv.Interface(), tagName)
case reflect.Map:
return convertFromMap(rv, tagName)
case reflect.Slice:
return convertFromSlice(rv, tagName)
case reflect.Chan:
return nil
case reflect.Invalid:
return nil
default:
return rv.Interface()
}
}
func convertFromMap(rv reflect.Value, tagName string) interface{} {
result := make(map[string]interface{})
for _, key := range rv.MapKeys() {
kv := rv.MapIndex(key)
result[fmt.Sprint(key.Interface())] = ConvertToValue(kv.Interface(), tagName)
}
return result
}
func convertFromSlice(rv reflect.Value, tagName string) interface{} {
var result []interface{}
for i, max := 0, rv.Len(); i < max; i++ {
result = append(result, ConvertToValue(rv.Index(i).Interface(), tagName))
}
return result
}
// convertFromStruct converts struct to value
// see: https://github.com/fatih/structs/
func convertFromStruct(p interface{}, tagName string) interface{} {
result := make(map[string]interface{})
return convertFromStructDeep(result, tagName, toType(p), toValue(p))
}
func convertFromStructDeep(result map[string]interface{}, tagName string, t reflect.Type, values reflect.Value) interface{} {
for i, max := 0, t.NumField(); i < max; i++ {
f := t.Field(i)
if f.PkgPath != "" && !f.Anonymous {
continue
}
if f.Anonymous {
tt := f.Type
if tt.Kind() == reflect.Ptr {
tt = tt.Elem()
}
vv := values.Field(i)
if !vv.IsValid() {
continue
}
if vv.Kind() == reflect.Ptr {
vv = vv.Elem()
}
if vv.Kind() == reflect.Struct {
convertFromStructDeep(result, tagName, tt, vv)
}
continue
}
tag, opts := parseTag(f, tagName)
if tag == "-" {
continue // skip `-` tag
}
if !values.IsValid() {
continue
}
v := values.Field(i)
if opts.Has("omitempty") && isZero(v) {
continue // skip zero-value when omitempty option exists in tag
}
name := getNameFromTag(f, tagName)
result[name] = ConvertToValue(v.Interface(), TagName)
}
return result
}
// toValue converts any value to reflect.Value
func toValue(p interface{}) reflect.Value {
v := reflect.ValueOf(p)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
return v
}
// toType converts any value to reflect.Type
func toType(p interface{}) reflect.Type {
t := reflect.ValueOf(p).Type()
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t
}
// isZero checks the value is zero-value or not
func isZero(v reflect.Value) bool {
zero := reflect.Zero(v.Type()).Interface()
value := v.Interface()
return reflect.DeepEqual(value, zero)
}
// getNameFromTag return the value in tag or field name in the struct field
func getNameFromTag(f reflect.StructField, tagName string) string {
tag, _ := parseTag(f, tagName)
if tag != "" {
return tag
}
return f.Name
}
// getTagValues returns tag value of the struct field
func getTagValues(f reflect.StructField, tag string) string {
return f.Tag.Get(tag)
}
// parseTag returns the first tag value of the struct field
func parseTag(f reflect.StructField, tag string) (string, options) {
return splitTags(getTagValues(f, tag))
}
// splitTags returns the first tag value and rest slice
func splitTags(tags string) (string, options) {
res := strings.Split(tags, ",")
return res[0], res[1:]
}
// TagOptions is wrapper struct for rest tag values
type options []string
// Has checks the value exists in the rest values or not
func (t options) Has(tag string) bool {
for _, opt := range t {
if opt == tag {
return true
}
}
return false
}