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