validator.go 11 KB

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