-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathtypes.go
425 lines (392 loc) · 10.9 KB
/
types.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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
package typ
import (
"errors"
"reflect"
"unsafe"
)
var (
// ErrBaseInvalid is returned when a base option has invalid range
ErrBaseInvalid = ErrorInvalidArgument(errors.New("base option must be in range 2 <= base <= 36"))
// ErrFmtByteInvalid is returned when a fmt byte has invalid value
ErrFmtByteInvalid = ErrorInvalidArgument(errors.New("fmtByte option must be one of 'b', 'e', 'E', 'f', 'g', 'G'"))
)
type (
intStrOpts struct {
defaultValue *string
base int
}
uintStrOpts struct {
defaultValue *string
base int
}
floatStrOpts struct {
defaultValue *string
fmtByte byte
precision int
bitSize int
}
complexStrOpts struct {
defaultValue *string
}
// Option is interface function used as argument value for type conversion configuration
Option func(*opts) error
// IntStringOption is interface function used as argument value for type conversion configuration from int to string
IntStringOption func(*intStrOpts) error
// UintStringOption is interface function used as argument value for type conversion configuration from uint to string
UintStringOption func(*uintStrOpts) error
// FloatStringOption is interface function used as argument value for type conversion configuration from float to string
FloatStringOption func(*floatStrOpts) error
// ComplexStringOption is interface function used as argument value for type conversion configuration from complex to string
ComplexStringOption func(*complexStrOpts) error
)
type opts struct {
fmtByte *byte
base, precision *int
suffix, prefix, delimiter *string
}
// IntStringDefault set default string value for int conversion to string.
func IntStringDefault(value string) IntStringOption {
return func(t *intStrOpts) error {
t.defaultValue = &value
return nil
}
}
// IntStringBase set base for int conversion to string.
// The base must be 2 <= base <= 36
// for digit values >= 10.
func IntStringBase(value int) IntStringOption {
return func(t *intStrOpts) error {
if value < 2 || value > 36 {
return ErrBaseInvalid
}
t.base = value
return nil
}
}
// UintStringDefault set default string value for uint conversion to string.
func UintStringDefault(value string) UintStringOption {
return func(t *uintStrOpts) error {
t.defaultValue = &value
return nil
}
}
// UintStringBase set base for uint conversion to string.
// The base must be 2 <= base <= 36
// for digit values >= 10.
func UintStringBase(value int) UintStringOption {
return func(t *uintStrOpts) error {
if value < 2 || value > 36 {
return ErrBaseInvalid
}
t.base = value
return nil
}
}
// FloatStringDefault set default string value for float conversion to string.
func FloatStringDefault(value string) FloatStringOption {
return func(t *floatStrOpts) error {
t.defaultValue = &value
return nil
}
}
// FloatStringFmtByte set format for float conversion to string.
// The precision prec controls the number of digits (excluding the exponent)
// printed by the 'e', 'E', 'f', 'g', and 'G' formats.
// For 'e', 'E', and 'f' it is the number of digits after the decimal point.
// For 'g' and 'G' it is the maximum number of significant digits (trailing
// zeros are removed).
// The special precision -1 uses the smallest number of digits
// necessary such that ParseFloat will return f exactly.
func FloatStringFmtByte(value byte) FloatStringOption {
return func(t *floatStrOpts) error {
switch value {
case 'b', 'e', 'E', 'f', 'g', 'G':
default:
return ErrFmtByteInvalid
}
t.fmtByte = value
return nil
}
}
// FloatStringPrecision set precision for float conversion to string.
// The precision prec controls the number of digits (excluding the exponent)
// printed by the 'e', 'E', 'f', 'g', and 'G' formats.
// For 'e', 'E', and 'f' it is the number of digits after the decimal point.
// For 'g' and 'G' it is the maximum number of significant digits (trailing
// zeros are removed).
// The special precision -1 uses the smallest number of digits
// necessary such that ParseFloat will return f exactly.
func FloatStringPrecision(value int) FloatStringOption {
return func(t *floatStrOpts) error {
t.precision = value
return nil
}
}
// FloatStringBitSize set bit size for float conversion to string.
// The bitSize must be 32 or 64
func FloatStringBitSize(value int) FloatStringOption {
return func(t *floatStrOpts) error {
t.bitSize = value
return nil
}
}
// ComplexStringDefault set default string value for complex conversion to string.
func ComplexStringDefault(value string) ComplexStringOption {
return func(t *complexStrOpts) error {
t.defaultValue = &value
return nil
}
}
// Precision set precision for float conversion.
// The precision prec controls the number of digits (excluding the exponent)
// printed by the 'e', 'E', 'f', 'g', and 'G' formats.
// For 'e', 'E', and 'f' it is the number of digits after the decimal point.
// For 'g' and 'G' it is the maximum number of significant digits (trailing
// zeros are removed).
// The special precision -1 uses the smallest number of digits
// necessary such that ParseFloat will return f exactly.
func Precision(value int) Option {
return func(t *opts) error {
t.precision = &value
return nil
}
}
// Base set base for int conversion.
// The base must be 2 <= base <= 36
// for digit values >= 10.
func Base(value int) Option {
return func(t *opts) error {
if value < 2 || value > 36 {
return ErrBaseInvalid
}
t.base = &value
return nil
}
}
// FmtByte set format for float conversion.
// The format fmt is one of
// 'b' (-ddddp±ddd, a binary exponent),
// 'e' (-d.dddde±dd, a decimal exponent),
// 'E' (-d.ddddE±dd, a decimal exponent),
// 'f' (-ddd.dddd, no exponent),
// 'g' ('e' for large exponents, 'f' otherwise), or
// 'G' ('E' for large exponents, 'f' otherwise).
func FmtByte(value byte) Option {
return func(t *opts) error {
switch value {
case 'b', 'e', 'E', 'f', 'g', 'G':
default:
return ErrFmtByteInvalid
}
t.fmtByte = &value
return nil
}
}
// Suffix set suffix for string manipulation
func Suffix(value string) Option {
return func(t *opts) error {
t.suffix = &value
return nil
}
}
// Prefix set prefix for string manipulation
func Prefix(value string) Option {
return func(t *opts) error {
t.prefix = &value
return nil
}
}
// Delimiter set delimiter for string manipulation
func Delimiter(value string) Option {
return func(t *opts) error {
t.delimiter = &value
return nil
}
}
// Type stores all information about underlying value.
type Type struct {
rv reflect.Value
kind reflect.Kind
opts opts
err error
}
// Convert "value" to any convertible primitive types
func (t *Type) to(typeTo reflect.Kind) InterfaceAccessor {
nv := &NullInterface{}
switch {
case isUint(typeTo):
v := t.toUint(typeTo)
nv.P, nv.Error = v.V(), v.Err()
return nv
case isInt(typeTo):
v := t.toInt(typeTo)
nv.P, nv.Error = v.V(), v.Err()
return nv
case isFloat(typeTo):
v := t.toFloat(typeTo)
nv.P, nv.Error = v.V(), v.Err()
return nv
case isComplex(typeTo):
v := t.toComplex(typeTo)
nv.P, nv.Error = v.V(), v.Err()
return nv
case typeTo == reflect.Bool:
v := t.Bool()
nv.P, nv.Error = v.V(), v.Err()
return nv
case typeTo == reflect.String:
v := t.String()
nv.P, nv.Error = v.V(), v.Err()
return nv
}
nv.Error = ErrConvert
return nv
}
// Empty determine whether a variable is zero
func (t *Type) Empty() BoolAccessor {
nv := &NullBool{}
from := t.rv.Kind()
switch {
case t.IsUint(true):
v := t.rv.Uint() == 0
nv.P = &v
return nv
case t.IsInt(true):
v := t.rv.Int() == 0
nv.P = &v
return nv
case t.IsFloat(true):
v := t.rv.Float() == 0
nv.P = &v
return nv
case t.IsComplex(true):
v := t.rv.Complex() == 0
nv.P = &v
return nv
case t.IsComposite(true) || from == reflect.Chan || from == reflect.String:
v := t.rv.Len() == 0
nv.P = &v
return nv
case t.IsBool(true):
v := !t.rv.Bool()
nv.P = &v
return nv
case from == reflect.Uintptr:
v := t.rv.Interface().(uintptr) == 0
nv.P = &v
return nv
case from == reflect.UnsafePointer:
v := uintptr(t.rv.Interface().(unsafe.Pointer)) == 0
nv.P = &v
return nv
}
v := !t.rv.IsValid()
nv.P = &v
if v {
nv.Error = ErrUnexpectedValue
}
return nv
}
// Equals determine whether a variable is equals with current "value" (same value, but can have different primitives types)
// Primitives type is: int, uint, float, complex, bool
func (t *Type) Equals(value interface{}) BoolAccessor {
if vp := Of(value).to(t.rv.Kind()); vp.Valid() {
value = vp.V()
}
return t.Identical(value)
}
// Identical determine whether a variable is identical with current "value" (same type and same value)
func (t *Type) Identical(src interface{}) BoolAccessor {
nv := &NullBool{}
if !t.rv.IsValid() {
nv.Error = ErrUnexpectedValue
return nv
}
v := reflect.DeepEqual(t.rv.Interface(), src)
nv.P = &v
return nv
}
// Interface returns value as interface.
// Returns nil if value can't safely represents as interface
func (t *Type) Interface() InterfaceAccessor {
nv := &NullInterface{InterfaceCommon{Error: t.err}}
if !t.rv.IsValid() {
return nv
}
nv.P = t.rv.Interface()
return nv
}
// OptionBase returns the base for numeric conversion to string
func (t *Type) OptionBase() int {
return *t.opts.base
}
// OptionFmtByte returns float format option for float conversion to string
func (t *Type) OptionFmtByte() byte {
return *t.opts.fmtByte
}
// OptionPrecision returns float precision for float conversion to string
func (t *Type) OptionPrecision() int {
return *t.opts.precision
}
// Error returns Underlying error.
func (t *Type) Error() error {
return t.err
}
// Of create type converter from interface value.
// This function recursive dereference value by a reference if value is a pointer
func Of(value interface{}, options ...Option) *Type {
return NewType(value, nil, options...)
}
var (
dBase = 10
dFmtByte = byte('f')
dPrecision = -1
)
// NewType create a Type instance with value.
// This function recursive dereference value by a reference if value is a pointer
func NewType(value interface{}, err error, options ...Option) *Type {
nt := &Type{err: err}
switch v := value.(type) {
case *Type:
nt.rv, nt.kind = v.rv, v.kind
nt.opts.fmtByte, nt.opts.base, nt.opts.precision = v.opts.fmtByte, v.opts.base, v.opts.precision
if v.err != nil && err == nil {
nt.err = v.err
}
return nt
default:
nt.rv = reflect.ValueOf(value)
nt.kind = nt.rv.Kind()
next:
for {
switch nt.rv.Kind() {
case reflect.Interface:
nt.rv = nt.rv.Elem()
case reflect.Ptr:
nt.rv = nt.rv.Elem()
case reflect.UnsafePointer, reflect.Uintptr, reflect.Invalid:
fallthrough
default:
break next
}
}
if len(options) > 0 {
for _, v := range options {
if optErr := v(&nt.opts); optErr != nil {
nt.err = optErr
break
}
}
}
if nt.opts.base == nil {
nt.opts.base = &dBase
}
if nt.opts.fmtByte == nil {
nt.opts.fmtByte = &dFmtByte
}
if nt.opts.precision == nil {
nt.opts.precision = &dPrecision
}
return nt
}
}