validator_instance.go 24 KB

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