validator_instance.go 21 KB

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