validator.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  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.hasTag {
  94. if kind == reflect.Invalid {
  95. v.str1 = string(append(ns, cf.altName...))
  96. if v.v.hasTagNameFunc {
  97. v.str2 = string(append(structNs, cf.name...))
  98. } else {
  99. v.str2 = v.str1
  100. }
  101. v.errs = append(v.errs,
  102. &fieldError{
  103. v: v.v,
  104. tag: ct.aliasTag,
  105. actualTag: ct.tag,
  106. ns: v.str1,
  107. structNs: v.str2,
  108. fieldLen: uint8(len(cf.altName)),
  109. structfieldLen: uint8(len(cf.name)),
  110. param: ct.param,
  111. kind: kind,
  112. },
  113. )
  114. return
  115. }
  116. v.str1 = string(append(ns, cf.altName...))
  117. if v.v.hasTagNameFunc {
  118. v.str2 = string(append(structNs, cf.name...))
  119. } else {
  120. v.str2 = v.str1
  121. }
  122. if !ct.runValidationWhenNil {
  123. v.errs = append(v.errs,
  124. &fieldError{
  125. v: v.v,
  126. tag: ct.aliasTag,
  127. actualTag: ct.tag,
  128. ns: v.str1,
  129. structNs: v.str2,
  130. fieldLen: uint8(len(cf.altName)),
  131. structfieldLen: uint8(len(cf.name)),
  132. value: getValue(current),
  133. param: ct.param,
  134. kind: kind,
  135. typ: current.Type(),
  136. },
  137. )
  138. return
  139. }
  140. }
  141. if kind == reflect.Invalid {
  142. return
  143. }
  144. case reflect.Struct:
  145. isNestedStruct = !current.Type().ConvertibleTo(timeType)
  146. // For backward compatibility before struct level validation tags were supported
  147. // as there were a number of projects relying on `required` not failing on non-pointer
  148. // structs. Since it's basically nonsensical to use `required` with a non-pointer struct
  149. // are explicitly skipping the required validation for it. This WILL be removed in the
  150. // next major version.
  151. if isNestedStruct && !v.v.requiredStructEnabled && ct != nil && ct.tag == requiredTag {
  152. ct = ct.next
  153. }
  154. }
  155. typ = current.Type()
  156. OUTER:
  157. for {
  158. if ct == nil || !ct.hasTag || (isNestedStruct && len(cf.name) == 0) {
  159. // isNestedStruct check here
  160. if isNestedStruct {
  161. // if len == 0 then validating using 'Var' or 'VarWithValue'
  162. // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
  163. // VarWithField - this allows for validating against each field within the struct against a specific value
  164. // pretty handy in certain situations
  165. if len(cf.name) > 0 {
  166. ns = append(append(ns, cf.altName...), '.')
  167. structNs = append(append(structNs, cf.name...), '.')
  168. }
  169. v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
  170. }
  171. return
  172. }
  173. switch ct.typeof {
  174. case typeNoStructLevel:
  175. return
  176. case typeStructOnly:
  177. if isNestedStruct {
  178. // if len == 0 then validating using 'Var' or 'VarWithValue'
  179. // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
  180. // VarWithField - this allows for validating against each field within the struct against a specific value
  181. // pretty handy in certain situations
  182. if len(cf.name) > 0 {
  183. ns = append(append(ns, cf.altName...), '.')
  184. structNs = append(append(structNs, cf.name...), '.')
  185. }
  186. v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
  187. }
  188. return
  189. case typeOmitEmpty:
  190. // set Field Level fields
  191. v.slflParent = parent
  192. v.flField = current
  193. v.cf = cf
  194. v.ct = ct
  195. if !hasValue(v) {
  196. return
  197. }
  198. ct = ct.next
  199. continue
  200. case typeOmitNil:
  201. v.slflParent = parent
  202. v.flField = current
  203. v.cf = cf
  204. v.ct = ct
  205. switch field := v.Field(); field.Kind() {
  206. case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
  207. if field.IsNil() {
  208. return
  209. }
  210. default:
  211. if v.fldIsPointer && field.Interface() == nil {
  212. return
  213. }
  214. }
  215. ct = ct.next
  216. continue
  217. case typeEndKeys:
  218. return
  219. case typeDive:
  220. ct = ct.next
  221. // traverse slice or map here
  222. // or panic ;)
  223. switch kind {
  224. case reflect.Slice, reflect.Array:
  225. var i64 int64
  226. reusableCF := &cField{}
  227. for i := 0; i < current.Len(); i++ {
  228. i64 = int64(i)
  229. v.misc = append(v.misc[0:0], cf.name...)
  230. v.misc = append(v.misc, '[')
  231. v.misc = strconv.AppendInt(v.misc, i64, 10)
  232. v.misc = append(v.misc, ']')
  233. reusableCF.name = string(v.misc)
  234. if cf.namesEqual {
  235. reusableCF.altName = reusableCF.name
  236. } else {
  237. v.misc = append(v.misc[0:0], cf.altName...)
  238. v.misc = append(v.misc, '[')
  239. v.misc = strconv.AppendInt(v.misc, i64, 10)
  240. v.misc = append(v.misc, ']')
  241. reusableCF.altName = string(v.misc)
  242. }
  243. v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
  244. }
  245. case reflect.Map:
  246. var pv string
  247. reusableCF := &cField{}
  248. for _, key := range current.MapKeys() {
  249. pv = fmt.Sprintf("%v", key.Interface())
  250. v.misc = append(v.misc[0:0], cf.name...)
  251. v.misc = append(v.misc, '[')
  252. v.misc = append(v.misc, pv...)
  253. v.misc = append(v.misc, ']')
  254. reusableCF.name = string(v.misc)
  255. if cf.namesEqual {
  256. reusableCF.altName = reusableCF.name
  257. } else {
  258. v.misc = append(v.misc[0:0], cf.altName...)
  259. v.misc = append(v.misc, '[')
  260. v.misc = append(v.misc, pv...)
  261. v.misc = append(v.misc, ']')
  262. reusableCF.altName = string(v.misc)
  263. }
  264. if ct != nil && ct.typeof == typeKeys && ct.keys != nil {
  265. v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys)
  266. // can be nil when just keys being validated
  267. if ct.next != nil {
  268. v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
  269. }
  270. } else {
  271. v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
  272. }
  273. }
  274. default:
  275. // throw error, if not a slice or map then should not have gotten here
  276. // bad dive tag
  277. panic("dive error! can't dive on a non slice or map")
  278. }
  279. return
  280. case typeOr:
  281. v.misc = v.misc[0:0]
  282. for {
  283. // set Field Level fields
  284. v.slflParent = parent
  285. v.flField = current
  286. v.cf = cf
  287. v.ct = ct
  288. if ct.fn(ctx, v) {
  289. if ct.isBlockEnd {
  290. ct = ct.next
  291. continue OUTER
  292. }
  293. // drain rest of the 'or' values, then continue or leave
  294. for {
  295. ct = ct.next
  296. if ct == nil {
  297. continue OUTER
  298. }
  299. if ct.typeof != typeOr {
  300. continue OUTER
  301. }
  302. if ct.isBlockEnd {
  303. ct = ct.next
  304. continue OUTER
  305. }
  306. }
  307. }
  308. v.misc = append(v.misc, '|')
  309. v.misc = append(v.misc, ct.tag...)
  310. if ct.hasParam {
  311. v.misc = append(v.misc, '=')
  312. v.misc = append(v.misc, ct.param...)
  313. }
  314. if ct.isBlockEnd || ct.next == nil {
  315. // if we get here, no valid 'or' value and no more tags
  316. v.str1 = string(append(ns, cf.altName...))
  317. if v.v.hasTagNameFunc {
  318. v.str2 = string(append(structNs, cf.name...))
  319. } else {
  320. v.str2 = v.str1
  321. }
  322. if ct.hasAlias {
  323. v.errs = append(v.errs,
  324. &fieldError{
  325. v: v.v,
  326. tag: ct.aliasTag,
  327. actualTag: ct.actualAliasTag,
  328. ns: v.str1,
  329. structNs: v.str2,
  330. fieldLen: uint8(len(cf.altName)),
  331. structfieldLen: uint8(len(cf.name)),
  332. value: getValue(current),
  333. param: ct.param,
  334. kind: kind,
  335. typ: typ,
  336. },
  337. )
  338. } else {
  339. tVal := string(v.misc)[1:]
  340. v.errs = append(v.errs,
  341. &fieldError{
  342. v: v.v,
  343. tag: tVal,
  344. actualTag: tVal,
  345. ns: v.str1,
  346. structNs: v.str2,
  347. fieldLen: uint8(len(cf.altName)),
  348. structfieldLen: uint8(len(cf.name)),
  349. value: getValue(current),
  350. param: ct.param,
  351. kind: kind,
  352. typ: typ,
  353. },
  354. )
  355. }
  356. return
  357. }
  358. ct = ct.next
  359. }
  360. default:
  361. // set Field Level fields
  362. v.slflParent = parent
  363. v.flField = current
  364. v.cf = cf
  365. v.ct = ct
  366. if !ct.fn(ctx, v) {
  367. v.str1 = string(append(ns, cf.altName...))
  368. if v.v.hasTagNameFunc {
  369. v.str2 = string(append(structNs, cf.name...))
  370. } else {
  371. v.str2 = v.str1
  372. }
  373. v.errs = append(v.errs,
  374. &fieldError{
  375. v: v.v,
  376. tag: ct.aliasTag,
  377. actualTag: ct.tag,
  378. ns: v.str1,
  379. structNs: v.str2,
  380. fieldLen: uint8(len(cf.altName)),
  381. structfieldLen: uint8(len(cf.name)),
  382. value: getValue(current),
  383. param: ct.param,
  384. kind: kind,
  385. typ: typ,
  386. },
  387. )
  388. return
  389. }
  390. ct = ct.next
  391. }
  392. }
  393. }
  394. func getValue(val reflect.Value) interface{} {
  395. if val.CanInterface() {
  396. return val.Interface()
  397. }
  398. if val.CanAddr() {
  399. return reflect.NewAt(val.Type(), unsafe.Pointer(val.UnsafeAddr())).Elem().Interface()
  400. }
  401. switch val.Kind() {
  402. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  403. return val.Int()
  404. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  405. return val.Uint()
  406. case reflect.Complex64, reflect.Complex128:
  407. return val.Complex()
  408. case reflect.Float32, reflect.Float64:
  409. return val.Float()
  410. default:
  411. return val.String()
  412. }
  413. }