176 lines
5.3 KiB
Go
176 lines
5.3 KiB
Go
|
package validator
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
// StructLevelFunc accepts all values needed for struct level validation
|
||
|
type StructLevelFunc func(sl StructLevel)
|
||
|
|
||
|
// StructLevelFuncCtx accepts all values needed for struct level validation
|
||
|
// but also allows passing of contextual validation information via context.Context.
|
||
|
type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)
|
||
|
|
||
|
// wrapStructLevelFunc wraps normal StructLevelFunc makes it compatible with StructLevelFuncCtx
|
||
|
func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
|
||
|
return func(ctx context.Context, sl StructLevel) {
|
||
|
fn(sl)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// StructLevel contains all the information and helper functions
|
||
|
// to validate a struct
|
||
|
type StructLevel interface {
|
||
|
|
||
|
// returns the main validation object, in case one wants to call validations internally.
|
||
|
// this is so you don't have to use anonymous functions to get access to the validate
|
||
|
// instance.
|
||
|
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.
|
||
|
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 its 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. e.g. 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, structFieldName string, tag, param string)
|
||
|
|
||
|
// reports an error just by passing ValidationErrors
|
||
|
//
|
||
|
// NOTES:
|
||
|
//
|
||
|
// relativeNamespace and relativeActualNamespace get appended to the
|
||
|
// existing namespace that validator is on.
|
||
|
// e.g. 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
|
||
|
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
|
||
|
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {
|
||
|
|
||
|
fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)
|
||
|
|
||
|
if len(structFieldName) == 0 {
|
||
|
structFieldName = fieldName
|
||
|
}
|
||
|
|
||
|
v.str1 = string(append(v.ns, fieldName...))
|
||
|
|
||
|
if v.v.hasTagNameFunc || fieldName != structFieldName {
|
||
|
v.str2 = string(append(v.actualNs, structFieldName...))
|
||
|
} else {
|
||
|
v.str2 = v.str1
|
||
|
}
|
||
|
|
||
|
if kind == reflect.Invalid {
|
||
|
|
||
|
v.errs = append(v.errs,
|
||
|
&fieldError{
|
||
|
v: v.v,
|
||
|
tag: tag,
|
||
|
actualTag: tag,
|
||
|
ns: v.str1,
|
||
|
structNs: v.str2,
|
||
|
fieldLen: uint8(len(fieldName)),
|
||
|
structfieldLen: uint8(len(structFieldName)),
|
||
|
param: param,
|
||
|
kind: kind,
|
||
|
},
|
||
|
)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
v.errs = append(v.errs,
|
||
|
&fieldError{
|
||
|
v: v.v,
|
||
|
tag: tag,
|
||
|
actualTag: tag,
|
||
|
ns: v.str1,
|
||
|
structNs: v.str2,
|
||
|
fieldLen: uint8(len(fieldName)),
|
||
|
structfieldLen: uint8(len(structFieldName)),
|
||
|
value: fv.Interface(),
|
||
|
param: param,
|
||
|
kind: kind,
|
||
|
typ: fv.Type(),
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// ReportValidationErrors reports ValidationErrors obtained from running validations within the Struct Level validation.
|
||
|
//
|
||
|
// NOTE: this function prepends the current namespace to the relative ones.
|
||
|
func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {
|
||
|
|
||
|
var err *fieldError
|
||
|
|
||
|
for i := 0; i < len(errs); i++ {
|
||
|
|
||
|
err = errs[i].(*fieldError)
|
||
|
err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...))
|
||
|
err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...))
|
||
|
|
||
|
v.errs = append(v.errs, err)
|
||
|
}
|
||
|
}
|