validator_instance.go 24 KB

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