OSDN Git Service

new repo
[bytom/vapor.git] / vendor / gopkg.in / go-playground / validator.v9 / validator.go
1 package validator
2
3 import (
4         "context"
5         "fmt"
6         "reflect"
7         "strconv"
8 )
9
10 // per validate contruct
11 type validate struct {
12         v              *Validate
13         top            reflect.Value
14         ns             []byte
15         actualNs       []byte
16         errs           ValidationErrors
17         isPartial      bool
18         hasExcludes    bool
19         includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
20
21         ffn FilterFunc
22
23         // StructLevel & FieldLevel fields
24         slflParent   reflect.Value
25         slCurrent    reflect.Value
26         flField      reflect.Value
27         fldIsPointer bool
28         cf           *cField
29         ct           *cTag
30
31         // misc reusable values
32         misc []byte
33         str1 string
34         str2 string
35 }
36
37 // parent and current will be the same the first run of validateStruct
38 func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
39
40         cs, ok := v.v.structCache.Get(typ)
41         if !ok {
42                 cs = v.v.extractStructCache(current, typ.Name())
43         }
44
45         if len(ns) == 0 && len(cs.name) != 0 {
46
47                 ns = append(ns, cs.name...)
48                 ns = append(ns, '.')
49
50                 structNs = append(structNs, cs.name...)
51                 structNs = append(structNs, '.')
52         }
53
54         // ct is nil on top level struct, and structs as fields that have no tag info
55         // so if nil or if not nil and the structonly tag isn't present
56         if ct == nil || ct.typeof != typeStructOnly {
57
58                 var f *cField
59
60                 for i := 0; i < len(cs.fields); i++ {
61
62                         f = cs.fields[i]
63
64                         if v.isPartial {
65
66                                 if v.ffn != nil {
67                                         // used with StructFiltered
68                                         if v.ffn(append(structNs, f.name...)) {
69                                                 continue
70                                         }
71
72                                 } else {
73                                         // used with StructPartial & StructExcept
74                                         _, ok = v.includeExclude[string(append(structNs, f.name...))]
75
76                                         if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
77                                                 continue
78                                         }
79                                 }
80                         }
81
82                         v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags)
83                 }
84         }
85
86         // check if any struct level validations, after all field validations already checked.
87         // first iteration will have no info about nostructlevel tag, and is checked prior to
88         // calling the next iteration of validateStruct called from traverseField.
89         if cs.fn != nil {
90
91                 v.slflParent = parent
92                 v.slCurrent = current
93                 v.ns = ns
94                 v.actualNs = structNs
95
96                 cs.fn(ctx, v)
97         }
98 }
99
100 // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
101 func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
102
103         var typ reflect.Type
104         var kind reflect.Kind
105
106         current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
107
108         switch kind {
109         case reflect.Ptr, reflect.Interface, reflect.Invalid:
110
111                 if ct == nil {
112                         return
113                 }
114
115                 if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault {
116                         return
117                 }
118
119                 if ct.hasTag {
120
121                         v.str1 = string(append(ns, cf.altName...))
122
123                         if v.v.hasTagNameFunc {
124                                 v.str2 = string(append(structNs, cf.name...))
125                         } else {
126                                 v.str2 = v.str1
127                         }
128
129                         if kind == reflect.Invalid {
130
131                                 v.errs = append(v.errs,
132                                         &fieldError{
133                                                 v:              v.v,
134                                                 tag:            ct.aliasTag,
135                                                 actualTag:      ct.tag,
136                                                 ns:             v.str1,
137                                                 structNs:       v.str2,
138                                                 fieldLen:       uint8(len(cf.altName)),
139                                                 structfieldLen: uint8(len(cf.name)),
140                                                 param:          ct.param,
141                                                 kind:           kind,
142                                         },
143                                 )
144
145                                 return
146                         }
147
148                         v.errs = append(v.errs,
149                                 &fieldError{
150                                         v:              v.v,
151                                         tag:            ct.aliasTag,
152                                         actualTag:      ct.tag,
153                                         ns:             v.str1,
154                                         structNs:       v.str2,
155                                         fieldLen:       uint8(len(cf.altName)),
156                                         structfieldLen: uint8(len(cf.name)),
157                                         value:          current.Interface(),
158                                         param:          ct.param,
159                                         kind:           kind,
160                                         typ:            current.Type(),
161                                 },
162                         )
163
164                         return
165                 }
166
167         case reflect.Struct:
168
169                 typ = current.Type()
170
171                 if typ != timeType {
172
173                         if ct != nil {
174
175                                 if ct.typeof == typeStructOnly {
176                                         goto CONTINUE
177                                 } else if ct.typeof == typeIsDefault {
178                                         // set Field Level fields
179                                         v.slflParent = parent
180                                         v.flField = current
181                                         v.cf = cf
182                                         v.ct = ct
183
184                                         if !ct.fn(ctx, v) {
185                                                 v.str1 = string(append(ns, cf.altName...))
186
187                                                 if v.v.hasTagNameFunc {
188                                                         v.str2 = string(append(structNs, cf.name...))
189                                                 } else {
190                                                         v.str2 = v.str1
191                                                 }
192
193                                                 v.errs = append(v.errs,
194                                                         &fieldError{
195                                                                 v:              v.v,
196                                                                 tag:            ct.aliasTag,
197                                                                 actualTag:      ct.tag,
198                                                                 ns:             v.str1,
199                                                                 structNs:       v.str2,
200                                                                 fieldLen:       uint8(len(cf.altName)),
201                                                                 structfieldLen: uint8(len(cf.name)),
202                                                                 value:          current.Interface(),
203                                                                 param:          ct.param,
204                                                                 kind:           kind,
205                                                                 typ:            typ,
206                                                         },
207                                                 )
208                                                 return
209                                         }
210                                 }
211
212                                 ct = ct.next
213                         }
214
215                         if ct != nil && ct.typeof == typeNoStructLevel {
216                                 return
217                         }
218
219                 CONTINUE:
220                         // if len == 0 then validating using 'Var' or 'VarWithValue'
221                         // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
222                         // VarWithField - this allows for validating against each field withing the struct against a specific value
223                         //                pretty handly in certain situations
224                         if len(cf.name) > 0 {
225                                 ns = append(append(ns, cf.altName...), '.')
226                                 structNs = append(append(structNs, cf.name...), '.')
227                         }
228
229                         v.validateStruct(ctx, current, current, typ, ns, structNs, ct)
230                         return
231                 }
232         }
233
234         if !ct.hasTag {
235                 return
236         }
237
238         typ = current.Type()
239
240 OUTER:
241         for {
242                 if ct == nil {
243                         return
244                 }
245
246                 switch ct.typeof {
247
248                 case typeOmitEmpty:
249
250                         // set Field Level fields
251                         v.slflParent = parent
252                         v.flField = current
253                         v.cf = cf
254                         v.ct = ct
255
256                         if !v.fldIsPointer && !hasValue(v) {
257                                 return
258                         }
259
260                         ct = ct.next
261                         continue
262
263                 case typeDive:
264
265                         ct = ct.next
266
267                         // traverse slice or map here
268                         // or panic ;)
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
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                                         v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
329                                 }
330
331                         default:
332                                 // throw error, if not a slice or map then should not have gotten here
333                                 // bad dive tag
334                                 panic("dive error! can't dive on a non slice or map")
335                         }
336
337                         return
338
339                 case typeOr:
340
341                         v.misc = v.misc[0:0]
342
343                         for {
344
345                                 // set Field Level fields
346                                 v.slflParent = parent
347                                 v.flField = current
348                                 v.cf = cf
349                                 v.ct = ct
350
351                                 if ct.fn(ctx, v) {
352
353                                         // drain rest of the 'or' values, then continue or leave
354                                         for {
355
356                                                 ct = ct.next
357
358                                                 if ct == nil {
359                                                         return
360                                                 }
361
362                                                 if ct.typeof != typeOr {
363                                                         continue OUTER
364                                                 }
365                                         }
366                                 }
367
368                                 v.misc = append(v.misc, '|')
369                                 v.misc = append(v.misc, ct.tag...)
370
371                                 if len(ct.param) > 0 {
372                                         v.misc = append(v.misc, '=')
373                                         v.misc = append(v.misc, ct.param...)
374                                 }
375
376                                 if ct.next == nil || ct.next.typeof != typeOr { // ct.typeof != typeOr
377                                         // if we get here, no valid 'or' value and no more tags
378
379                                         v.str1 = string(append(ns, cf.altName...))
380
381                                         if v.v.hasTagNameFunc {
382                                                 v.str2 = string(append(structNs, cf.name...))
383                                         } else {
384                                                 v.str2 = v.str1
385                                         }
386
387                                         if ct.hasAlias {
388
389                                                 v.errs = append(v.errs,
390                                                         &fieldError{
391                                                                 v:              v.v,
392                                                                 tag:            ct.aliasTag,
393                                                                 actualTag:      ct.actualAliasTag,
394                                                                 ns:             v.str1,
395                                                                 structNs:       v.str2,
396                                                                 fieldLen:       uint8(len(cf.altName)),
397                                                                 structfieldLen: uint8(len(cf.name)),
398                                                                 value:          current.Interface(),
399                                                                 param:          ct.param,
400                                                                 kind:           kind,
401                                                                 typ:            typ,
402                                                         },
403                                                 )
404
405                                         } else {
406
407                                                 tVal := string(v.misc)[1:]
408
409                                                 v.errs = append(v.errs,
410                                                         &fieldError{
411                                                                 v:              v.v,
412                                                                 tag:            tVal,
413                                                                 actualTag:      tVal,
414                                                                 ns:             v.str1,
415                                                                 structNs:       v.str2,
416                                                                 fieldLen:       uint8(len(cf.altName)),
417                                                                 structfieldLen: uint8(len(cf.name)),
418                                                                 value:          current.Interface(),
419                                                                 param:          ct.param,
420                                                                 kind:           kind,
421                                                                 typ:            typ,
422                                                         },
423                                                 )
424                                         }
425
426                                         return
427                                 }
428
429                                 ct = ct.next
430                         }
431
432                 default:
433
434                         // set Field Level fields
435                         v.slflParent = parent
436                         v.flField = current
437                         v.cf = cf
438                         v.ct = ct
439
440                         if !ct.fn(ctx, v) {
441
442                                 v.str1 = string(append(ns, cf.altName...))
443
444                                 if v.v.hasTagNameFunc {
445                                         v.str2 = string(append(structNs, cf.name...))
446                                 } else {
447                                         v.str2 = v.str1
448                                 }
449
450                                 v.errs = append(v.errs,
451                                         &fieldError{
452                                                 v:              v.v,
453                                                 tag:            ct.aliasTag,
454                                                 actualTag:      ct.tag,
455                                                 ns:             v.str1,
456                                                 structNs:       v.str2,
457                                                 fieldLen:       uint8(len(cf.altName)),
458                                                 structfieldLen: uint8(len(cf.name)),
459                                                 value:          current.Interface(),
460                                                 param:          ct.param,
461                                                 kind:           kind,
462                                                 typ:            typ,
463                                         },
464                                 )
465
466                                 return
467
468                         }
469
470                         ct = ct.next
471                 }
472         }
473
474 }