-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.go
188 lines (156 loc) · 4.87 KB
/
utils.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package conv
import (
"errors"
"fmt"
"math"
"reflect"
"strconv"
"time"
)
//lint:ignore U1000 The alias of the empty interface. Go 1.18 defines this but in earlier versions we can't use it.
type any = interface{}
var (
minInt int64
maxInt int64
maxUint uint64
typTime = reflect.TypeOf(time.Time{})
zeroTime = time.Time{}
// The type of map used when converting between structs and maps.
typStringMap = reflect.TypeOf(map[string]interface{}(nil))
// The type of the empty interface.
typEmptyInterface = reflect.TypeOf((*interface{})(nil)).Elem()
)
func init() {
switch strconv.IntSize {
case 32:
minInt = math.MinInt32
maxInt = math.MaxInt32
maxUint = math.MaxUint32
case 64:
minInt = math.MinInt64
maxInt = math.MaxInt64
maxUint = math.MaxUint64
}
}
// IsPrimitiveKind returns true if the given Kind is any of bool, int*, uint*, float*, complex* or string.
func IsPrimitiveKind(k reflect.Kind) bool {
// Exclude reflect.Uintptr .
return k >= reflect.Bool && k <= reflect.Uint64 ||
k >= reflect.Float32 && k <= reflect.Complex128 ||
k == reflect.String
}
// IsPrimitiveType returns true if the given type is any of bool, int*, uint*, float*, complex* or string.
func IsPrimitiveType(t reflect.Type) bool {
return t != nil && IsPrimitiveKind(t.Kind())
}
// IsSimpleType returns true if the given type IsPrimitiveType() or is convertible to time.Time .
func IsSimpleType(t reflect.Type) bool {
if t == nil {
return false
}
return IsPrimitiveType(t) || t.ConvertibleTo(typTime)
}
func isKindInt(k reflect.Kind) bool {
return k >= reflect.Int && k <= reflect.Int64
}
func isKindUint(k reflect.Kind) bool {
return k >= reflect.Uint && k <= reflect.Uint64
}
func isKindFloat(k reflect.Kind) bool {
return k == reflect.Float32 || k == reflect.Float64
}
func isKindComplex(k reflect.Kind) bool {
return k == reflect.Complex64 || k == reflect.Complex128
}
func errCantConvertTo(v interface{}, dstType string) error {
return fmt.Errorf("cannot convert %#v (%[1]T) to %s", v, dstType)
}
func errValueOverflow(v interface{}, dstType string) error {
return fmt.Errorf("value overflow when converting %#v (%[1]T) to %s", v, dstType)
}
func errPrecisionLoss(v interface{}, dstType string) error {
return fmt.Errorf("lost precision when converting %#v (%[1]T) to %s", v, dstType)
}
func errImaginaryPartLoss(v interface{}, dstType string) error {
return fmt.Errorf("lost imaginary part when converting %#v (%[1]T) to %s", v, dstType)
}
// errForFunction returns an error which is used by exported functions,
// the error message contains the function name.
func errForFunction(fn, msgFormat string, a ...interface{}) error {
msg := "conv." + fn + ": " + fmt.Sprintf(msgFormat, a...)
return errors.New(msg)
}
func errSourceShouldNotBeNil(fnName string) error {
return errForFunction(fnName, "the source value should not be nil")
}
// getFieldPath returns the path of an embedded field. Embedded pointers are supported.
// Panics on invalid parameters.
//
// e.g.
//
// type A struct { X, Y int }
// type B struct { *A }
// type C struct { B }
// type D struct { *C }
//
// getFieldPath(reflect.TypeOf(D{}), []int{0, 0, 0, 1}) //-> C.B.A.Y
func getFieldPath(typ reflect.Type, index []int) string {
var path string
for i := 0; i < len(index); i++ {
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
fs := typ.Field(index[i])
typ = fs.Type
if i > 0 {
path += "."
}
path += fs.Name
}
return path
}
// getFieldValue returns the value of the field at the given index.
// If the field is a field of an embedded pointer of a struct, and the pointer is nil, this function will try to
// initialize the value of the embedded pointer with the zero value.
//
// If the embedded pointer cannot be initialized, returns an error.
//
// If the value is nil, or is not an *addressable* struct, or the index is out of range,
// or the length of index is 0, the function panics.
func getFieldValue(val reflect.Value, index []int) (reflect.Value, error) {
ln := len(index)
if ln == 0 {
panic("index must be given")
}
for val.Kind() == reflect.Ptr {
if val.IsNil() {
panic("value is nil")
}
val = val.Elem()
}
if val.Kind() != reflect.Struct {
panic("value must be struct")
}
current := val
// It the field is embedded, ensure the parent struct has value.
for i := 0; i < ln-1; i++ {
current = current.Field(index[i])
// The field may be a nested pointer such as **struct{...}, the check should be performed recursively.
for {
if current.Kind() != reflect.Ptr {
goto next
}
if current.IsNil() {
if !current.CanSet() {
return reflect.Value{}, fmt.Errorf("cannot set embedded pointer on field %s", getFieldPath(val.Type(), index[:i+1]))
}
// Initialize with the zero value of the underlying type.
v := reflect.New(current.Type().Elem())
current.Set(v)
}
current = current.Elem()
}
next:
}
return current.Field(index[ln-1]), nil
}