validator_instance.go 21 KB

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