validator.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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. v.str1 = string(append(ns, cf.altName...))
  90. if v.v.hasTagNameFunc {
  91. v.str2 = string(append(structNs, cf.name...))
  92. } else {
  93. v.str2 = v.str1
  94. }
  95. if kind == reflect.Invalid {
  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.errs = append(v.errs,
  112. &fieldError{
  113. v: v.v,
  114. tag: ct.aliasTag,
  115. actualTag: ct.tag,
  116. ns: v.str1,
  117. structNs: v.str2,
  118. fieldLen: uint8(len(cf.altName)),
  119. structfieldLen: uint8(len(cf.name)),
  120. value: current.Interface(),
  121. param: ct.param,
  122. kind: kind,
  123. typ: current.Type(),
  124. },
  125. )
  126. return
  127. }
  128. case reflect.Struct:
  129. typ = current.Type()
  130. if typ != timeType {
  131. if ct != nil {
  132. if ct.typeof == typeStructOnly {
  133. goto CONTINUE
  134. } else if ct.typeof == typeIsDefault {
  135. // set Field Level fields
  136. v.slflParent = parent
  137. v.flField = current
  138. v.cf = cf
  139. v.ct = ct
  140. if !ct.fn(ctx, v) {
  141. v.str1 = string(append(ns, cf.altName...))
  142. if v.v.hasTagNameFunc {
  143. v.str2 = string(append(structNs, cf.name...))
  144. } else {
  145. v.str2 = v.str1
  146. }
  147. v.errs = append(v.errs,
  148. &fieldError{
  149. v: v.v,
  150. tag: ct.aliasTag,
  151. actualTag: ct.tag,
  152. ns: v.str1,
  153. structNs: v.str2,
  154. fieldLen: uint8(len(cf.altName)),
  155. structfieldLen: uint8(len(cf.name)),
  156. value: current.Interface(),
  157. param: ct.param,
  158. kind: kind,
  159. typ: typ,
  160. },
  161. )
  162. return
  163. }
  164. }
  165. ct = ct.next
  166. }
  167. if ct != nil && ct.typeof == typeNoStructLevel {
  168. return
  169. }
  170. CONTINUE:
  171. // if len == 0 then validating using 'Var' or 'VarWithValue'
  172. // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
  173. // VarWithField - this allows for validating against each field within the struct against a specific value
  174. // pretty handy in certain situations
  175. if len(cf.name) > 0 {
  176. ns = append(append(ns, cf.altName...), '.')
  177. structNs = append(append(structNs, cf.name...), '.')
  178. }
  179. v.validateStruct(ctx, current, current, typ, ns, structNs, ct)
  180. return
  181. }
  182. }
  183. if !ct.hasTag {
  184. return
  185. }
  186. typ = current.Type()
  187. OUTER:
  188. for {
  189. if ct == nil {
  190. return
  191. }
  192. switch ct.typeof {
  193. case typeOmitEmpty:
  194. // set Field Level fields
  195. v.slflParent = parent
  196. v.flField = current
  197. v.cf = cf
  198. v.ct = ct
  199. if !v.fldIsPointer && !hasValue(v) {
  200. return
  201. }
  202. ct = ct.next
  203. continue
  204. case typeEndKeys:
  205. return
  206. case typeDive:
  207. ct = ct.next
  208. // traverse slice or map here
  209. // or panic ;)
  210. switch kind {
  211. case reflect.Slice, reflect.Array:
  212. var i64 int64
  213. reusableCF := &cField{}
  214. for i := 0; i < current.Len(); i++ {
  215. i64 = int64(i)
  216. v.misc = append(v.misc[0:0], cf.name...)
  217. v.misc = append(v.misc, '[')
  218. v.misc = strconv.AppendInt(v.misc, i64, 10)
  219. v.misc = append(v.misc, ']')
  220. reusableCF.name = string(v.misc)
  221. if cf.namesEqual {
  222. reusableCF.altName = reusableCF.name
  223. } else {
  224. v.misc = append(v.misc[0:0], cf.altName...)
  225. v.misc = append(v.misc, '[')
  226. v.misc = strconv.AppendInt(v.misc, i64, 10)
  227. v.misc = append(v.misc, ']')
  228. reusableCF.altName = string(v.misc)
  229. }
  230. v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
  231. }
  232. case reflect.Map:
  233. var pv string
  234. reusableCF := &cField{}
  235. for _, key := range current.MapKeys() {
  236. pv = fmt.Sprintf("%v", key.Interface())
  237. v.misc = append(v.misc[0:0], cf.name...)
  238. v.misc = append(v.misc, '[')
  239. v.misc = append(v.misc, pv...)
  240. v.misc = append(v.misc, ']')
  241. reusableCF.name = string(v.misc)
  242. if cf.namesEqual {
  243. reusableCF.altName = reusableCF.name
  244. } else {
  245. v.misc = append(v.misc[0:0], cf.altName...)
  246. v.misc = append(v.misc, '[')
  247. v.misc = append(v.misc, pv...)
  248. v.misc = append(v.misc, ']')
  249. reusableCF.altName = string(v.misc)
  250. }
  251. if ct != nil && ct.typeof == typeKeys && ct.keys != nil {
  252. v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys)
  253. // can be nil when just keys being validated
  254. if ct.next != nil {
  255. v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
  256. }
  257. } else {
  258. v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
  259. }
  260. }
  261. default:
  262. // throw error, if not a slice or map then should not have gotten here
  263. // bad dive tag
  264. panic("dive error! can't dive on a non slice or map")
  265. }
  266. return
  267. case typeOr:
  268. v.misc = v.misc[0:0]
  269. for {
  270. // set Field Level fields
  271. v.slflParent = parent
  272. v.flField = current
  273. v.cf = cf
  274. v.ct = ct
  275. if ct.fn(ctx, v) {
  276. // drain rest of the 'or' values, then continue or leave
  277. for {
  278. ct = ct.next
  279. if ct == nil {
  280. return
  281. }
  282. if ct.typeof != typeOr {
  283. continue OUTER
  284. }
  285. }
  286. }
  287. v.misc = append(v.misc, '|')
  288. v.misc = append(v.misc, ct.tag...)
  289. if ct.hasParam {
  290. v.misc = append(v.misc, '=')
  291. v.misc = append(v.misc, ct.param...)
  292. }
  293. if ct.isBlockEnd || ct.next == nil {
  294. // if we get here, no valid 'or' value and no more tags
  295. v.str1 = string(append(ns, cf.altName...))
  296. if v.v.hasTagNameFunc {
  297. v.str2 = string(append(structNs, cf.name...))
  298. } else {
  299. v.str2 = v.str1
  300. }
  301. if ct.hasAlias {
  302. v.errs = append(v.errs,
  303. &fieldError{
  304. v: v.v,
  305. tag: ct.aliasTag,
  306. actualTag: ct.actualAliasTag,
  307. ns: v.str1,
  308. structNs: v.str2,
  309. fieldLen: uint8(len(cf.altName)),
  310. structfieldLen: uint8(len(cf.name)),
  311. value: current.Interface(),
  312. param: ct.param,
  313. kind: kind,
  314. typ: typ,
  315. },
  316. )
  317. } else {
  318. tVal := string(v.misc)[1:]
  319. v.errs = append(v.errs,
  320. &fieldError{
  321. v: v.v,
  322. tag: tVal,
  323. actualTag: tVal,
  324. ns: v.str1,
  325. structNs: v.str2,
  326. fieldLen: uint8(len(cf.altName)),
  327. structfieldLen: uint8(len(cf.name)),
  328. value: current.Interface(),
  329. param: ct.param,
  330. kind: kind,
  331. typ: typ,
  332. },
  333. )
  334. }
  335. return
  336. }
  337. ct = ct.next
  338. }
  339. default:
  340. // set Field Level fields
  341. v.slflParent = parent
  342. v.flField = current
  343. v.cf = cf
  344. v.ct = ct
  345. if !ct.fn(ctx, v) {
  346. v.str1 = string(append(ns, cf.altName...))
  347. if v.v.hasTagNameFunc {
  348. v.str2 = string(append(structNs, cf.name...))
  349. } else {
  350. v.str2 = v.str1
  351. }
  352. v.errs = append(v.errs,
  353. &fieldError{
  354. v: v.v,
  355. tag: ct.aliasTag,
  356. actualTag: ct.tag,
  357. ns: v.str1,
  358. structNs: v.str2,
  359. fieldLen: uint8(len(cf.altName)),
  360. structfieldLen: uint8(len(cf.name)),
  361. value: current.Interface(),
  362. param: ct.param,
  363. kind: kind,
  364. typ: typ,
  365. },
  366. )
  367. return
  368. }
  369. ct = ct.next
  370. }
  371. }
  372. }