💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
validator/struct_level.go

169 lines
5.0 KiB

8 years ago
package validator
import "reflect"
// StructLevelFunc accepts all values needed for struct level validation
type StructLevelFunc func(sl StructLevel)
// StructLevel contains all the information and helper functions
// to validate a struct
type StructLevel interface {
// returns the main validation object, in case one want to call validations internally.
// this is so you don;t have to use anonymous functoins to get access to the validate
// instance.
8 years ago
Validator() *Validate
// returns the top level struct, if any
Top() reflect.Value
// returns the current fields parent struct, if any
Parent() reflect.Value
// returns the current struct.
// this is not needed when implementing 'Validatable' interface,
// only when a StructLevel is registered
Current() reflect.Value
// ExtractType gets the actual underlying type of field value.
// It will dive into pointers, customTypes and return you the
// underlying value and it's kind.
ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
// reports an error just by passing the field and tag information
//
// NOTES:
//
// fieldName and altName get appended to the existing namespace that
// validator is on. eg. pass 'FirstName' or 'Names[0]' depending
// on the nesting
//
// tag can be an existing validation tag or just something you make up
// and process on the flip side it's up to you.
ReportError(field interface{}, fieldName, altName, tag string)
// reports an error just by passing ValidationErrors
//
// NOTES:
//
// relativeNamespace and relativeActualNamespace get appended to the
// existing namespace that validator is on.
// eg. pass 'User.FirstName' or 'Users[0].FirstName' depending
// on the nesting. most of the time they will be blank, unless you validate
// at a level lower the the current field depth
//
// tag can be an existing validation tag or just something you make up
// and process on the flip side it's up to you.
ReportValidationErrors(relativeNamespace, relativeActualNamespace string, errs ValidationErrors)
}
var _ StructLevel = new(validate)
// Top returns the top level struct
//
// NOTE: this can be the same as the current struct being validated
// if not is a nested struct.
//
// this is only called when within Struct and Field Level validation and
// should not be relied upon for an acurate value otherwise.
func (v *validate) Top() reflect.Value {
return v.top
}
// Parent returns the current structs parent
//
// NOTE: this can be the same as the current struct being validated
// if not is a nested struct.
//
// this is only called when within Struct and Field Level validation and
// should not be relied upon for an acurate value otherwise.
func (v *validate) Parent() reflect.Value {
return v.slflParent
}
// Current returns the current struct.
func (v *validate) Current() reflect.Value {
return v.slCurrent
}
// Validator returns the main validation object, in case one want to call validations internally.
func (v *validate) Validator() *Validate {
return v.v
}
// ExtractType gets the actual underlying type of field value.
func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind, bool) {
return v.extractTypeInternal(field, false)
}
// ReportError reports an error just by passing the field and tag information
8 years ago
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag string) {
8 years ago
fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)
8 years ago
if len(structFieldName) == 0 {
structFieldName = fieldName
8 years ago
}
ns := append(v.slNs, fieldName...)
8 years ago
nsStruct := append(v.slStructNs, structFieldName...)
8 years ago
switch kind {
case reflect.Invalid:
v.errs = append(v.errs,
&fieldError{
tag: tag,
actualTag: tag,
ns: string(ns),
8 years ago
structNs: string(nsStruct),
8 years ago
field: fieldName,
8 years ago
structField: structFieldName,
// param: "",
kind: kind,
8 years ago
},
)
default:
v.errs = append(v.errs,
&fieldError{
tag: tag,
actualTag: tag,
ns: string(ns),
8 years ago
structNs: string(nsStruct),
8 years ago
field: fieldName,
8 years ago
structField: structFieldName,
8 years ago
value: fv.Interface(),
8 years ago
// param: "",
kind: kind,
typ: fv.Type(),
8 years ago
},
)
}
}
// ReportValidationErrors reports ValidationErrors obtained from running validations within the Struct Level validation.
//
// NOTE: this function prepends the current namespace to the relative ones.
8 years ago
func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {
8 years ago
var err *fieldError
for i := 0; i < len(errs); i++ {
err = errs[i].(*fieldError)
8 years ago
err.ns = string(append(append(v.slNs, relativeNamespace...), err.ns...))
err.structNs = string(append(append(v.slStructNs, relativeStructNamespace...), err.structNs...))
8 years ago
v.errs = append(v.errs, err)
}
}
// Validatable is the interface a struct can implement and
// be validated just like registering a StructLevel validation
// (they actually have the exact same signature.)
type Validatable interface {
Validate(sl StructLevel)
}