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

295 lines
5.3 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
import (
"context"
"errors"
"fmt"
"reflect"
"strings"
)
var (
2024-02-18 10:42:21 +00:00
// ErrStructPointer is the error that a struct being validated is not specified as a pointer.
2024-02-18 10:42:21 +00:00
ErrStructPointer = errors.New("only a pointer to a struct can be validated")
)
type (
2024-02-18 10:42:21 +00:00
// ErrFieldPointer is the error that a field is not specified as a pointer.
2024-02-18 10:42:21 +00:00
ErrFieldPointer int
// ErrFieldNotFound is the error that a field cannot be found in the struct.
2024-02-18 10:42:21 +00:00
ErrFieldNotFound int
// FieldRules represents a rule set associated with a struct field.
2024-02-18 10:42:21 +00:00
FieldRules struct {
fieldPtr interface{}
rules []Rule
2024-02-18 10:42:21 +00:00
}
)
// Error returns the error string of ErrFieldPointer.
2024-02-18 10:42:21 +00:00
func (e ErrFieldPointer) Error() string {
2024-02-18 10:42:21 +00:00
return fmt.Sprintf("field #%v must be specified as a pointer", int(e))
2024-02-18 10:42:21 +00:00
}
// Error returns the error string of ErrFieldNotFound.
2024-02-18 10:42:21 +00:00
func (e ErrFieldNotFound) Error() string {
2024-02-18 10:42:21 +00:00
return fmt.Sprintf("field #%v cannot be found in the struct", int(e))
2024-02-18 10:42:21 +00:00
}
// ValidateStruct validates a struct by checking the specified struct fields against the corresponding validation rules.
2024-02-18 10:42:21 +00:00
// Note that the struct being validated must be specified as a pointer to it. If the pointer is nil, it is considered valid.
2024-02-18 10:42:21 +00:00
// Use Field() to specify struct fields that need to be validated. Each Field() call specifies a single field which
2024-02-18 10:42:21 +00:00
// should be specified as a pointer to the field. A field can be associated with multiple rules.
2024-02-18 10:42:21 +00:00
// For example,
2024-02-18 10:42:21 +00:00
//
2024-06-14 08:41:36 +00:00
// value := struct {
2024-06-14 08:41:36 +00:00
// Name string
2024-06-14 08:41:36 +00:00
// Value string
2024-06-14 08:41:36 +00:00
// }{"name", "demo"}
2024-06-14 08:41:36 +00:00
// err := validation.ValidateStruct(&value,
2024-06-14 08:41:36 +00:00
// validation.Field(&a.Name, validation.Required),
2024-06-14 08:41:36 +00:00
// validation.Field(&a.Value, validation.Required, validation.Length(5, 10)),
2024-06-14 08:41:36 +00:00
// )
2024-06-14 08:41:36 +00:00
// fmt.Println(err)
2024-06-14 08:41:36 +00:00
// // Value: the length must be between 5 and 10.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// An error will be returned if validation fails.
2024-02-18 10:42:21 +00:00
func ValidateStruct(structPtr interface{}, fields ...*FieldRules) error {
2024-02-18 10:42:21 +00:00
return ValidateStructWithContext(nil, structPtr, fields...)
2024-02-18 10:42:21 +00:00
}
// ValidateStructWithContext validates a struct with the given context.
2024-02-18 10:42:21 +00:00
// The only difference between ValidateStructWithContext and ValidateStruct is that the former will
2024-02-18 10:42:21 +00:00
// validate struct fields with the provided context.
2024-02-18 10:42:21 +00:00
// Please refer to ValidateStruct for the detailed instructions on how to use this function.
2024-02-18 10:42:21 +00:00
func ValidateStructWithContext(ctx context.Context, structPtr interface{}, fields ...*FieldRules) error {
2024-02-18 10:42:21 +00:00
value := reflect.ValueOf(structPtr)
2024-02-18 10:42:21 +00:00
if value.Kind() != reflect.Ptr || !value.IsNil() && value.Elem().Kind() != reflect.Struct {
2024-02-18 10:42:21 +00:00
// must be a pointer to a struct
2024-02-18 10:42:21 +00:00
return NewInternalError(ErrStructPointer)
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 struct pointer as valid
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
value = value.Elem()
errs := Errors{}
for i, fr := range fields {
2024-02-18 10:42:21 +00:00
fv := reflect.ValueOf(fr.fieldPtr)
2024-02-18 10:42:21 +00:00
if fv.Kind() != reflect.Ptr {
2024-02-18 10:42:21 +00:00
return NewInternalError(ErrFieldPointer(i))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
ft := findStructField(value, fv)
2024-02-18 10:42:21 +00:00
if ft == nil {
2024-02-18 10:42:21 +00:00
return NewInternalError(ErrFieldNotFound(i))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
if ctx == nil {
2024-02-18 10:42:21 +00:00
err = Validate(fv.Elem().Interface(), fr.rules...)
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
err = ValidateWithContext(ctx, fv.Elem().Interface(), fr.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
if ft.Anonymous {
2024-02-18 10:42:21 +00:00
// merge errors from anonymous struct field
2024-02-18 10:42:21 +00:00
if es, ok := err.(Errors); ok {
2024-02-18 10:42:21 +00:00
for name, value := range es {
2024-02-18 10:42:21 +00:00
errs[name] = value
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
errs[getErrorFieldName(ft)] = err
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
}
// Field specifies a struct field and the corresponding validation rules.
2024-02-18 10:42:21 +00:00
// The struct field must be specified as a pointer to it.
2024-02-18 10:42:21 +00:00
func Field(fieldPtr interface{}, rules ...Rule) *FieldRules {
2024-02-18 10:42:21 +00:00
return &FieldRules{
2024-02-18 10:42:21 +00:00
fieldPtr: fieldPtr,
rules: rules,
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// findStructField looks for a field in the given struct.
2024-02-18 10:42:21 +00:00
// The field being looked for should be a pointer to the actual struct field.
2024-02-18 10:42:21 +00:00
// If found, the field info will be returned. Otherwise, nil will be returned.
2024-02-18 10:42:21 +00:00
func findStructField(structValue reflect.Value, fieldValue reflect.Value) *reflect.StructField {
2024-02-18 10:42:21 +00:00
ptr := fieldValue.Pointer()
2024-02-18 10:42:21 +00:00
for i := structValue.NumField() - 1; i >= 0; i-- {
2024-02-18 10:42:21 +00:00
sf := structValue.Type().Field(i)
2024-02-18 10:42:21 +00:00
if ptr == structValue.Field(i).UnsafeAddr() {
2024-02-18 10:42:21 +00:00
// do additional type comparison because it's possible that the address of
2024-02-18 10:42:21 +00:00
// an embedded struct is the same as the first field of the embedded struct
2024-02-18 10:42:21 +00:00
if sf.Type == fieldValue.Elem().Type() {
2024-02-18 10:42:21 +00:00
return &sf
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 sf.Anonymous {
2024-02-18 10:42:21 +00:00
// delve into anonymous struct to look for the field
2024-02-18 10:42:21 +00:00
fi := structValue.Field(i)
2024-02-18 10:42:21 +00:00
if sf.Type.Kind() == reflect.Ptr {
2024-02-18 10:42:21 +00:00
fi = fi.Elem()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if fi.Kind() == reflect.Struct {
2024-02-18 10:42:21 +00:00
if f := findStructField(fi, fieldValue); f != nil {
2024-02-18 10:42:21 +00:00
return f
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
}
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// getErrorFieldName returns the name that should be used to represent the validation error of a struct field.
2024-02-18 10:42:21 +00:00
func getErrorFieldName(f *reflect.StructField) string {
2024-02-18 10:42:21 +00:00
if tag := f.Tag.Get(ErrorTag); tag != "" && tag != "-" {
2024-02-18 10:42:21 +00:00
if cps := strings.SplitN(tag, ",", 2); cps[0] != "" {
2024-02-18 10:42:21 +00:00
return cps[0]
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return f.Name
2024-02-18 10:42:21 +00:00
}