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