validation.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright 2016 Qiang Xue. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. // Package validation provides configurable and extensible rules for validating data of various types.
  5. package validation
  6. import (
  7. "fmt"
  8. "reflect"
  9. "strconv"
  10. )
  11. type (
  12. // Validatable is the interface indicating the type implementing it supports data validation.
  13. Validatable interface {
  14. // Validate validates the data and returns an error if validation fails.
  15. Validate() error
  16. }
  17. // Rule represents a validation rule.
  18. Rule interface {
  19. // Validate validates a value and returns a value if validation fails.
  20. Validate(value interface{}) error
  21. }
  22. // RuleFunc represents a validator function.
  23. // You may wrap it as a Rule by calling By().
  24. RuleFunc func(value interface{}) error
  25. )
  26. var (
  27. // ErrorTag is the struct tag name used to customize the error field name for a struct field.
  28. ErrorTag = "json"
  29. // Skip is a special validation rule that indicates all rules following it should be skipped.
  30. Skip = &skipRule{}
  31. validatableType = reflect.TypeOf((*Validatable)(nil)).Elem()
  32. )
  33. // Validate validates the given value and returns the validation error, if any.
  34. //
  35. // Validate performs validation using the following steps:
  36. // - validate the value against the rules passed in as parameters
  37. // - if the value is a map and the map values implement `Validatable`, call `Validate` of every map value
  38. // - if the value is a slice or array whose values implement `Validatable`, call `Validate` of every element
  39. func Validate(value interface{}, rules ...Rule) error {
  40. for _, rule := range rules {
  41. if _, ok := rule.(*skipRule); ok {
  42. return nil
  43. }
  44. if err := rule.Validate(value); err != nil {
  45. return err
  46. }
  47. }
  48. rv := reflect.ValueOf(value)
  49. if (rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface) && rv.IsNil() {
  50. return nil
  51. }
  52. if v, ok := value.(Validatable); ok {
  53. return v.Validate()
  54. }
  55. switch rv.Kind() {
  56. case reflect.Map:
  57. if rv.Type().Elem().Implements(validatableType) {
  58. return validateMap(rv)
  59. }
  60. case reflect.Slice, reflect.Array:
  61. if rv.Type().Elem().Implements(validatableType) {
  62. return validateSlice(rv)
  63. }
  64. case reflect.Ptr, reflect.Interface:
  65. return Validate(rv.Elem().Interface())
  66. }
  67. return nil
  68. }
  69. // validateMap validates a map of validatable elements
  70. func validateMap(rv reflect.Value) error {
  71. errs := Errors{}
  72. for _, key := range rv.MapKeys() {
  73. if mv := rv.MapIndex(key).Interface(); mv != nil {
  74. if err := mv.(Validatable).Validate(); err != nil {
  75. errs[fmt.Sprintf("%v", key.Interface())] = err
  76. }
  77. }
  78. }
  79. if len(errs) > 0 {
  80. return errs
  81. }
  82. return nil
  83. }
  84. // validateMap validates a slice/array of validatable elements
  85. func validateSlice(rv reflect.Value) error {
  86. errs := Errors{}
  87. l := rv.Len()
  88. for i := 0; i < l; i++ {
  89. if ev := rv.Index(i).Interface(); ev != nil {
  90. if err := ev.(Validatable).Validate(); err != nil {
  91. errs[strconv.Itoa(i)] = err
  92. }
  93. }
  94. }
  95. if len(errs) > 0 {
  96. return errs
  97. }
  98. return nil
  99. }
  100. type skipRule struct{}
  101. func (r *skipRule) Validate(interface{}) error {
  102. return nil
  103. }
  104. type inlineRule struct {
  105. f RuleFunc
  106. }
  107. func (r *inlineRule) Validate(value interface{}) error {
  108. return r.f(value)
  109. }
  110. // By wraps a RuleFunc into a Rule.
  111. func By(f RuleFunc) Rule {
  112. return &inlineRule{f}
  113. }