map.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. package valid
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "reflect"
  7. )
  8. var (
  9. // ErrNotMap is the error that the value being validated is not a map.
  10. ErrNotMap = errors.New("only a map can be validated")
  11. // ErrKeyWrongType is the error returned in case of an incorrect key type.
  12. ErrKeyWrongType = NewError("validation_key_wrong_type", "key not the correct type")
  13. // ErrKeyMissing is the error returned in case of a missing key.
  14. ErrKeyMissing = NewError("validation_key_missing", "required key is missing")
  15. // ErrKeyUnexpected is the error returned in case of an unexpected key.
  16. ErrKeyUnexpected = NewError("validation_key_unexpected", "key not expected")
  17. )
  18. type (
  19. // MapRule represents a rule set associated with a map.
  20. MapRule struct {
  21. keys []*KeyRules
  22. allowExtraKeys bool
  23. }
  24. // KeyRules represents a rule set associated with a map key.
  25. KeyRules struct {
  26. key interface{}
  27. optional bool
  28. rules []Rule
  29. }
  30. )
  31. // Map returns a validation rule that checks the keys and values of a map.
  32. // This rule should only be used for validating maps, or a validation error will be reported.
  33. // Use Key() to specify map keys that need to be validated. Each Key() call specifies a single key which can
  34. // be associated with multiple rules.
  35. // For example,
  36. // validation.Map(
  37. // validation.Key("Name", validation.Required),
  38. // validation.Key("Value", validation.Required, validation.Length(5, 10)),
  39. // )
  40. //
  41. // A nil value is considered valid. Use the Required rule to make sure a map value is present.
  42. func Map(keys ...*KeyRules) MapRule {
  43. return MapRule{keys: keys}
  44. }
  45. // AllowExtraKeys configures the rule to ignore extra keys.
  46. func (r MapRule) AllowExtraKeys() MapRule {
  47. r.allowExtraKeys = true
  48. return r
  49. }
  50. // Validate checks if the given value is valid or not.
  51. func (r MapRule) Validate(m interface{}) error {
  52. return r.ValidateWithContext(nil, m)
  53. }
  54. // ValidateWithContext checks if the given value is valid or not.
  55. func (r MapRule) ValidateWithContext(ctx context.Context, m interface{}) error {
  56. value := reflect.ValueOf(m)
  57. if value.Kind() == reflect.Ptr {
  58. value = value.Elem()
  59. }
  60. if value.Kind() != reflect.Map {
  61. // must be a map
  62. return NewInternalError(ErrNotMap)
  63. }
  64. if value.IsNil() {
  65. // treat a nil map as valid
  66. return nil
  67. }
  68. errs := Errors{}
  69. kt := value.Type().Key()
  70. var extraKeys map[interface{}]bool
  71. if !r.allowExtraKeys {
  72. extraKeys = make(map[interface{}]bool, value.Len())
  73. for _, k := range value.MapKeys() {
  74. extraKeys[k.Interface()] = true
  75. }
  76. }
  77. for _, kr := range r.keys {
  78. var err error
  79. if kv := reflect.ValueOf(kr.key); !kt.AssignableTo(kv.Type()) {
  80. err = ErrKeyWrongType
  81. } else if vv := value.MapIndex(kv); !vv.IsValid() {
  82. if !kr.optional {
  83. err = ErrKeyMissing
  84. }
  85. } else if ctx == nil {
  86. err = Validate(vv.Interface(), kr.rules...)
  87. } else {
  88. err = ValidateWithContext(ctx, vv.Interface(), kr.rules...)
  89. }
  90. if err != nil {
  91. if ie, ok := err.(InternalError); ok && ie.InternalError() != nil {
  92. return err
  93. }
  94. errs[getErrorKeyName(kr.key)] = err
  95. }
  96. if !r.allowExtraKeys {
  97. delete(extraKeys, kr.key)
  98. }
  99. }
  100. if !r.allowExtraKeys {
  101. for key := range extraKeys {
  102. errs[getErrorKeyName(key)] = ErrKeyUnexpected
  103. }
  104. }
  105. if len(errs) > 0 {
  106. return errs
  107. }
  108. return nil
  109. }
  110. // Key specifies a map key and the corresponding validation rules.
  111. func Key(key interface{}, rules ...Rule) *KeyRules {
  112. return &KeyRules{
  113. key: key,
  114. rules: rules,
  115. }
  116. }
  117. // Optional configures the rule to ignore the key if missing.
  118. func (r *KeyRules) Optional() *KeyRules {
  119. r.optional = true
  120. return r
  121. }
  122. // getErrorKeyName returns the name that should be used to represent the validation error of a map key.
  123. func getErrorKeyName(key interface{}) string {
  124. return fmt.Sprintf("%v", key)
  125. }