-
Notifications
You must be signed in to change notification settings - Fork 0
/
process.go
187 lines (169 loc) · 4.25 KB
/
process.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
package hunkee
import (
"fmt"
"net"
"net/url"
"reflect"
"strconv"
"strings"
"time"
)
// processField gets token and parse it into corresponded type and puts into 'final' value
func (m *mapper) processField(field *field, final reflect.Value, token string) error {
v := final.FieldByIndex(field.index)
if v.Kind() == reflect.Ptr && v.IsNil() {
// Allocate memory
v.Set(reflect.New(deref(v.Type())))
}
// set raw value
if field.hasRaw {
raw := m.raw(field)
if raw == nil {
panic(fmt.Sprintf("%s field should have raw field, but it's not provided", field.name))
}
final.Field(raw.index[0]).Set(reflect.ValueOf(token))
}
// nothing to process, but if it's string, we should set token to the field
if token == "-" {
if field.reflectKind == reflect.String {
v.SetString(token)
}
return nil
}
switch field.reflectKind {
case reflect.Bool:
b, err := strconv.ParseBool(token)
if err != nil {
return err
}
v.SetBool(b)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i64, err := parseInt(field.reflectKind, token)
if err != nil && token != "" {
return fmt.Errorf("field: %s parse: %s", field.name, err)
}
v.SetInt(i64)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
ui64, err := parseUint(field.reflectKind, token)
if err != nil && token != "" {
return fmt.Errorf("field: %s parse: %s", field.name, err)
}
v.SetUint(ui64)
case reflect.String:
v.SetString(token)
case reflect.Float32, reflect.Float64:
fl64, err := parseFloat(field.reflectKind, token)
if err != nil && token != "" {
return fmt.Errorf("field: %s parse: %s", field.name, err)
}
v.SetFloat(fl64)
case reflect.Struct:
if err := parseStringToStruct(v, token, field); err != nil {
return fmt.Errorf("field: %s parse: %s", field.name, err)
}
default:
if field.ftype == typeIP {
ip := net.ParseIP(token)
v.Set(reflect.ValueOf(ip))
} else {
return fmt.Errorf("type %+v is not supported", field)
}
}
return nil
}
func parseUint(kind reflect.Kind, token string) (uint64, error) {
var size int
switch kind {
case reflect.Uint8:
size = 8
case reflect.Uint16:
size = 16
case reflect.Uint32:
size = 32
case reflect.Uint64, reflect.Uint:
size = 64
default:
return 0, ErrNotUint
}
return strconv.ParseUint(token, 10, size)
}
func parseInt(kind reflect.Kind, token string) (int64, error) {
var size int
switch kind {
case reflect.Int8:
size = 8
case reflect.Int16:
size = 16
case reflect.Int32:
size = 32
case reflect.Int, reflect.Int64:
size = 64
default:
return 0, ErrNotInt
}
return strconv.ParseInt(token, 10, size)
}
func parseFloat(kind reflect.Kind, token string) (float64, error) {
var size int
switch kind {
case reflect.Float32:
size = 32
case reflect.Float64:
size = 64
default:
return 0, ErrNotFloat
}
return strconv.ParseFloat(token, size)
}
// parseStringToStruct gets token and parses it into
// net.Addr, time.Time, time.Duration, url.URL
func parseStringToStruct(v reflect.Value, token string, field *field) (err error) {
switch field.ftype {
case typeTime:
var t time.Time
if field.timeOptions == nil {
return ErrNilTimeOptions
}
if field.timeOptions.Location == nil {
t, err = time.Parse(field.timeOptions.Layout, token)
} else {
t, err = time.ParseInLocation(field.timeOptions.Layout, token, field.timeOptions.Location)
}
if err != nil {
return err
}
v.Set(reflect.ValueOf(t))
case typeURL:
u, err := url.Parse(token)
if err != nil {
return err
}
v.Set(reflect.ValueOf(u))
case typeDuration:
d, err := time.ParseDuration(token)
if err != nil {
return err
}
v.Set(reflect.ValueOf(d))
default:
return fmt.Errorf("type %s is not supported: ftype: %d name: %s", field.reflectType, field.ftype, field.name)
}
return nil
}
// processTag returns full tag, normalName aka not raw name and error, if exists
func processTag(tagLine reflect.StructTag) (tag, normalName string, err error) {
var ok bool
tag, ok = tagLine.Lookup(libtag)
if !ok || tag == "" || tag == unexportedTag {
tag = unexportedTag
return
}
if strings.ContainsAny(tag, ".,") {
err = ErrComaNotSupported
return
}
if strings.HasSuffix(tag, "_raw") {
normalName = strings.TrimSuffix(tag, "_raw")
}
return
}