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

461 lines
8.2 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Copyright 2016 Qiang Xue. All rights reserved.
2024-02-18 10:42:21 +00:00
// Use of this source code is governed by a MIT-style
2024-02-18 10:42:21 +00:00
// license that can be found in the LICENSE file.
// Package validation provides configurable and extensible rules for validating data of various types.
2024-02-18 10:42:21 +00:00
package validation
import (
"context"
"fmt"
"reflect"
"strconv"
)
type (
2024-02-18 10:42:21 +00:00
// Validatable is the interface indicating the type implementing it supports data validation.
2024-02-18 10:42:21 +00:00
Validatable interface {
2024-02-18 10:42:21 +00:00
// Validate validates the data and returns an error if validation fails.
2024-02-18 10:42:21 +00:00
Validate() error
}
// ValidatableWithContext is the interface indicating the type implementing it supports context-aware data validation.
2024-02-18 10:42:21 +00:00
ValidatableWithContext interface {
2024-02-18 10:42:21 +00:00
// ValidateWithContext validates the data with the given context and returns an error if validation fails.
2024-02-18 10:42:21 +00:00
ValidateWithContext(ctx context.Context) error
}
// Rule represents a validation rule.
2024-02-18 10:42:21 +00:00
Rule interface {
2024-02-18 10:42:21 +00:00
// Validate validates a value and returns a value if validation fails.
2024-02-18 10:42:21 +00:00
Validate(value interface{}) error
}
// RuleWithContext represents a context-aware validation rule.
2024-02-18 10:42:21 +00:00
RuleWithContext interface {
2024-02-18 10:42:21 +00:00
// ValidateWithContext validates a value and returns a value if validation fails.
2024-02-18 10:42:21 +00:00
ValidateWithContext(ctx context.Context, value interface{}) error
}
// RuleFunc represents a validator function.
2024-02-18 10:42:21 +00:00
// You may wrap it as a Rule by calling By().
2024-02-18 10:42:21 +00:00
RuleFunc func(value interface{}) error
// RuleWithContextFunc represents a validator function that is context-aware.
2024-02-18 10:42:21 +00:00
// You may wrap it as a Rule by calling WithContext().
2024-02-18 10:42:21 +00:00
RuleWithContextFunc func(ctx context.Context, value interface{}) error
)
var (
2024-02-18 10:42:21 +00:00
// ErrorTag is the struct tag name used to customize the error field name for a struct field.
2024-02-18 10:42:21 +00:00
ErrorTag = "json"
// Skip is a special validation rule that indicates all rules following it should be skipped.
2024-02-18 10:42:21 +00:00
Skip = skipRule{skip: true}
validatableType = reflect.TypeOf((*Validatable)(nil)).Elem()
2024-02-18 10:42:21 +00:00
validatableWithContextType = reflect.TypeOf((*ValidatableWithContext)(nil)).Elem()
)
// Validate validates the given value and returns the validation error, if any.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Validate performs validation using the following steps:
2024-06-14 08:41:36 +00:00
// 1. For each rule, call its `Validate()` to validate the value. Return if any error is found.
2024-06-14 08:41:36 +00:00
// 2. If the value being validated implements `Validatable`, call the value's `Validate()`.
2024-06-14 08:41:36 +00:00
// Return with the validation result.
2024-06-14 08:41:36 +00:00
// 3. If the value being validated is a map/slice/array, and the element type implements `Validatable`,
2024-06-14 08:41:36 +00:00
// for each element call the element value's `Validate()`. Return with the validation result.
2024-02-18 10:42:21 +00:00
func Validate(value interface{}, rules ...Rule) error {
2024-02-18 10:42:21 +00:00
for _, rule := range rules {
2024-02-18 10:42:21 +00:00
if s, ok := rule.(skipRule); ok && s.skip {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err := rule.Validate(value); err != 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
}
rv := reflect.ValueOf(value)
2024-02-18 10:42:21 +00:00
if (rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface) && rv.IsNil() {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
if v, ok := value.(Validatable); ok {
2024-02-18 10:42:21 +00:00
return v.Validate()
2024-02-18 10:42:21 +00:00
}
switch rv.Kind() {
2024-02-18 10:42:21 +00:00
case reflect.Map:
2024-02-18 10:42:21 +00:00
if rv.Type().Elem().Implements(validatableType) {
2024-02-18 10:42:21 +00:00
return validateMap(rv)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case reflect.Slice, reflect.Array:
2024-02-18 10:42:21 +00:00
if rv.Type().Elem().Implements(validatableType) {
2024-02-18 10:42:21 +00:00
return validateSlice(rv)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case reflect.Ptr, reflect.Interface:
2024-02-18 10:42:21 +00:00
return Validate(rv.Elem().Interface())
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
// ValidateWithContext validates the given value with the given context and returns the validation error, if any.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// ValidateWithContext performs validation using the following steps:
2024-06-14 08:41:36 +00:00
// 1. For each rule, call its `ValidateWithContext()` to validate the value if the rule implements `RuleWithContext`.
2024-06-14 08:41:36 +00:00
// Otherwise call `Validate()` of the rule. Return if any error is found.
2024-06-14 08:41:36 +00:00
// 2. If the value being validated implements `ValidatableWithContext`, call the value's `ValidateWithContext()`
2024-06-14 08:41:36 +00:00
// and return with the validation result.
2024-06-14 08:41:36 +00:00
// 3. If the value being validated implements `Validatable`, call the value's `Validate()`
2024-06-14 08:41:36 +00:00
// and return with the validation result.
2024-06-14 08:41:36 +00:00
// 4. If the value being validated is a map/slice/array, and the element type implements `ValidatableWithContext`,
2024-06-14 08:41:36 +00:00
// for each element call the element value's `ValidateWithContext()`. Return with the validation result.
2024-06-14 08:41:36 +00:00
// 5. If the value being validated is a map/slice/array, and the element type implements `Validatable`,
2024-06-14 08:41:36 +00:00
// for each element call the element value's `Validate()`. Return with the validation result.
2024-02-18 10:42:21 +00:00
func ValidateWithContext(ctx context.Context, value interface{}, rules ...Rule) error {
2024-02-18 10:42:21 +00:00
for _, rule := range rules {
2024-02-18 10:42:21 +00:00
if s, ok := rule.(skipRule); ok && s.skip {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if rc, ok := rule.(RuleWithContext); ok {
2024-02-18 10:42:21 +00:00
if err := rc.ValidateWithContext(ctx, value); err != 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
} else if err := rule.Validate(value); err != 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
}
rv := reflect.ValueOf(value)
2024-02-18 10:42:21 +00:00
if (rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface) && rv.IsNil() {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
if v, ok := value.(ValidatableWithContext); ok {
2024-02-18 10:42:21 +00:00
return v.ValidateWithContext(ctx)
2024-02-18 10:42:21 +00:00
}
if v, ok := value.(Validatable); ok {
2024-02-18 10:42:21 +00:00
return v.Validate()
2024-02-18 10:42:21 +00:00
}
switch rv.Kind() {
2024-02-18 10:42:21 +00:00
case reflect.Map:
2024-02-18 10:42:21 +00:00
if rv.Type().Elem().Implements(validatableWithContextType) {
2024-02-18 10:42:21 +00:00
return validateMapWithContext(ctx, rv)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if rv.Type().Elem().Implements(validatableType) {
2024-02-18 10:42:21 +00:00
return validateMap(rv)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case reflect.Slice, reflect.Array:
2024-02-18 10:42:21 +00:00
if rv.Type().Elem().Implements(validatableWithContextType) {
2024-02-18 10:42:21 +00:00
return validateSliceWithContext(ctx, rv)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if rv.Type().Elem().Implements(validatableType) {
2024-02-18 10:42:21 +00:00
return validateSlice(rv)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case reflect.Ptr, reflect.Interface:
2024-02-18 10:42:21 +00:00
return ValidateWithContext(ctx, rv.Elem().Interface())
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
// validateMap validates a map of validatable elements
2024-02-18 10:42:21 +00:00
func validateMap(rv reflect.Value) error {
2024-02-18 10:42:21 +00:00
errs := Errors{}
2024-02-18 10:42:21 +00:00
for _, key := range rv.MapKeys() {
2024-02-18 10:42:21 +00:00
if mv := rv.MapIndex(key).Interface(); mv != nil {
2024-02-18 10:42:21 +00:00
if err := mv.(Validatable).Validate(); err != nil {
2024-02-18 10:42:21 +00:00
errs[fmt.Sprintf("%v", key.Interface())] = err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
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
}
// validateMapWithContext validates a map of validatable elements with the given context.
2024-02-18 10:42:21 +00:00
func validateMapWithContext(ctx context.Context, rv reflect.Value) error {
2024-02-18 10:42:21 +00:00
errs := Errors{}
2024-02-18 10:42:21 +00:00
for _, key := range rv.MapKeys() {
2024-02-18 10:42:21 +00:00
if mv := rv.MapIndex(key).Interface(); mv != nil {
2024-02-18 10:42:21 +00:00
if err := mv.(ValidatableWithContext).ValidateWithContext(ctx); err != nil {
2024-02-18 10:42:21 +00:00
errs[fmt.Sprintf("%v", key.Interface())] = err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
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
}
// validateSlice validates a slice/array of validatable elements
2024-02-18 10:42:21 +00:00
func validateSlice(rv reflect.Value) error {
2024-02-18 10:42:21 +00:00
errs := Errors{}
2024-02-18 10:42:21 +00:00
l := rv.Len()
2024-02-18 10:42:21 +00:00
for i := 0; i < l; i++ {
2024-02-18 10:42:21 +00:00
if ev := rv.Index(i).Interface(); ev != nil {
2024-02-18 10:42:21 +00:00
if err := ev.(Validatable).Validate(); err != nil {
2024-02-18 10:42:21 +00:00
errs[strconv.Itoa(i)] = err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
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
}
// validateSliceWithContext validates a slice/array of validatable elements with the given context.
2024-02-18 10:42:21 +00:00
func validateSliceWithContext(ctx context.Context, rv reflect.Value) error {
2024-02-18 10:42:21 +00:00
errs := Errors{}
2024-02-18 10:42:21 +00:00
l := rv.Len()
2024-02-18 10:42:21 +00:00
for i := 0; i < l; i++ {
2024-02-18 10:42:21 +00:00
if ev := rv.Index(i).Interface(); ev != nil {
2024-02-18 10:42:21 +00:00
if err := ev.(ValidatableWithContext).ValidateWithContext(ctx); err != nil {
2024-02-18 10:42:21 +00:00
errs[strconv.Itoa(i)] = err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
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
}
type skipRule struct {
skip bool
}
func (r skipRule) Validate(interface{}) error {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// When determines if all rules following it should be skipped.
2024-02-18 10:42:21 +00:00
func (r skipRule) When(condition bool) skipRule {
2024-02-18 10:42:21 +00:00
r.skip = condition
2024-02-18 10:42:21 +00:00
return r
2024-02-18 10:42:21 +00:00
}
type inlineRule struct {
f RuleFunc
2024-02-18 10:42:21 +00:00
fc RuleWithContextFunc
}
func (r *inlineRule) Validate(value interface{}) error {
2024-02-18 10:42:21 +00:00
if r.f == nil {
2024-02-18 10:42:21 +00:00
return r.fc(context.Background(), value)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return r.f(value)
2024-02-18 10:42:21 +00:00
}
func (r *inlineRule) ValidateWithContext(ctx context.Context, value interface{}) error {
2024-02-18 10:42:21 +00:00
if r.fc == nil {
2024-02-18 10:42:21 +00:00
return r.f(value)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return r.fc(ctx, value)
2024-02-18 10:42:21 +00:00
}
// By wraps a RuleFunc into a Rule.
2024-02-18 10:42:21 +00:00
func By(f RuleFunc) Rule {
2024-02-18 10:42:21 +00:00
return &inlineRule{f: f}
2024-02-18 10:42:21 +00:00
}
// WithContext wraps a RuleWithContextFunc into a context-aware Rule.
2024-02-18 10:42:21 +00:00
func WithContext(f RuleWithContextFunc) Rule {
2024-02-18 10:42:21 +00:00
return &inlineRule{fc: f}
2024-02-18 10:42:21 +00:00
}