validator_instance.go raw

   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