podman
709 строк · 23.8 Кб
1package validator
2
3import (
4"context"
5"errors"
6"fmt"
7"reflect"
8"strings"
9"sync"
10"time"
11
12ut "github.com/go-playground/universal-translator"
13)
14
15const (
16defaultTagName = "validate"
17utf8HexComma = "0x2C"
18utf8Pipe = "0x7C"
19tagSeparator = ","
20orSeparator = "|"
21tagKeySeparator = "="
22structOnlyTag = "structonly"
23noStructLevelTag = "nostructlevel"
24omitempty = "omitempty"
25omitnil = "omitnil"
26isdefault = "isdefault"
27requiredWithoutAllTag = "required_without_all"
28requiredWithoutTag = "required_without"
29requiredWithTag = "required_with"
30requiredWithAllTag = "required_with_all"
31requiredIfTag = "required_if"
32requiredUnlessTag = "required_unless"
33skipUnlessTag = "skip_unless"
34excludedWithoutAllTag = "excluded_without_all"
35excludedWithoutTag = "excluded_without"
36excludedWithTag = "excluded_with"
37excludedWithAllTag = "excluded_with_all"
38excludedIfTag = "excluded_if"
39excludedUnlessTag = "excluded_unless"
40skipValidationTag = "-"
41diveTag = "dive"
42keysTag = "keys"
43endKeysTag = "endkeys"
44requiredTag = "required"
45namespaceSeparator = "."
46leftBracket = "["
47rightBracket = "]"
48restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}"
49restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
50restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
51)
52
53var (
54timeDurationType = reflect.TypeOf(time.Duration(0))
55timeType = reflect.TypeOf(time.Time{})
56
57byteSliceType = reflect.TypeOf([]byte{})
58
59defaultCField = &cField{namesEqual: true}
60)
61
62// FilterFunc is the type used to filter fields using
63// StructFiltered(...) function.
64// returning true results in the field being filtered/skipped from
65// validation
66type FilterFunc func(ns []byte) bool
67
68// CustomTypeFunc allows for overriding or adding custom field type handler functions
69// field = field value of the type to return a value to be validated
70// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
71type CustomTypeFunc func(field reflect.Value) interface{}
72
73// TagNameFunc allows for adding of a custom tag name parser
74type TagNameFunc func(field reflect.StructField) string
75
76type internalValidationFuncWrapper struct {
77fn FuncCtx
78runValidatinOnNil bool
79}
80
81// Validate contains the validator settings and cache
82type Validate struct {
83tagName string
84pool *sync.Pool
85tagNameFunc TagNameFunc
86structLevelFuncs map[reflect.Type]StructLevelFuncCtx
87customFuncs map[reflect.Type]CustomTypeFunc
88aliases map[string]string
89validations map[string]internalValidationFuncWrapper
90transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
91rules map[reflect.Type]map[string]string
92tagCache *tagCache
93structCache *structCache
94hasCustomFuncs bool
95hasTagNameFunc bool
96requiredStructEnabled bool
97}
98
99// New returns a new instance of 'validate' with sane defaults.
100// Validate is designed to be thread-safe and used as a singleton instance.
101// It caches information about your struct and validations,
102// in essence only parsing your validation tags once per struct type.
103// Using multiple instances neglects the benefit of caching.
104func New(options ...Option) *Validate {
105
106tc := new(tagCache)
107tc.m.Store(make(map[string]*cTag))
108
109sc := new(structCache)
110sc.m.Store(make(map[reflect.Type]*cStruct))
111
112v := &Validate{
113tagName: defaultTagName,
114aliases: make(map[string]string, len(bakedInAliases)),
115validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)),
116tagCache: tc,
117structCache: sc,
118}
119
120// must copy alias validators for separate validations to be used in each validator instance
121for k, val := range bakedInAliases {
122v.RegisterAlias(k, val)
123}
124
125// must copy validators for separate validations to be used in each instance
126for k, val := range bakedInValidators {
127
128switch k {
129// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
130case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
131excludedIfTag, excludedUnlessTag, excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag,
132skipUnlessTag:
133_ = v.registerValidation(k, wrapFunc(val), true, true)
134default:
135// no need to error check here, baked in will always be valid
136_ = v.registerValidation(k, wrapFunc(val), true, false)
137}
138}
139
140v.pool = &sync.Pool{
141New: func() interface{} {
142return &validate{
143v: v,
144ns: make([]byte, 0, 64),
145actualNs: make([]byte, 0, 64),
146misc: make([]byte, 32),
147}
148},
149}
150
151for _, o := range options {
152o(v)
153}
154return v
155}
156
157// SetTagName allows for changing of the default tag name of 'validate'
158func (v *Validate) SetTagName(name string) {
159v.tagName = name
160}
161
162// ValidateMapCtx validates a map using a map of validation rules and allows passing of contextual
163// validation information via context.Context.
164func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
165errs := make(map[string]interface{})
166for field, rule := range rules {
167if ruleObj, ok := rule.(map[string]interface{}); ok {
168if dataObj, ok := data[field].(map[string]interface{}); ok {
169err := v.ValidateMapCtx(ctx, dataObj, ruleObj)
170if len(err) > 0 {
171errs[field] = err
172}
173} else if dataObjs, ok := data[field].([]map[string]interface{}); ok {
174for _, obj := range dataObjs {
175err := v.ValidateMapCtx(ctx, obj, ruleObj)
176if len(err) > 0 {
177errs[field] = err
178}
179}
180} else {
181errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
182}
183} else if ruleStr, ok := rule.(string); ok {
184err := v.VarCtx(ctx, data[field], ruleStr)
185if err != nil {
186errs[field] = err
187}
188}
189}
190return errs
191}
192
193// ValidateMap validates map data from a map of tags
194func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
195return v.ValidateMapCtx(context.Background(), data, rules)
196}
197
198// RegisterTagNameFunc registers a function to get alternate names for StructFields.
199//
200// eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names:
201//
202// validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
203// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
204// // skip if tag key says it should be ignored
205// if name == "-" {
206// return ""
207// }
208// return name
209// })
210func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
211v.tagNameFunc = fn
212v.hasTagNameFunc = true
213}
214
215// RegisterValidation adds a validation with the given tag
216//
217// NOTES:
218// - if the key already exists, the previous validation function will be replaced.
219// - this method is not thread-safe it is intended that these all be registered prior to any validation
220func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error {
221return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...)
222}
223
224// RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation
225// allowing context.Context validation support.
226func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error {
227var nilCheckable bool
228if len(callValidationEvenIfNull) > 0 {
229nilCheckable = callValidationEvenIfNull[0]
230}
231return v.registerValidation(tag, fn, false, nilCheckable)
232}
233
234func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
235if len(tag) == 0 {
236return errors.New("function Key cannot be empty")
237}
238
239if fn == nil {
240return errors.New("function cannot be empty")
241}
242
243_, ok := restrictedTags[tag]
244if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
245panic(fmt.Sprintf(restrictedTagErr, tag))
246}
247v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable}
248return nil
249}
250
251// RegisterAlias registers a mapping of a single validation tag that
252// defines a common or complex set of validation(s) to simplify adding validation
253// to structs.
254//
255// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
256func (v *Validate) RegisterAlias(alias, tags string) {
257
258_, ok := restrictedTags[alias]
259
260if ok || strings.ContainsAny(alias, restrictedTagChars) {
261panic(fmt.Sprintf(restrictedAliasErr, alias))
262}
263
264v.aliases[alias] = tags
265}
266
267// RegisterStructValidation registers a StructLevelFunc against a number of types.
268//
269// NOTE:
270// - this method is not thread-safe it is intended that these all be registered prior to any validation
271func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) {
272v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...)
273}
274
275// RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing
276// of contextual validation information via context.Context.
277//
278// NOTE:
279// - this method is not thread-safe it is intended that these all be registered prior to any validation
280func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) {
281
282if v.structLevelFuncs == nil {
283v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
284}
285
286for _, t := range types {
287tv := reflect.ValueOf(t)
288if tv.Kind() == reflect.Ptr {
289t = reflect.Indirect(tv).Interface()
290}
291
292v.structLevelFuncs[reflect.TypeOf(t)] = fn
293}
294}
295
296// RegisterStructValidationMapRules registers validate map rules.
297// Be aware that map validation rules supersede those defined on a/the struct if present.
298//
299// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
300func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
301if v.rules == nil {
302v.rules = make(map[reflect.Type]map[string]string)
303}
304
305deepCopyRules := make(map[string]string)
306for i, rule := range rules {
307deepCopyRules[i] = rule
308}
309
310for _, t := range types {
311typ := reflect.TypeOf(t)
312
313if typ.Kind() == reflect.Ptr {
314typ = typ.Elem()
315}
316
317if typ.Kind() != reflect.Struct {
318continue
319}
320v.rules[typ] = deepCopyRules
321}
322}
323
324// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
325//
326// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
327func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
328
329if v.customFuncs == nil {
330v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
331}
332
333for _, t := range types {
334v.customFuncs[reflect.TypeOf(t)] = fn
335}
336
337v.hasCustomFuncs = true
338}
339
340// RegisterTranslation registers translations against the provided tag.
341func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
342
343if v.transTagFunc == nil {
344v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
345}
346
347if err = registerFn(trans); err != nil {
348return
349}
350
351m, ok := v.transTagFunc[trans]
352if !ok {
353m = make(map[string]TranslationFunc)
354v.transTagFunc[trans] = m
355}
356
357m[tag] = translationFn
358
359return
360}
361
362// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
363//
364// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
365// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
366func (v *Validate) Struct(s interface{}) error {
367return v.StructCtx(context.Background(), s)
368}
369
370// StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified
371// and also allows passing of context.Context for contextual validation information.
372//
373// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
374// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
375func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
376
377val := reflect.ValueOf(s)
378top := val
379
380if val.Kind() == reflect.Ptr && !val.IsNil() {
381val = val.Elem()
382}
383
384if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
385return &InvalidValidationError{Type: reflect.TypeOf(s)}
386}
387
388// good to validate
389vd := v.pool.Get().(*validate)
390vd.top = top
391vd.isPartial = false
392// vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
393
394vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
395
396if len(vd.errs) > 0 {
397err = vd.errs
398vd.errs = nil
399}
400
401v.pool.Put(vd)
402
403return
404}
405
406// StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates
407// nested structs, unless otherwise specified.
408//
409// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
410// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
411func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) error {
412return v.StructFilteredCtx(context.Background(), s, fn)
413}
414
415// StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates
416// nested structs, unless otherwise specified and also allows passing of contextual validation information via
417// context.Context
418//
419// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
420// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
421func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn FilterFunc) (err error) {
422val := reflect.ValueOf(s)
423top := val
424
425if val.Kind() == reflect.Ptr && !val.IsNil() {
426val = val.Elem()
427}
428
429if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
430return &InvalidValidationError{Type: reflect.TypeOf(s)}
431}
432
433// good to validate
434vd := v.pool.Get().(*validate)
435vd.top = top
436vd.isPartial = true
437vd.ffn = fn
438// vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
439
440vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
441
442if len(vd.errs) > 0 {
443err = vd.errs
444vd.errs = nil
445}
446
447v.pool.Put(vd)
448
449return
450}
451
452// StructPartial validates the fields passed in only, ignoring all others.
453// Fields may be provided in a namespaced fashion relative to the struct provided
454// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
455//
456// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
457// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
458func (v *Validate) StructPartial(s interface{}, fields ...string) error {
459return v.StructPartialCtx(context.Background(), s, fields...)
460}
461
462// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
463// validation information via context.Context
464// Fields may be provided in a namespaced fashion relative to the struct provided
465// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
466//
467// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
468// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
469func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
470val := reflect.ValueOf(s)
471top := val
472
473if val.Kind() == reflect.Ptr && !val.IsNil() {
474val = val.Elem()
475}
476
477if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
478return &InvalidValidationError{Type: reflect.TypeOf(s)}
479}
480
481// good to validate
482vd := v.pool.Get().(*validate)
483vd.top = top
484vd.isPartial = true
485vd.ffn = nil
486vd.hasExcludes = false
487vd.includeExclude = make(map[string]struct{})
488
489typ := val.Type()
490name := typ.Name()
491
492for _, k := range fields {
493
494flds := strings.Split(k, namespaceSeparator)
495if len(flds) > 0 {
496
497vd.misc = append(vd.misc[0:0], name...)
498// Don't append empty name for unnamed structs
499if len(vd.misc) != 0 {
500vd.misc = append(vd.misc, '.')
501}
502
503for _, s := range flds {
504
505idx := strings.Index(s, leftBracket)
506
507if idx != -1 {
508for idx != -1 {
509vd.misc = append(vd.misc, s[:idx]...)
510vd.includeExclude[string(vd.misc)] = struct{}{}
511
512idx2 := strings.Index(s, rightBracket)
513idx2++
514vd.misc = append(vd.misc, s[idx:idx2]...)
515vd.includeExclude[string(vd.misc)] = struct{}{}
516s = s[idx2:]
517idx = strings.Index(s, leftBracket)
518}
519} else {
520
521vd.misc = append(vd.misc, s...)
522vd.includeExclude[string(vd.misc)] = struct{}{}
523}
524
525vd.misc = append(vd.misc, '.')
526}
527}
528}
529
530vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
531
532if len(vd.errs) > 0 {
533err = vd.errs
534vd.errs = nil
535}
536
537v.pool.Put(vd)
538
539return
540}
541
542// StructExcept validates all fields except the ones passed in.
543// Fields may be provided in a namespaced fashion relative to the struct provided
544// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
545//
546// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
547// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
548func (v *Validate) StructExcept(s interface{}, fields ...string) error {
549return v.StructExceptCtx(context.Background(), s, fields...)
550}
551
552// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
553// validation information via context.Context
554// Fields may be provided in a namespaced fashion relative to the struct provided
555// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
556//
557// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
558// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
559func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
560val := reflect.ValueOf(s)
561top := val
562
563if val.Kind() == reflect.Ptr && !val.IsNil() {
564val = val.Elem()
565}
566
567if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
568return &InvalidValidationError{Type: reflect.TypeOf(s)}
569}
570
571// good to validate
572vd := v.pool.Get().(*validate)
573vd.top = top
574vd.isPartial = true
575vd.ffn = nil
576vd.hasExcludes = true
577vd.includeExclude = make(map[string]struct{})
578
579typ := val.Type()
580name := typ.Name()
581
582for _, key := range fields {
583
584vd.misc = vd.misc[0:0]
585
586if len(name) > 0 {
587vd.misc = append(vd.misc, name...)
588vd.misc = append(vd.misc, '.')
589}
590
591vd.misc = append(vd.misc, key...)
592vd.includeExclude[string(vd.misc)] = struct{}{}
593}
594
595vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
596
597if len(vd.errs) > 0 {
598err = vd.errs
599vd.errs = nil
600}
601
602v.pool.Put(vd)
603
604return
605}
606
607// Var validates a single variable using tag style validation.
608// eg.
609// var i int
610// validate.Var(i, "gt=1,lt=10")
611//
612// WARNING: a struct can be passed for validation eg. time.Time is a struct or
613// if you have a custom type and have registered a custom type handler, so must
614// allow it; however unforeseen validations will occur if trying to validate a
615// struct that is meant to be passed to 'validate.Struct'
616//
617// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
618// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
619// validate Array, Slice and maps fields which may contain more than one error
620func (v *Validate) Var(field interface{}, tag string) error {
621return v.VarCtx(context.Background(), field, tag)
622}
623
624// VarCtx validates a single variable using tag style validation and allows passing of contextual
625// validation information via context.Context.
626// eg.
627// var i int
628// validate.Var(i, "gt=1,lt=10")
629//
630// WARNING: a struct can be passed for validation eg. time.Time is a struct or
631// if you have a custom type and have registered a custom type handler, so must
632// allow it; however unforeseen validations will occur if trying to validate a
633// struct that is meant to be passed to 'validate.Struct'
634//
635// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
636// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
637// validate Array, Slice and maps fields which may contain more than one error
638func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (err error) {
639if len(tag) == 0 || tag == skipValidationTag {
640return nil
641}
642
643ctag := v.fetchCacheTag(tag)
644
645val := reflect.ValueOf(field)
646vd := v.pool.Get().(*validate)
647vd.top = val
648vd.isPartial = false
649vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
650
651if len(vd.errs) > 0 {
652err = vd.errs
653vd.errs = nil
654}
655v.pool.Put(vd)
656return
657}
658
659// VarWithValue validates a single variable, against another variable/field's value using tag style validation
660// eg.
661// s1 := "abcd"
662// s2 := "abcd"
663// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
664//
665// WARNING: a struct can be passed for validation eg. time.Time is a struct or
666// if you have a custom type and have registered a custom type handler, so must
667// allow it; however unforeseen validations will occur if trying to validate a
668// struct that is meant to be passed to 'validate.Struct'
669//
670// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
671// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
672// validate Array, Slice and maps fields which may contain more than one error
673func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) error {
674return v.VarWithValueCtx(context.Background(), field, other, tag)
675}
676
677// VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and
678// allows passing of contextual validation validation information via context.Context.
679// eg.
680// s1 := "abcd"
681// s2 := "abcd"
682// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
683//
684// WARNING: a struct can be passed for validation eg. time.Time is a struct or
685// if you have a custom type and have registered a custom type handler, so must
686// allow it; however unforeseen validations will occur if trying to validate a
687// struct that is meant to be passed to 'validate.Struct'
688//
689// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
690// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
691// validate Array, Slice and maps fields which may contain more than one error
692func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other interface{}, tag string) (err error) {
693if len(tag) == 0 || tag == skipValidationTag {
694return nil
695}
696ctag := v.fetchCacheTag(tag)
697otherVal := reflect.ValueOf(other)
698vd := v.pool.Get().(*validate)
699vd.top = otherVal
700vd.isPartial = false
701vd.traverseField(ctx, otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
702
703if len(vd.errs) > 0 {
704err = vd.errs
705vd.errs = nil
706}
707v.pool.Put(vd)
708return
709}
710