validator_instance.go 24 KB

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