10 // per validate contruct
11 type validate struct {
19 includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
23 // StructLevel & FieldLevel fields
24 slflParent reflect.Value
25 slCurrent reflect.Value
31 // misc reusable values
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) {
40 cs, ok := v.v.structCache.Get(typ)
42 cs = v.v.extractStructCache(current, typ.Name())
45 if len(ns) == 0 && len(cs.name) != 0 {
47 ns = append(ns, cs.name...)
50 structNs = append(structNs, cs.name...)
51 structNs = append(structNs, '.')
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 {
60 for i := 0; i < len(cs.fields); i++ {
67 // used with StructFiltered
68 if v.ffn(append(structNs, f.name...)) {
73 // used with StructPartial & StructExcept
74 _, ok = v.includeExclude[string(append(structNs, f.name...))]
76 if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
82 v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags)
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.
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) {
104 var kind reflect.Kind
106 current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
109 case reflect.Ptr, reflect.Interface, reflect.Invalid:
115 if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault {
121 v.str1 = string(append(ns, cf.altName...))
123 if v.v.hasTagNameFunc {
124 v.str2 = string(append(structNs, cf.name...))
129 if kind == reflect.Invalid {
131 v.errs = append(v.errs,
138 fieldLen: uint8(len(cf.altName)),
139 structfieldLen: uint8(len(cf.name)),
148 v.errs = append(v.errs,
155 fieldLen: uint8(len(cf.altName)),
156 structfieldLen: uint8(len(cf.name)),
157 value: current.Interface(),
175 if ct.typeof == typeStructOnly {
177 } else if ct.typeof == typeIsDefault {
178 // set Field Level fields
179 v.slflParent = parent
185 v.str1 = string(append(ns, cf.altName...))
187 if v.v.hasTagNameFunc {
188 v.str2 = string(append(structNs, cf.name...))
193 v.errs = append(v.errs,
200 fieldLen: uint8(len(cf.altName)),
201 structfieldLen: uint8(len(cf.name)),
202 value: current.Interface(),
215 if ct != nil && ct.typeof == typeNoStructLevel {
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...), '.')
229 v.validateStruct(ctx, current, current, typ, ns, structNs, ct)
250 // set Field Level fields
251 v.slflParent = parent
256 if !v.fldIsPointer && !hasValue(v) {
267 // traverse slice or map here
270 case reflect.Slice, reflect.Array:
273 reusableCF := &cField{}
275 for i := 0; i < current.Len(); i++ {
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, ']')
284 reusableCF.name = string(v.misc)
287 reusableCF.altName = reusableCF.name
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, ']')
295 reusableCF.altName = string(v.misc)
298 v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
304 reusableCF := &cField{}
306 for _, key := range current.MapKeys() {
308 pv = fmt.Sprintf("%v", key.Interface())
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, ']')
315 reusableCF.name = string(v.misc)
318 reusableCF.altName = reusableCF.name
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, ']')
325 reusableCF.altName = string(v.misc)
328 v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
332 // throw error, if not a slice or map then should not have gotten here
334 panic("dive error! can't dive on a non slice or map")
345 // set Field Level fields
346 v.slflParent = parent
353 // drain rest of the 'or' values, then continue or leave
362 if ct.typeof != typeOr {
368 v.misc = append(v.misc, '|')
369 v.misc = append(v.misc, ct.tag...)
371 if len(ct.param) > 0 {
372 v.misc = append(v.misc, '=')
373 v.misc = append(v.misc, ct.param...)
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
379 v.str1 = string(append(ns, cf.altName...))
381 if v.v.hasTagNameFunc {
382 v.str2 = string(append(structNs, cf.name...))
389 v.errs = append(v.errs,
393 actualTag: ct.actualAliasTag,
396 fieldLen: uint8(len(cf.altName)),
397 structfieldLen: uint8(len(cf.name)),
398 value: current.Interface(),
407 tVal := string(v.misc)[1:]
409 v.errs = append(v.errs,
416 fieldLen: uint8(len(cf.altName)),
417 structfieldLen: uint8(len(cf.name)),
418 value: current.Interface(),
434 // set Field Level fields
435 v.slflParent = parent
442 v.str1 = string(append(ns, cf.altName...))
444 if v.v.hasTagNameFunc {
445 v.str2 = string(append(structNs, cf.name...))
450 v.errs = append(v.errs,
457 fieldLen: uint8(len(cf.altName)),
458 structfieldLen: uint8(len(cf.name)),
459 value: current.Interface(),