-
Notifications
You must be signed in to change notification settings - Fork 1
/
text_decode.go
144 lines (119 loc) · 3.2 KB
/
text_decode.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
package gg
import (
"encoding"
r "reflect"
"strconv"
)
/*
Decodes arbitrary text into a value of the given type, using `ParseCatch`.
Panics on errors.
*/
func ParseTo[Out any, Src Text](src Src) (out Out) {
Try(ParseCatch(src, &out))
return
}
/*
Decodes arbitrary text into a value of the given type, using `ParseCatch`.
Panics on errors.
*/
func Parse[Out any, Src Text](src Src, out *Out) {
Try(ParseCatch(src, out))
}
/*
Missing feature of the standard library. Decodes arbitrary text into a value of
an arbitrary given type. The output must either implement `Parser`, or
implement `encoding.TextUnmarshaler`, or be a pointer to any of the types
described by the constraint `Textable` defined by this package. If the output
is not decodable, this returns an error.
*/
func ParseCatch[Out any, Src Text](src Src, out *Out) error {
if out == nil {
return nil
}
parser, _ := AnyNoEscUnsafe(out).(Parser)
if parser != nil {
return parser.Parse(ToString(src))
}
unmarshaler, _ := AnyNoEscUnsafe(out).(encoding.TextUnmarshaler)
if unmarshaler != nil {
return unmarshaler.UnmarshalText(ToBytes(src))
}
return ParseReflectCatch(src, r.ValueOf(AnyNoEscUnsafe(out)).Elem())
}
/*
Reflection-based component of `ParseCatch`.
Mostly for internal use.
*/
func ParseReflectCatch[A Text](src A, out r.Value) error {
typ := out.Type()
kind := typ.Kind()
switch kind {
case r.Int8, r.Int16, r.Int32, r.Int64, r.Int:
val, err := strconv.ParseInt(ToString(src), 10, typeBitSize(typ))
out.SetInt(val)
return ErrParse(err, src, typ)
case r.Uint8, r.Uint16, r.Uint32, r.Uint64, r.Uint:
val, err := strconv.ParseUint(ToString(src), 10, typeBitSize(typ))
out.SetUint(val)
return ErrParse(err, src, typ)
case r.Float32, r.Float64:
val, err := strconv.ParseFloat(ToString(src), typeBitSize(typ))
out.SetFloat(val)
return ErrParse(err, src, typ)
case r.Bool:
return parseBool(ToString(src), out)
case r.String:
out.SetString(string(src))
return nil
case r.Pointer:
if out.IsNil() {
out.Set(r.New(typ.Elem()))
}
ptr := out.Interface()
parser, _ := ptr.(Parser)
if parser != nil {
return parser.Parse(ToString(src))
}
unmarshaler, _ := ptr.(encoding.TextUnmarshaler)
if unmarshaler != nil {
return unmarshaler.UnmarshalText(ToBytes(src))
}
return ParseReflectCatch[A](src, out.Elem())
default:
if IsTypeBytes(typ) {
out.SetBytes([]byte(src))
return nil
}
return Errf(`unable to convert string to %v: unsupported kind %v`, typ, kind)
}
}
/*
Shortcut for implementing text decoding of types that wrap other types, such as
`Opt`. Mostly for internal use.
*/
func ParseClearCatch[Out any, Tar ClearerPtrGetter[Out], Src Text](src Src, tar Tar) error {
if len(src) <= 0 {
tar.Clear()
return nil
}
return ParseCatch(src, tar.Ptr())
}
/*
Shortcut for implementing `sql.Scanner` on types that wrap other types, such as
`Opt`. Mostly for internal use.
*/
func ScanCatch[Inner any, Outer Ptrer[Inner]](src any, tar Outer) error {
if src == nil {
return nil
}
ptr := tar.Ptr()
impl, _ := AnyNoEscUnsafe(ptr).(Scanner)
if impl != nil {
return impl.Scan(src)
}
str, ok := AnyToText[string](src)
if ok {
return ParseCatch(str, ptr)
}
return ErrConv(src, Type[Outer]())
}