validator.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. package validator
  2. import (
  3. "context"
  4. "fmt"
  5. "reflect"
  6. "strconv"
  7. "unsafe"
  8. )
  9. // per validate construct
  10. type validate struct {
  11. v *Validate
  12. top reflect.Value
  13. ns []byte
  14. actualNs []byte
  15. errs ValidationErrors
  16. includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
  17. ffn FilterFunc
  18. slflParent reflect.Value // StructLevel & FieldLevel
  19. slCurrent reflect.Value // StructLevel & FieldLevel
  20. flField reflect.Value // StructLevel & FieldLevel
  21. cf *cField // StructLevel & FieldLevel
  22. ct *cTag // StructLevel & FieldLevel
  23. misc []byte // misc reusable
  24. str1 string // misc reusable
  25. str2 string // misc reusable
  26. fldIsPointer bool // StructLevel & FieldLevel
  27. isPartial bool
  28. hasExcludes bool
  29. }
  30. // parent and current will be the same the first run of validateStruct
  31. func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
  32. cs, ok := v.v.structCache.Get(typ)
  33. if !ok {
  34. cs = v.v.extractStructCache(current, typ.Name())
  35. }
  36. if len(ns) == 0 && len(cs.name) != 0 {
  37. ns = append(ns, cs.name...)
  38. ns = append(ns, '.')
  39. structNs = append(structNs, cs.name...)
  40. structNs = append(structNs, '.')
  41. }
  42. // ct is nil on top level struct, and structs as fields that have no tag info
  43. // so if nil or if not nil and the structonly tag isn't present
  44. if ct == nil || ct.typeof != typeStructOnly {
  45. var f *cField
  46. for i := 0; i < len(cs.fields); i++ {
  47. f = cs.fields[i]
  48. if v.isPartial {
  49. if v.ffn != nil {
  50. // used with StructFiltered
  51. if v.ffn(append(structNs, f.name...)) {
  52. continue
  53. }
  54. } else {
  55. // used with StructPartial & StructExcept
  56. _, ok = v.includeExclude[string(append(structNs, f.name...))]
  57. if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
  58. continue
  59. }
  60. }
  61. }
  62. v.traverseField(ctx, current, current.Field(f.idx), ns, structNs, f, f.cTags)
  63. }
  64. }
  65. // check if any struct level validations, after all field validations already checked.
  66. // first iteration will have no info about nostructlevel tag, and is checked prior to
  67. // calling the next iteration of validateStruct called from traverseField.
  68. if cs.fn != nil {
  69. v.slflParent = parent
  70. v.slCurrent = current
  71. v.ns = ns
  72. v.actualNs = structNs
  73. cs.fn(ctx, v)
  74. }
  75. }
  76. // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
  77. func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
  78. var typ reflect.Type
  79. var kind reflect.Kind
  80. current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
  81. var isNestedStruct bool
  82. switch kind {
  83. case reflect.Ptr, reflect.Interface, reflect.Invalid:
  84. if ct == nil {
  85. return
  86. }
  87. if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault {
  88. return
  89. }
  90. if ct.typeof == typeOmitNil && (kind != reflect.Invalid && current.IsNil()) {
  91. return
  92. }
  93. if ct.typeof == typeOmitZero {
  94. return
  95. }
  96. if ct.hasTag {
  97. if kind == reflect.Invalid {
  98. v.str1 = string(append(ns, cf.altName...))
  99. if v.v.hasTagNameFunc {
  100. v.str2 = string(append(structNs, cf.name...))
  101. } else {
  102. v.str2 = v.str1
  103. }
  104. v.errs = append(v.errs,
  105. &fieldError{
  106. v: v.v,
  107. tag: ct.aliasTag,
  108. actualTag: ct.tag,
  109. ns: v.str1,
  110. structNs: v.str2,
  111. fieldLen: uint8(len(cf.altName)),
  112. structfieldLen: uint8(len(cf.name)),
  113. param: ct.param,
  114. kind: kind,
  115. },
  116. )
  117. return
  118. }
  119. v.str1 = string(append(ns, cf.altName...))
  120. if v.v.hasTagNameFunc {
  121. v.str2 = string(append(structNs, cf.name...))
  122. } else {
  123. v.str2 = v.str1
  124. }
  125. if !ct.runValidationWhenNil {
  126. v.errs = append(v.errs,
  127. &fieldError{
  128. v: v.v,
  129. tag: ct.aliasTag,
  130. actualTag: ct.tag,
  131. ns: v.str1,
  132. structNs: v.str2,
  133. fieldLen: uint8(len(cf.altName)),
  134. structfieldLen: uint8(len(cf.name)),
  135. value: getValue(current),
  136. param: ct.param,
  137. kind: kind,
  138. typ: current.Type(),
  139. },
  140. )
  141. return
  142. }
  143. }
  144. if kind == reflect.Invalid {
  145. return
  146. }
  147. case reflect.Struct:
  148. isNestedStruct = !current.Type().ConvertibleTo(timeType)
  149. // For backward compatibility before struct level validation tags were supported
  150. // as there were a number of projects relying on `required` not failing on non-pointer
  151. // structs. Since it's basically nonsensical to use `required` with a non-pointer struct
  152. // are explicitly skipping the required validation for it. This WILL be removed in the
  153. // next major version.
  154. if isNestedStruct && !v.v.requiredStructEnabled && ct != nil && ct.tag == requiredTag {
  155. ct = ct.next
  156. }
  157. }
  158. typ = current.Type()
  159. OUTER:
  160. for {
  161. if ct == nil || !ct.hasTag || (isNestedStruct && len(cf.name) == 0) {
  162. // isNestedStruct check here
  163. if isNestedStruct {
  164. // if len == 0 then validating using 'Var' or 'VarWithValue'
  165. // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
  166. // VarWithField - this allows for validating against each field within the struct against a specific value
  167. // pretty handy in certain situations
  168. if len(cf.name) > 0 {
  169. ns = append(append(ns, cf.altName...), '.')
  170. structNs = append(append(structNs, cf.name...), '.')
  171. }
  172. v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
  173. }
  174. return
  175. }
  176. switch ct.typeof {
  177. case typeNoStructLevel:
  178. return
  179. case typeStructOnly:
  180. if isNestedStruct {
  181. // if len == 0 then validating using 'Var' or 'VarWithValue'
  182. // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
  183. // VarWithField - this allows for validating against each field within the struct against a specific value
  184. // pretty handy in certain situations
  185. if len(cf.name) > 0 {
  186. ns = append(append(ns, cf.altName...), '.')
  187. structNs = append(append(structNs, cf.name...), '.')
  188. }
  189. v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
  190. }
  191. return
  192. case typeOmitEmpty:
  193. // set Field Level fields
  194. v.slflParent = parent
  195. v.flField = current
  196. v.cf = cf
  197. v.ct = ct
  198. if !hasValue(v) {
  199. return
  200. }
  201. ct = ct.next
  202. continue
  203. case typeOmitZero:
  204. v.slflParent = parent
  205. v.flField = current
  206. v.cf = cf
  207. v.ct = ct
  208. if !hasNotZeroValue(v) {
  209. return
  210. }
  211. ct = ct.next
  212. continue
  213. case typeOmitNil:
  214. v.slflParent = parent
  215. v.flField = current
  216. v.cf = cf
  217. v.ct = ct
  218. switch field := v.Field(); field.Kind() {
  219. case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
  220. if field.IsNil() {
  221. return
  222. }
  223. default:
  224. if v.fldIsPointer && field.Interface() == nil {
  225. return
  226. }
  227. }
  228. ct = ct.next
  229. continue
  230. case typeEndKeys:
  231. return
  232. case typeDive:
  233. ct = ct.next
  234. // traverse slice or map here
  235. // or panic ;)
  236. switch kind {
  237. case reflect.Slice, reflect.Array:
  238. var i64 int64
  239. reusableCF := &cField{}
  240. for i := 0; i < current.Len(); i++ {
  241. i64 = int64(i)
  242. v.misc = append(v.misc[0:0], cf.name...)
  243. v.misc = append(v.misc, '[')
  244. v.misc = strconv.AppendInt(v.misc, i64, 10)
  245. v.misc = append(v.misc, ']')
  246. reusableCF.name = string(v.misc)
  247. if cf.namesEqual {
  248. reusableCF.altName = reusableCF.name
  249. } else {
  250. v.misc = append(v.misc[0:0], cf.altName...)
  251. v.misc = append(v.misc, '[')
  252. v.misc = strconv.AppendInt(v.misc, i64, 10)
  253. v.misc = append(v.misc, ']')
  254. reusableCF.altName = string(v.misc)
  255. }
  256. v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
  257. }
  258. case reflect.Map:
  259. var pv string
  260. reusableCF := &cField{}
  261. for _, key := range current.MapKeys() {
  262. pv = fmt.Sprintf("%v", key.Interface())
  263. v.misc = append(v.misc[0:0], cf.name...)
  264. v.misc = append(v.misc, '[')
  265. v.misc = append(v.misc, pv...)
  266. v.misc = append(v.misc, ']')
  267. reusableCF.name = string(v.misc)
  268. if cf.namesEqual {
  269. reusableCF.altName = reusableCF.name
  270. } else {
  271. v.misc = append(v.misc[0:0], cf.altName...)
  272. v.misc = append(v.misc, '[')
  273. v.misc = append(v.misc, pv...)
  274. v.misc = append(v.misc, ']')
  275. reusableCF.altName = string(v.misc)
  276. }
  277. if ct != nil && ct.typeof == typeKeys && ct.keys != nil {
  278. v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys)
  279. // can be nil when just keys being validated
  280. if ct.next != nil {
  281. v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
  282. }
  283. } else {
  284. v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
  285. }
  286. }
  287. default:
  288. // throw error, if not a slice or map then should not have gotten here
  289. // bad dive tag
  290. panic("dive error! can't dive on a non slice or map")
  291. }
  292. return
  293. case typeOr:
  294. v.misc = v.misc[0:0]
  295. for {
  296. // set Field Level fields
  297. v.slflParent = parent
  298. v.flField = current
  299. v.cf = cf
  300. v.ct = ct
  301. if ct.fn(ctx, v) {
  302. if ct.isBlockEnd {
  303. ct = ct.next
  304. continue OUTER
  305. }
  306. // drain rest of the 'or' values, then continue or leave
  307. for {
  308. ct = ct.next
  309. if ct == nil {
  310. continue OUTER
  311. }
  312. if ct.typeof != typeOr {
  313. continue OUTER
  314. }
  315. if ct.isBlockEnd {
  316. ct = ct.next
  317. continue OUTER
  318. }
  319. }
  320. }
  321. v.misc = append(v.misc, '|')
  322. v.misc = append(v.misc, ct.tag...)
  323. if ct.hasParam {
  324. v.misc = append(v.misc, '=')
  325. v.misc = append(v.misc, ct.param...)
  326. }
  327. if ct.isBlockEnd || ct.next == nil {
  328. // if we get here, no valid 'or' value and no more tags
  329. v.str1 = string(append(ns, cf.altName...))
  330. if v.v.hasTagNameFunc {
  331. v.str2 = string(append(structNs, cf.name...))
  332. } else {
  333. v.str2 = v.str1
  334. }
  335. if ct.hasAlias {
  336. v.errs = append(v.errs,
  337. &fieldError{
  338. v: v.v,
  339. tag: ct.aliasTag,
  340. actualTag: ct.actualAliasTag,
  341. ns: v.str1,
  342. structNs: v.str2,
  343. fieldLen: uint8(len(cf.altName)),
  344. structfieldLen: uint8(len(cf.name)),
  345. value: getValue(current),
  346. param: ct.param,
  347. kind: kind,
  348. typ: typ,
  349. },
  350. )
  351. } else {
  352. tVal := string(v.misc)[1:]
  353. v.errs = append(v.errs,
  354. &fieldError{
  355. v: v.v,
  356. tag: tVal,
  357. actualTag: tVal,
  358. ns: v.str1,
  359. structNs: v.str2,
  360. fieldLen: uint8(len(cf.altName)),
  361. structfieldLen: uint8(len(cf.name)),
  362. value: getValue(current),
  363. param: ct.param,
  364. kind: kind,
  365. typ: typ,
  366. },
  367. )
  368. }
  369. return
  370. }
  371. ct = ct.next
  372. }
  373. default:
  374. // set Field Level fields
  375. v.slflParent = parent
  376. v.flField = current
  377. v.cf = cf
  378. v.ct = ct
  379. if !ct.fn(ctx, v) {
  380. v.str1 = string(append(ns, cf.altName...))
  381. if v.v.hasTagNameFunc {
  382. v.str2 = string(append(structNs, cf.name...))
  383. } else {
  384. v.str2 = v.str1
  385. }
  386. v.errs = append(v.errs,
  387. &fieldError{
  388. v: v.v,
  389. tag: ct.aliasTag,
  390. actualTag: ct.tag,
  391. ns: v.str1,
  392. structNs: v.str2,
  393. fieldLen: uint8(len(cf.altName)),
  394. structfieldLen: uint8(len(cf.name)),
  395. value: getValue(current),
  396. param: ct.param,
  397. kind: kind,
  398. typ: typ,
  399. },
  400. )
  401. return
  402. }
  403. ct = ct.next
  404. }
  405. }
  406. }
  407. func getValue(val reflect.Value) interface{} {
  408. if val.CanInterface() {
  409. return val.Interface()
  410. }
  411. if val.CanAddr() {
  412. return reflect.NewAt(val.Type(), unsafe.Pointer(val.UnsafeAddr())).Elem().Interface()
  413. }
  414. switch val.Kind() {
  415. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  416. return val.Int()
  417. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  418. return val.Uint()
  419. case reflect.Complex64, reflect.Complex128:
  420. return val.Complex()
  421. case reflect.Float32, reflect.Float64:
  422. return val.Float()
  423. default:
  424. return val.String()
  425. }
  426. }