forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/go-ozzo/ozzo-validation/v4/map.go

240 lines
3.8 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
package validation
import (
"context"
"errors"
"fmt"
"reflect"
)
var (
2024-02-18 10:42:21 +00:00
// ErrNotMap is the error that the value being validated is not a map.
2024-02-18 10:42:21 +00:00
ErrNotMap = errors.New("only a map can be validated")
// ErrKeyWrongType is the error returned in case of an incorrect key type.
2024-02-18 10:42:21 +00:00
ErrKeyWrongType = NewError("validation_key_wrong_type", "key not the correct type")
// ErrKeyMissing is the error returned in case of a missing key.
2024-02-18 10:42:21 +00:00
ErrKeyMissing = NewError("validation_key_missing", "required key is missing")
// ErrKeyUnexpected is the error returned in case of an unexpected key.
2024-02-18 10:42:21 +00:00
ErrKeyUnexpected = NewError("validation_key_unexpected", "key not expected")
)
type (
2024-02-18 10:42:21 +00:00
// MapRule represents a rule set associated with a map.
2024-02-18 10:42:21 +00:00
MapRule struct {
keys []*KeyRules
2024-02-18 10:42:21 +00:00
allowExtraKeys bool
}
// KeyRules represents a rule set associated with a map key.
2024-02-18 10:42:21 +00:00
KeyRules struct {
key interface{}
2024-02-18 10:42:21 +00:00
optional bool
rules []Rule
2024-02-18 10:42:21 +00:00
}
)
// Map returns a validation rule that checks the keys and values of a map.
2024-02-18 10:42:21 +00:00
// This rule should only be used for validating maps, or a validation error will be reported.
2024-02-18 10:42:21 +00:00
// Use Key() to specify map keys that need to be validated. Each Key() call specifies a single key which can
2024-02-18 10:42:21 +00:00
// be associated with multiple rules.
2024-02-18 10:42:21 +00:00
// For example,
2024-06-14 08:41:36 +00:00
//
2024-06-14 08:41:36 +00:00
// validation.Map(
2024-06-14 08:41:36 +00:00
// validation.Key("Name", validation.Required),
2024-06-14 08:41:36 +00:00
// validation.Key("Value", validation.Required, validation.Length(5, 10)),
2024-06-14 08:41:36 +00:00
// )
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// A nil value is considered valid. Use the Required rule to make sure a map value is present.
2024-02-18 10:42:21 +00:00
func Map(keys ...*KeyRules) MapRule {
2024-02-18 10:42:21 +00:00
return MapRule{keys: keys}
2024-02-18 10:42:21 +00:00
}
// AllowExtraKeys configures the rule to ignore extra keys.
2024-02-18 10:42:21 +00:00
func (r MapRule) AllowExtraKeys() MapRule {
2024-02-18 10:42:21 +00:00
r.allowExtraKeys = true
2024-02-18 10:42:21 +00:00
return r
2024-02-18 10:42:21 +00:00
}
// Validate checks if the given value is valid or not.
2024-02-18 10:42:21 +00:00
func (r MapRule) Validate(m interface{}) error {
2024-02-18 10:42:21 +00:00
return r.ValidateWithContext(nil, m)
2024-02-18 10:42:21 +00:00
}
// ValidateWithContext checks if the given value is valid or not.
2024-02-18 10:42:21 +00:00
func (r MapRule) ValidateWithContext(ctx context.Context, m interface{}) error {
2024-02-18 10:42:21 +00:00
value := reflect.ValueOf(m)
2024-02-18 10:42:21 +00:00
if value.Kind() == reflect.Ptr {
2024-02-18 10:42:21 +00:00
value = value.Elem()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if value.Kind() != reflect.Map {
2024-02-18 10:42:21 +00:00
// must be a map
2024-02-18 10:42:21 +00:00
return NewInternalError(ErrNotMap)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if value.IsNil() {
2024-02-18 10:42:21 +00:00
// treat a nil map as valid
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
errs := Errors{}
2024-02-18 10:42:21 +00:00
kt := value.Type().Key()
var extraKeys map[interface{}]bool
2024-02-18 10:42:21 +00:00
if !r.allowExtraKeys {
2024-02-18 10:42:21 +00:00
extraKeys = make(map[interface{}]bool, value.Len())
2024-02-18 10:42:21 +00:00
for _, k := range value.MapKeys() {
2024-02-18 10:42:21 +00:00
extraKeys[k.Interface()] = true
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for _, kr := range r.keys {
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
if kv := reflect.ValueOf(kr.key); !kt.AssignableTo(kv.Type()) {
2024-02-18 10:42:21 +00:00
err = ErrKeyWrongType
2024-02-18 10:42:21 +00:00
} else if vv := value.MapIndex(kv); !vv.IsValid() {
2024-02-18 10:42:21 +00:00
if !kr.optional {
2024-02-18 10:42:21 +00:00
err = ErrKeyMissing
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else if ctx == nil {
2024-02-18 10:42:21 +00:00
err = Validate(vv.Interface(), kr.rules...)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
err = ValidateWithContext(ctx, vv.Interface(), kr.rules...)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
if ie, ok := err.(InternalError); ok && ie.InternalError() != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
errs[getErrorKeyName(kr.key)] = err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if !r.allowExtraKeys {
2024-02-18 10:42:21 +00:00
delete(extraKeys, kr.key)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if !r.allowExtraKeys {
2024-02-18 10:42:21 +00:00
for key := range extraKeys {
2024-02-18 10:42:21 +00:00
errs[getErrorKeyName(key)] = ErrKeyUnexpected
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if len(errs) > 0 {
2024-02-18 10:42:21 +00:00
return errs
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// Key specifies a map key and the corresponding validation rules.
2024-02-18 10:42:21 +00:00
func Key(key interface{}, rules ...Rule) *KeyRules {
2024-02-18 10:42:21 +00:00
return &KeyRules{
key: key,
2024-02-18 10:42:21 +00:00
rules: rules,
}
2024-02-18 10:42:21 +00:00
}
// Optional configures the rule to ignore the key if missing.
2024-02-18 10:42:21 +00:00
func (r *KeyRules) Optional() *KeyRules {
2024-02-18 10:42:21 +00:00
r.optional = true
2024-02-18 10:42:21 +00:00
return r
2024-02-18 10:42:21 +00:00
}
// getErrorKeyName returns the name that should be used to represent the validation error of a map key.
2024-02-18 10:42:21 +00:00
func getErrorKeyName(key interface{}) string {
2024-02-18 10:42:21 +00:00
return fmt.Sprintf("%v", key)
2024-02-18 10:42:21 +00:00
}