A fast pacakge for validation
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

312 lines
6.1 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package validate
  2. import (
  3. "reflect"
  4. "strconv"
  5. "strings"
  6. "sync"
  7. )
  8. type ValidateValuer interface {
  9. ValidateValue() interface{}
  10. }
  11. var vvType = reflect.TypeOf((*ValidateValuer)(nil)).Elem()
  12. // Validate validates a variable
  13. func Validate(v interface{}) []ValidationError {
  14. rv := reflect.ValueOf(v)
  15. return validate(rv)
  16. }
  17. // Field is always an array/slice index or struct field
  18. type Field struct {
  19. Index *int
  20. Field *reflect.StructField
  21. }
  22. // Fields is a list of Field
  23. type Fields []Field
  24. // ToString converts a list of fields to a string using the given struct tag
  25. func (f Fields) ToString(tag string) string {
  26. var field string
  27. for k, v := range f {
  28. if v.Index != nil {
  29. field += `[` + strconv.Itoa(*v.Index) + `]`
  30. continue
  31. }
  32. if k != 0 {
  33. field += `.`
  34. }
  35. var name string
  36. if tag != `` {
  37. name = v.Field.Tag.Get(tag)
  38. if idx := strings.IndexRune(name, ','); idx != -1 {
  39. name = name[:idx]
  40. }
  41. }
  42. if name == `` {
  43. name = v.Field.Name
  44. }
  45. field += name
  46. }
  47. return field
  48. }
  49. func (f Fields) String() string {
  50. return f.ToString(``)
  51. }
  52. // ValidationError contains information about a failed validation
  53. type ValidationError struct {
  54. Field Fields
  55. Check string
  56. Value string
  57. }
  58. func (e ValidationError) String() string {
  59. var val string
  60. if e.Value != `` {
  61. val = `=` + e.Value
  62. }
  63. return e.Field.String() + `: ` + e.Check + val
  64. }
  65. func prependErrs(f Field, errs []ValidationError) []ValidationError {
  66. for k := range errs {
  67. fields := make([]Field, len(errs[k].Field)+1)
  68. fields[0] = f
  69. for k, v := range errs[k].Field {
  70. fields[k+1] = v
  71. }
  72. errs[k].Field = fields
  73. }
  74. return errs
  75. }
  76. func validate(rv reflect.Value) []ValidationError {
  77. for rv.Kind() == reflect.Ptr {
  78. if rv.IsNil() {
  79. return nil
  80. }
  81. rv = rv.Elem()
  82. }
  83. if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
  84. var errs []ValidationError
  85. for i := 0; i < rv.Len(); i++ {
  86. newErrs := validate(rv.Index(i))
  87. index := i
  88. errs = append(errs, prependErrs(Field{&index, nil}, newErrs)...)
  89. }
  90. return errs
  91. }
  92. if rv.Kind() != reflect.Struct {
  93. return nil
  94. }
  95. var errs []ValidationError
  96. skip := -1
  97. for _, rule := range getCachedRules(rv.Type()) {
  98. if skip == rule.index {
  99. continue
  100. }
  101. err, cont := rule.f(rv.Field(rule.index))
  102. errs = append(errs, err...)
  103. if !cont {
  104. skip = rule.index
  105. }
  106. }
  107. return errs
  108. }
  109. var cache = struct {
  110. sync.Mutex
  111. data map[reflect.Type][]rule
  112. }{data: map[reflect.Type][]rule{}}
  113. func getCachedRules(rt reflect.Type) []rule {
  114. cache.Lock()
  115. defer cache.Unlock()
  116. rules, ok := cache.data[rt]
  117. if !ok {
  118. rules = getRules(rt)
  119. cache.data[rt] = rules
  120. }
  121. return rules
  122. }
  123. type rule struct {
  124. index int
  125. f func(reflect.Value) ([]ValidationError, bool)
  126. }
  127. func getRules(rt reflect.Type) []rule {
  128. var rules []rule
  129. for i := 0; i < rt.NumField(); i++ {
  130. ft := rt.Field(i)
  131. var valuer bool
  132. if ft.Type.Implements(vvType) {
  133. ft.Type = reflect.TypeOf(reflect.New(ft.Type).Interface().(ValidateValuer).ValidateValue())
  134. valuer = true
  135. }
  136. kind := simplifyKind(ft.Type.Kind())
  137. var ptr bool
  138. if kind == reflect.Ptr {
  139. kind = ft.Type.Elem().Kind()
  140. ptr = true
  141. }
  142. kind = simplifyKind(kind)
  143. tags := strings.Split(ft.Tag.Get(`validate`), `,`)
  144. rules = append(rules, getTagFuncs(i, ft, kind, tags, ptr, valuer)...)
  145. switch kind {
  146. case reflect.Slice, reflect.Struct, reflect.Interface:
  147. rules = append(rules, rule{i, nest(ft)})
  148. }
  149. }
  150. return rules
  151. }
  152. func nest(ft reflect.StructField) func(reflect.Value) ([]ValidationError, bool) {
  153. return func(rv reflect.Value) ([]ValidationError, bool) {
  154. errs := validate(rv)
  155. if errs != nil {
  156. return prependErrs(Field{nil, &ft}, errs), false
  157. }
  158. return nil, true
  159. }
  160. }
  161. func getTagFuncs(i int, ft reflect.StructField, kind reflect.Kind, tags []string, ptr, valuer bool) []rule {
  162. var rules []rule
  163. for _, v := range tags {
  164. if v == `` {
  165. continue
  166. }
  167. parts := strings.SplitN(v, `=`, 2)
  168. tag, value := parts[0], ``
  169. if len(parts) > 1 {
  170. value = parts[1]
  171. }
  172. kind := kind
  173. ptr := ptr
  174. if ptr && (tag == `optional` || strings.TrimPrefix(tag, `!`) == `required`) {
  175. kind = reflect.Ptr
  176. ptr = false
  177. }
  178. var f validateCheck
  179. if tag == `optional` {
  180. f = func(rv reflect.Value) ([]ValidationError, bool) {
  181. check, _ := getTagFunc(`required`, ``, kind)
  182. return nil, check(rv, nil)
  183. }
  184. } else {
  185. var not bool
  186. if strings.HasPrefix(tag, `!`) {
  187. not = true
  188. }
  189. check, val := getTagFunc(strings.TrimPrefix(tag, `!`), value, kind)
  190. f = func(rv reflect.Value) ([]ValidationError, bool) {
  191. if check(rv, val) == !not {
  192. return nil, true
  193. }
  194. return []ValidationError{{Field: []Field{{nil, &ft}}, Check: tag, Value: value}}, false
  195. }
  196. }
  197. if ptr {
  198. f = depointerFunc(f, ft, tag, value)
  199. }
  200. if valuer {
  201. f = valuerFunc(f)
  202. }
  203. rules = append(rules, rule{i, f})
  204. }
  205. return rules
  206. }
  207. type validateCheck = func(rv reflect.Value) ([]ValidationError, bool)
  208. func depointerFunc(f validateCheck, ft reflect.StructField, tag, value string) validateCheck {
  209. return func(rv reflect.Value) ([]ValidationError, bool) {
  210. if rv.IsNil() {
  211. return []ValidationError{{Field: []Field{{nil, &ft}}, Check: tag, Value: value}}, false
  212. }
  213. return f(rv.Elem())
  214. }
  215. }
  216. func valuerFunc(f validateCheck) validateCheck {
  217. return func(rv reflect.Value) ([]ValidationError, bool) {
  218. rv = reflect.ValueOf(rv.Interface().(ValidateValuer).ValidateValue())
  219. return f(rv)
  220. }
  221. }
  222. func getTagFunc(tag, value string, kind reflect.Kind) (ValidationFunc, interface{}) {
  223. tagInfo, ok := funcs[tag]
  224. if !ok {
  225. panic(`Unknown validation ` + tag)
  226. }
  227. check, ok := tagInfo.kinds[kind]
  228. if !ok {
  229. panic(`Validation ` + tag + ` does not support ` + kind.String())
  230. }
  231. var val interface{}
  232. if value != `` && tagInfo.inputFunc != nil {
  233. val = tagInfo.inputFunc(kind, value)
  234. }
  235. return check, val
  236. }
  237. func simplifyKind(kind reflect.Kind) reflect.Kind {
  238. switch kind {
  239. case reflect.Array:
  240. return reflect.Slice
  241. case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  242. return reflect.Int
  243. case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  244. return reflect.Uint
  245. case reflect.Float32:
  246. return reflect.Float64
  247. }
  248. return kind
  249. }