validator_instance.go 22 KB

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