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