1 package validator
2
3 import (
4 "context"
5 "fmt"
6 "reflect"
7 "strconv"
8 "unsafe"
9 )
10
11
12 type validate struct {
13 v *Validate
14 top reflect.Value
15 ns []byte
16 actualNs []byte
17 errs ValidationErrors
18 includeExclude map[string]struct{}
19 ffn FilterFunc
20 slflParent reflect.Value
21 slCurrent reflect.Value
22 flField reflect.Value
23 cf *cField
24 ct *cTag
25 misc []byte
26 str1 string
27 str2 string
28 fldIsPointer bool
29 isPartial bool
30 hasExcludes bool
31 }
32
33
34 func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
35
36 cs, ok := v.v.structCache.Get(typ)
37 if !ok {
38 cs = v.v.extractStructCache(current, typ.Name())
39 }
40
41 if len(ns) == 0 && len(cs.name) != 0 {
42
43 ns = append(ns, cs.name...)
44 ns = append(ns, '.')
45
46 structNs = append(structNs, cs.name...)
47 structNs = append(structNs, '.')
48 }
49
50
51
52 if ct == nil || ct.typeof != typeStructOnly {
53
54 var f *cField
55
56 for i := 0; i < len(cs.fields); i++ {
57
58 f = cs.fields[i]
59
60 if v.isPartial {
61
62 if v.ffn != nil {
63
64 if v.ffn(append(structNs, f.name...)) {
65 continue
66 }
67
68 } else {
69
70 _, ok = v.includeExclude[string(append(structNs, f.name...))]
71
72 if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
73 continue
74 }
75 }
76 }
77
78 v.traverseField(ctx, current, current.Field(f.idx), ns, structNs, f, f.cTags)
79 }
80 }
81
82
83
84
85 if cs.fn != nil {
86
87 v.slflParent = parent
88 v.slCurrent = current
89 v.ns = ns
90 v.actualNs = structNs
91
92 cs.fn(ctx, v)
93 }
94 }
95
96
97 func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
98 var typ reflect.Type
99 var kind reflect.Kind
100
101 current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
102
103 var isNestedStruct bool
104
105 switch kind {
106 case reflect.Ptr, reflect.Interface, reflect.Invalid:
107
108 if ct == nil {
109 return
110 }
111
112 if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault {
113 return
114 }
115
116 if ct.typeof == typeOmitNil && (kind != reflect.Invalid && current.IsNil()) {
117 return
118 }
119
120 if ct.hasTag {
121 if kind == reflect.Invalid {
122 v.str1 = string(append(ns, cf.altName...))
123 if v.v.hasTagNameFunc {
124 v.str2 = string(append(structNs, cf.name...))
125 } else {
126 v.str2 = v.str1
127 }
128 v.errs = append(v.errs,
129 &fieldError{
130 v: v.v,
131 tag: ct.aliasTag,
132 actualTag: ct.tag,
133 ns: v.str1,
134 structNs: v.str2,
135 fieldLen: uint8(len(cf.altName)),
136 structfieldLen: uint8(len(cf.name)),
137 param: ct.param,
138 kind: kind,
139 },
140 )
141 return
142 }
143
144 v.str1 = string(append(ns, cf.altName...))
145 if v.v.hasTagNameFunc {
146 v.str2 = string(append(structNs, cf.name...))
147 } else {
148 v.str2 = v.str1
149 }
150 if !ct.runValidationWhenNil {
151 v.errs = append(v.errs,
152 &fieldError{
153 v: v.v,
154 tag: ct.aliasTag,
155 actualTag: ct.tag,
156 ns: v.str1,
157 structNs: v.str2,
158 fieldLen: uint8(len(cf.altName)),
159 structfieldLen: uint8(len(cf.name)),
160 value: getValue(current),
161 param: ct.param,
162 kind: kind,
163 typ: current.Type(),
164 },
165 )
166 return
167 }
168 }
169
170 if kind == reflect.Invalid {
171 return
172 }
173
174 case reflect.Struct:
175 isNestedStruct = !current.Type().ConvertibleTo(timeType)
176
177
178
179
180
181 if isNestedStruct && !v.v.requiredStructEnabled && ct != nil && ct.tag == requiredTag {
182 ct = ct.next
183 }
184 }
185
186 typ = current.Type()
187
188 OUTER:
189 for {
190 if ct == nil || !ct.hasTag || (isNestedStruct && len(cf.name) == 0) {
191
192 if isNestedStruct {
193
194
195
196
197 if len(cf.name) > 0 {
198 ns = append(append(ns, cf.altName...), '.')
199 structNs = append(append(structNs, cf.name...), '.')
200 }
201
202 v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
203 }
204 return
205 }
206
207 switch ct.typeof {
208 case typeNoStructLevel:
209 return
210
211 case typeStructOnly:
212 if isNestedStruct {
213
214
215
216
217 if len(cf.name) > 0 {
218 ns = append(append(ns, cf.altName...), '.')
219 structNs = append(append(structNs, cf.name...), '.')
220 }
221
222 v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
223 }
224 return
225
226 case typeOmitEmpty:
227
228
229 v.slflParent = parent
230 v.flField = current
231 v.cf = cf
232 v.ct = ct
233
234 if !hasValue(v) {
235 return
236 }
237
238 ct = ct.next
239 continue
240
241 case typeOmitNil:
242 v.slflParent = parent
243 v.flField = current
244 v.cf = cf
245 v.ct = ct
246
247 switch field := v.Field(); field.Kind() {
248 case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
249 if field.IsNil() {
250 return
251 }
252 default:
253 if v.fldIsPointer && field.Interface() == nil {
254 return
255 }
256 }
257
258 ct = ct.next
259 continue
260
261 case typeEndKeys:
262 return
263
264 case typeDive:
265
266 ct = ct.next
267
268
269
270 switch kind {
271 case reflect.Slice, reflect.Array:
272
273 var i64 int64
274 reusableCF := &cField{}
275
276 for i := 0; i < current.Len(); i++ {
277
278 i64 = int64(i)
279
280 v.misc = append(v.misc[0:0], cf.name...)
281 v.misc = append(v.misc, '[')
282 v.misc = strconv.AppendInt(v.misc, i64, 10)
283 v.misc = append(v.misc, ']')
284
285 reusableCF.name = string(v.misc)
286
287 if cf.namesEqual {
288 reusableCF.altName = reusableCF.name
289 } else {
290
291 v.misc = append(v.misc[0:0], cf.altName...)
292 v.misc = append(v.misc, '[')
293 v.misc = strconv.AppendInt(v.misc, i64, 10)
294 v.misc = append(v.misc, ']')
295
296 reusableCF.altName = string(v.misc)
297 }
298 v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
299 }
300
301 case reflect.Map:
302
303 var pv string
304 reusableCF := &cField{}
305
306 for _, key := range current.MapKeys() {
307
308 pv = fmt.Sprintf("%v", key.Interface())
309
310 v.misc = append(v.misc[0:0], cf.name...)
311 v.misc = append(v.misc, '[')
312 v.misc = append(v.misc, pv...)
313 v.misc = append(v.misc, ']')
314
315 reusableCF.name = string(v.misc)
316
317 if cf.namesEqual {
318 reusableCF.altName = reusableCF.name
319 } else {
320 v.misc = append(v.misc[0:0], cf.altName...)
321 v.misc = append(v.misc, '[')
322 v.misc = append(v.misc, pv...)
323 v.misc = append(v.misc, ']')
324
325 reusableCF.altName = string(v.misc)
326 }
327
328 if ct != nil && ct.typeof == typeKeys && ct.keys != nil {
329 v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys)
330
331 if ct.next != nil {
332 v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
333 }
334 } else {
335 v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
336 }
337 }
338
339 default:
340
341
342 panic("dive error! can't dive on a non slice or map")
343 }
344
345 return
346
347 case typeOr:
348
349 v.misc = v.misc[0:0]
350
351 for {
352
353
354 v.slflParent = parent
355 v.flField = current
356 v.cf = cf
357 v.ct = ct
358
359 if ct.fn(ctx, v) {
360 if ct.isBlockEnd {
361 ct = ct.next
362 continue OUTER
363 }
364
365
366 for {
367
368 ct = ct.next
369
370 if ct == nil {
371 continue OUTER
372 }
373
374 if ct.typeof != typeOr {
375 continue OUTER
376 }
377
378 if ct.isBlockEnd {
379 ct = ct.next
380 continue OUTER
381 }
382 }
383 }
384
385 v.misc = append(v.misc, '|')
386 v.misc = append(v.misc, ct.tag...)
387
388 if ct.hasParam {
389 v.misc = append(v.misc, '=')
390 v.misc = append(v.misc, ct.param...)
391 }
392
393 if ct.isBlockEnd || ct.next == nil {
394
395 v.str1 = string(append(ns, cf.altName...))
396
397 if v.v.hasTagNameFunc {
398 v.str2 = string(append(structNs, cf.name...))
399 } else {
400 v.str2 = v.str1
401 }
402
403 if ct.hasAlias {
404
405 v.errs = append(v.errs,
406 &fieldError{
407 v: v.v,
408 tag: ct.aliasTag,
409 actualTag: ct.actualAliasTag,
410 ns: v.str1,
411 structNs: v.str2,
412 fieldLen: uint8(len(cf.altName)),
413 structfieldLen: uint8(len(cf.name)),
414 value: getValue(current),
415 param: ct.param,
416 kind: kind,
417 typ: typ,
418 },
419 )
420
421 } else {
422
423 tVal := string(v.misc)[1:]
424
425 v.errs = append(v.errs,
426 &fieldError{
427 v: v.v,
428 tag: tVal,
429 actualTag: tVal,
430 ns: v.str1,
431 structNs: v.str2,
432 fieldLen: uint8(len(cf.altName)),
433 structfieldLen: uint8(len(cf.name)),
434 value: getValue(current),
435 param: ct.param,
436 kind: kind,
437 typ: typ,
438 },
439 )
440 }
441
442 return
443 }
444
445 ct = ct.next
446 }
447
448 default:
449
450
451 v.slflParent = parent
452 v.flField = current
453 v.cf = cf
454 v.ct = ct
455
456 if !ct.fn(ctx, v) {
457 v.str1 = string(append(ns, cf.altName...))
458
459 if v.v.hasTagNameFunc {
460 v.str2 = string(append(structNs, cf.name...))
461 } else {
462 v.str2 = v.str1
463 }
464
465 v.errs = append(v.errs,
466 &fieldError{
467 v: v.v,
468 tag: ct.aliasTag,
469 actualTag: ct.tag,
470 ns: v.str1,
471 structNs: v.str2,
472 fieldLen: uint8(len(cf.altName)),
473 structfieldLen: uint8(len(cf.name)),
474 value: getValue(current),
475 param: ct.param,
476 kind: kind,
477 typ: typ,
478 },
479 )
480
481 return
482 }
483 ct = ct.next
484 }
485 }
486
487 }
488
489 func getValue(val reflect.Value) interface{} {
490 if val.CanInterface() {
491 return val.Interface()
492 }
493
494 if val.CanAddr() {
495 return reflect.NewAt(val.Type(), unsafe.Pointer(val.UnsafeAddr())).Elem().Interface()
496 }
497
498 switch val.Kind() {
499 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
500 return val.Int()
501 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
502 return val.Uint()
503 case reflect.Complex64, reflect.Complex128:
504 return val.Complex()
505 case reflect.Float32, reflect.Float64:
506 return val.Float()
507 default:
508 return val.String()
509 }
510 }
511
View as plain text