parent
5f57d2222a
commit
e0e1af6a61
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,202 @@ |
||||
package validator |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"reflect" |
||||
"strings" |
||||
) |
||||
|
||||
const ( |
||||
fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag" |
||||
) |
||||
|
||||
// InvalidValidationError describes an invalid argument passed to
|
||||
// `Struct`, `StructExcept`, StructPartial` or `Field`
|
||||
type InvalidValidationError struct { |
||||
Type reflect.Type |
||||
} |
||||
|
||||
// Error returns InvalidValidationError message
|
||||
func (e *InvalidValidationError) Error() string { |
||||
|
||||
if e.Type == nil { |
||||
return "validator: (nil)" |
||||
} |
||||
|
||||
return "validator: (nil " + e.Type.String() + ")" |
||||
} |
||||
|
||||
// ValidationErrors is an array of FieldError's
|
||||
// for use in custom error messages post validation.
|
||||
type ValidationErrors []FieldError |
||||
|
||||
// Error is intended for use in development + debugging and not intended to be a production error message.
|
||||
// It allows ValidationErrors to subscribe to the Error interface.
|
||||
// All information to create an error message specific to your application is contained within
|
||||
// the FieldError found within the ValidationErrors array
|
||||
func (ve ValidationErrors) Error() string { |
||||
|
||||
buff := bytes.NewBufferString("") |
||||
|
||||
var err *fieldError |
||||
|
||||
for i := 0; i < len(ve); i++ { |
||||
|
||||
err = ve[i].(*fieldError) |
||||
buff.WriteString(err.Error()) |
||||
buff.WriteString("\n") |
||||
} |
||||
|
||||
return strings.TrimSpace(buff.String()) |
||||
} |
||||
|
||||
// FieldError contains all functions to get error details
|
||||
type FieldError interface { |
||||
|
||||
// returns the validation tag that failed. if the
|
||||
// validation was an alias, this will return the
|
||||
// alias name and not the underlying tag that failed.
|
||||
//
|
||||
// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
|
||||
// will return "iscolor"
|
||||
Tag() string |
||||
|
||||
// returns the validation tag that failed, even if an
|
||||
// alias the actual tag within the alias will be returned.
|
||||
// If an 'or' validation fails the entire or will be returned.
|
||||
//
|
||||
// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
|
||||
// will return "hexcolor|rgb|rgba|hsl|hsla"
|
||||
ActualTag() string |
||||
|
||||
// returns the namespace for the field error, with the tag
|
||||
// name taking precedence over the fields actual name.
|
||||
//
|
||||
// eq. JSON name "User.fname" see ActualNamespace for comparison
|
||||
//
|
||||
// NOTE: this field can be blank when validating a single primitive field
|
||||
// using validate.Field(...) as there is no way to extract it's name
|
||||
Namespace() string |
||||
|
||||
// returns the namespace for the field error, with the fields
|
||||
// actual name.
|
||||
//
|
||||
// eq. "User.FirstName" see Namespace for comparison
|
||||
//
|
||||
// NOTE: this field can be blank when validating a single primitive field
|
||||
// using validate.Field(...) as there is no way to extract it's name
|
||||
ActualNamespace() string |
||||
|
||||
// returns the fields name with the tag name taking precedence over the
|
||||
// fields actual name.
|
||||
//
|
||||
// eq. JSON name "fname"
|
||||
// see ActualField for comparison
|
||||
Field() string |
||||
|
||||
// returns the fields actual name.
|
||||
//
|
||||
// eq. "FirstName"
|
||||
// see Field for comparison
|
||||
ActualField() string |
||||
|
||||
// returns the actual fields value in case needed for creating the error
|
||||
// message
|
||||
Value() interface{} |
||||
|
||||
// returns the param value, already converted into the fields type for
|
||||
// comparison; this will also help with generating an error message
|
||||
Param() interface{} |
||||
|
||||
// Kind returns the Field's reflect Kind
|
||||
//
|
||||
// eg. time.Time's kind is a struct
|
||||
Kind() reflect.Kind |
||||
|
||||
// Type returns the Field's reflect Type
|
||||
//
|
||||
// // eg. time.Time's type is time.Time
|
||||
Type() reflect.Type |
||||
} |
||||
|
||||
// compile time interface checks
|
||||
var _ FieldError = new(fieldError) |
||||
var _ error = new(fieldError) |
||||
|
||||
// fieldError contains a single field's validation error along
|
||||
// with other properties that may be needed for error message creation
|
||||
// it complies with the FieldError interface
|
||||
type fieldError struct { |
||||
tag string |
||||
actualTag string |
||||
ns string |
||||
actualNs string |
||||
field string |
||||
actualField string |
||||
value interface{} |
||||
param interface{} |
||||
kind reflect.Kind |
||||
typ reflect.Type |
||||
} |
||||
|
||||
// Tag returns the validation tag that failed.
|
||||
func (fe *fieldError) Tag() string { |
||||
return fe.tag |
||||
} |
||||
|
||||
// ActualTag returns the validation tag that failed, even if an
|
||||
// alias the actual tag within the alias will be returned.
|
||||
func (fe *fieldError) ActualTag() string { |
||||
return fe.actualTag |
||||
} |
||||
|
||||
// Namespace returns the namespace for the field error, with the tag
|
||||
// name taking precedence over the fields actual name.
|
||||
func (fe *fieldError) Namespace() string { |
||||
return fe.ns |
||||
} |
||||
|
||||
// ActualNamespace returns the namespace for the field error, with the fields
|
||||
// actual name.
|
||||
func (fe *fieldError) ActualNamespace() string { |
||||
return fe.actualNs |
||||
} |
||||
|
||||
// Field returns the fields name with the tag name taking precedence over the
|
||||
// fields actual name.
|
||||
func (fe *fieldError) Field() string { |
||||
return fe.field |
||||
} |
||||
|
||||
// ActualField returns the fields actual name.
|
||||
func (fe *fieldError) ActualField() string { |
||||
return fe.actualField |
||||
} |
||||
|
||||
// Value returns the actual fields value in case needed for creating the error
|
||||
// message
|
||||
func (fe *fieldError) Value() interface{} { |
||||
return fe.value |
||||
} |
||||
|
||||
// Param returns the param value, already converted into the fields type for
|
||||
// comparison; this will also help with generating an error message
|
||||
func (fe *fieldError) Param() interface{} { |
||||
return fe.param |
||||
} |
||||
|
||||
// Kind returns the Field's reflect Kind
|
||||
func (fe *fieldError) Kind() reflect.Kind { |
||||
return fe.kind |
||||
} |
||||
|
||||
// Type returns the Field's reflect Type
|
||||
func (fe *fieldError) Type() reflect.Type { |
||||
return fe.typ |
||||
} |
||||
|
||||
// Error returns the fieldError's error message
|
||||
func (fe *fieldError) Error() string { |
||||
return fmt.Sprintf(fieldErrMsg, fe.ns, fe.field, fe.tag) |
||||
} |
@ -1,83 +1,83 @@ |
||||
package validator_test |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"gopkg.in/go-playground/validator.v8" |
||||
) |
||||
|
||||
func ExampleValidate_new() { |
||||
config := &validator.Config{TagName: "validate"} |
||||
|
||||
validator.New(config) |
||||
} |
||||
|
||||
func ExampleValidate_field() { |
||||
// This should be stored somewhere globally
|
||||
var validate *validator.Validate |
||||
|
||||
config := &validator.Config{TagName: "validate"} |
||||
|
||||
validate = validator.New(config) |
||||
|
||||
i := 0 |
||||
errs := validate.Field(i, "gt=1,lte=10") |
||||
err := errs.(validator.ValidationErrors)[""] |
||||
fmt.Println(err.Field) |
||||
fmt.Println(err.Tag) |
||||
fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time
|
||||
fmt.Println(err.Type) |
||||
fmt.Println(err.Param) |
||||
fmt.Println(err.Value) |
||||
//Output:
|
||||
//
|
||||
//gt
|
||||
//int
|
||||
//int
|
||||
//1
|
||||
//0
|
||||
} |
||||
|
||||
func ExampleValidate_struct() { |
||||
// This should be stored somewhere globally
|
||||
var validate *validator.Validate |
||||
|
||||
config := &validator.Config{TagName: "validate"} |
||||
|
||||
validate = validator.New(config) |
||||
|
||||
type ContactInformation struct { |
||||
Phone string `validate:"required"` |
||||
Street string `validate:"required"` |
||||
City string `validate:"required"` |
||||
} |
||||
|
||||
type User struct { |
||||
Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,)
|
||||
Age int8 `validate:"required,gt=0,lt=150"` |
||||
Email string `validate:"email"` |
||||
ContactInformation []*ContactInformation |
||||
} |
||||
|
||||
contactInfo := &ContactInformation{ |
||||
Street: "26 Here Blvd.", |
||||
City: "Paradeso", |
||||
} |
||||
|
||||
user := &User{ |
||||
Name: "Joey Bloggs", |
||||
Age: 31, |
||||
Email: "joeybloggs@gmail.com", |
||||
ContactInformation: []*ContactInformation{contactInfo}, |
||||
} |
||||
|
||||
errs := validate.Struct(user) |
||||
for _, v := range errs.(validator.ValidationErrors) { |
||||
fmt.Println(v.Field) // Phone
|
||||
fmt.Println(v.Tag) // required
|
||||
//... and so forth
|
||||
//Output:
|
||||
//Phone
|
||||
//required
|
||||
} |
||||
} |
||||
// import (
|
||||
// "fmt"
|
||||
|
||||
// "gopkg.in/go-playground/validator.v8"
|
||||
// )
|
||||
|
||||
// func ExampleValidate_new() {
|
||||
// config := &validator.Config{TagName: "validate"}
|
||||
|
||||
// validator.New(config)
|
||||
// }
|
||||
|
||||
// func ExampleValidate_field() {
|
||||
// // This should be stored somewhere globally
|
||||
// var validate *validator.Validate
|
||||
|
||||
// config := &validator.Config{TagName: "validate"}
|
||||
|
||||
// validate = validator.New(config)
|
||||
|
||||
// i := 0
|
||||
// errs := validate.Field(i, "gt=1,lte=10")
|
||||
// err := errs.(validator.ValidationErrors)[""]
|
||||
// fmt.Println(err.Field)
|
||||
// fmt.Println(err.Tag)
|
||||
// fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time
|
||||
// fmt.Println(err.Type)
|
||||
// fmt.Println(err.Param)
|
||||
// fmt.Println(err.Value)
|
||||
// //Output:
|
||||
// //
|
||||
// //gt
|
||||
// //int
|
||||
// //int
|
||||
// //1
|
||||
// //0
|
||||
// }
|
||||
|
||||
// func ExampleValidate_struct() {
|
||||
// // This should be stored somewhere globally
|
||||
// var validate *validator.Validate
|
||||
|
||||
// config := &validator.Config{TagName: "validate"}
|
||||
|
||||
// validate = validator.New(config)
|
||||
|
||||
// type ContactInformation struct {
|
||||
// Phone string `validate:"required"`
|
||||
// Street string `validate:"required"`
|
||||
// City string `validate:"required"`
|
||||
// }
|
||||
|
||||
// type User struct {
|
||||
// Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,)
|
||||
// Age int8 `validate:"required,gt=0,lt=150"`
|
||||
// Email string `validate:"email"`
|
||||
// ContactInformation []*ContactInformation
|
||||
// }
|
||||
|
||||
// contactInfo := &ContactInformation{
|
||||
// Street: "26 Here Blvd.",
|
||||
// City: "Paradeso",
|
||||
// }
|
||||
|
||||
// user := &User{
|
||||
// Name: "Joey Bloggs",
|
||||
// Age: 31,
|
||||
// Email: "joeybloggs@gmail.com",
|
||||
// ContactInformation: []*ContactInformation{contactInfo},
|
||||
// }
|
||||
|
||||
// errs := validate.Struct(user)
|
||||
// for _, v := range errs.(validator.ValidationErrors) {
|
||||
// fmt.Println(v.Field) // Phone
|
||||
// fmt.Println(v.Tag) // required
|
||||
// //... and so forth
|
||||
// //Output:
|
||||
// //Phone
|
||||
// //required
|
||||
// }
|
||||
// }
|
||||
|
@ -0,0 +1,50 @@ |
||||
package validator |
||||
|
||||
import "reflect" |
||||
|
||||
// FieldLevel contains all the information and helper functions
|
||||
// to validate a field
|
||||
type FieldLevel interface { |
||||
|
||||
// returns the top level struct, if any
|
||||
Top() reflect.Value |
||||
|
||||
// returns the current fields parent struct, if any
|
||||
Parent() reflect.Value |
||||
|
||||
// returns current field for validation
|
||||
Field() reflect.Value |
||||
|
||||
// returns param for validation against current field
|
||||
Param() string |
||||
|
||||
// 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) |
||||
|
||||
// traverses the parent struct to retrieve a specific field denoted by the provided namespace
|
||||
// in the param and returns the field, field kind and whether is was successful in retrieving
|
||||
// the field at all.
|
||||
//
|
||||
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
|
||||
// could not be retrieved because it didn't exist.
|
||||
GetStructFieldOK() (reflect.Value, reflect.Kind, bool) |
||||
} |
||||
|
||||
var _ FieldLevel = new(validate) |
||||
|
||||
// Field returns current field for validation
|
||||
func (v *validate) Field() reflect.Value { |
||||
return v.flField |
||||
} |
||||
|
||||
// Param returns param for validation against current field
|
||||
func (v *validate) Param() string { |
||||
return v.flParam |
||||
} |
||||
|
||||
// GetStructFieldOK returns Param returns param for validation against current field
|
||||
func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) { |
||||
return v.getStructFieldOKInternal(v.slflParent, v.flParam) |
||||
} |
@ -0,0 +1,166 @@ |
||||
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.
|
||||
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
|
||||
func (v *validate) ReportError(field interface{}, fieldName, altName, tag string) { |
||||
|
||||
fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false) |
||||
|
||||
if len(altName) == 0 { |
||||
altName = fieldName |
||||
} |
||||
|
||||
ns := append(v.slNs, fieldName...) |
||||
nsActual := append(v.slActualNs, altName...) |
||||
|
||||
switch kind { |
||||
case reflect.Invalid: |
||||
|
||||
v.errs = append(v.errs, |
||||
&fieldError{ |
||||
tag: tag, |
||||
actualTag: tag, |
||||
ns: string(ns), |
||||
actualNs: string(nsActual), |
||||
field: fieldName, |
||||
actualField: altName, |
||||
param: "", |
||||
kind: kind, |
||||
}, |
||||
) |
||||
|
||||
default: |
||||
|
||||
v.errs = append(v.errs, |
||||
&fieldError{ |
||||
tag: tag, |
||||
actualTag: tag, |
||||
ns: string(ns), |
||||
actualNs: string(nsActual), |
||||
field: fieldName, |
||||
actualField: altName, |
||||
value: fv.Interface(), |
||||
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, relativeActualNamespace string, errs ValidationErrors) { |
||||
|
||||
var err *fieldError |
||||
|
||||
for i := 0; i < len(errs); i++ { |
||||
|
||||
err = errs[i].(*fieldError) |
||||
err.ns = string(append(append(v.slNs, err.ns...), err.field...)) |
||||
err.actualNs = string(append(append(v.slActualNs, err.actualNs...), err.actualField...)) |
||||
|
||||
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) |
||||
} |
@ -0,0 +1,442 @@ |
||||
package validator |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"reflect" |
||||
"strings" |
||||
"sync" |
||||
"time" |
||||
) |
||||
|
||||
const ( |
||||
defaultTagName = "validate" |
||||
utf8HexComma = "0x2C" |
||||
utf8Pipe = "0x7C" |
||||
tagSeparator = "," |
||||
orSeparator = "|" |
||||
tagKeySeparator = "=" |
||||
structOnlyTag = "structonly" |
||||
noStructLevelTag = "nostructlevel" |
||||
omitempty = "omitempty" |
||||
skipValidationTag = "-" |
||||
diveTag = "dive" |
||||
namespaceSeparator = "." |
||||
leftBracket = "[" |
||||
rightBracket = "]" |
||||
restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" |
||||
restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" |
||||
restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" |
||||
) |
||||
|
||||
var ( |
||||
timeType = reflect.TypeOf(time.Time{}) |
||||
defaultCField = new(cField) |
||||
) |
||||
|
||||
// CustomTypeFunc allows for overriding or adding custom field type handler functions
|
||||
// field = field value of the type to return a value to be validated
|
||||
// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
|
||||
type CustomTypeFunc func(field reflect.Value) interface{} |
||||
|
||||
// TagNameFunc allows for adding of a custom tag name parser
|
||||
type TagNameFunc func(field reflect.StructField) string |
||||
|
||||
// Validate contains the validator settings and cache
|
||||
type Validate struct { |
||||
tagName string |
||||
pool *sync.Pool |
||||
hasCustomFuncs bool |
||||
hasTagNameFunc bool |
||||
tagNameFunc TagNameFunc |
||||
structLevelFuncs map[reflect.Type]StructLevelFunc |
||||
customFuncs map[reflect.Type]CustomTypeFunc |
||||
aliases map[string]string |
||||
validations map[string]Func |
||||
tagCache *tagCache |
||||
structCache *structCache |
||||
} |
||||
|
||||
// New returns a new instacne of 'validate' with sane defaults.
|
||||
func New() *Validate { |
||||
|
||||
tc := new(tagCache) |
||||
tc.m.Store(make(map[string]*cTag)) |
||||
|
||||
sc := new(structCache) |
||||
sc.m.Store(make(map[reflect.Type]*cStruct)) |
||||
|
||||
v := &Validate{ |
||||
tagName: defaultTagName, |
||||
aliases: make(map[string]string, len(bakedInAliases)), |
||||
validations: make(map[string]Func, len(bakedInValidators)), |
||||
tagCache: tc, |
||||
structCache: sc, |
||||
} |
||||
|
||||
// must copy alias validators for separate validations to be used in each validator instance
|
||||
for k, val := range bakedInAliases { |
||||
v.RegisterAlias(k, val) |
||||
} |
||||
|
||||
// must copy validators for separate validations to be used in each instance
|
||||
for k, val := range bakedInValidators { |
||||
|
||||
// no need to error check here, baked in will alwaays be valid
|
||||
v.RegisterValidation(k, val) |
||||
} |
||||
|
||||
v.pool = &sync.Pool{ |
||||
New: func() interface{} { |
||||
return &validate{ |
||||
v: v, |
||||
} |
||||
}, |
||||
} |
||||
|
||||
return v |
||||
} |
||||
|
||||
// SetTagName allows for changing of the default tag name of 'validate'
|
||||
func (v *Validate) SetTagName(name string) { |
||||
v.tagName = name |
||||
} |
||||
|
||||
// RegisterTagNameFunc registers a function to get another name from the
|
||||
// StructField eg. the JSON name
|
||||
func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { |
||||
v.tagNameFunc = fn |
||||
v.hasTagNameFunc = true |
||||
} |
||||
|
||||
// RegisterValidation adds a validation with the given tag
|
||||
//
|
||||
// NOTES:
|
||||
// - if the key already exists, the previous validation function will be replaced.
|
||||
// - this method is not thread-safe it is intended that these all be registered prior to any validation
|
||||
func (v *Validate) RegisterValidation(tag string, fn Func) error { |
||||
|
||||
if len(tag) == 0 { |
||||
return errors.New("Function Key cannot be empty") |
||||
} |
||||
|
||||
if fn == nil { |
||||
return errors.New("Function cannot be empty") |
||||
} |
||||
|
||||
_, ok := restrictedTags[tag] |
||||
|
||||
if ok || strings.ContainsAny(tag, restrictedTagChars) { |
||||
panic(fmt.Sprintf(restrictedTagErr, tag)) |
||||
} |
||||
|
||||
v.validations[tag] = fn |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// RegisterAlias registers a mapping of a single validation tag that
|
||||
// defines a common or complex set of validation(s) to simplify adding validation
|
||||
// to structs.
|
||||
//
|
||||
// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
|
||||
func (v *Validate) RegisterAlias(alias, tags string) { |
||||
|
||||
_, ok := restrictedTags[alias] |
||||
|
||||
if ok || strings.ContainsAny(alias, restrictedTagChars) { |
||||
panic(fmt.Sprintf(restrictedAliasErr, alias)) |
||||
} |
||||
|
||||
v.aliases[alias] = tags |
||||
} |
||||
|
||||
// RegisterStructValidation registers a StructLevelFunc against a number of types.
|
||||
// This is akin to implementing the 'Validatable' interface, but for structs for which
|
||||
// you may not have access or rights to change.
|
||||
//
|
||||
// NOTES:
|
||||
// - if this and the 'Validatable' interface are implemented the Struct Level takes precedence as to enable
|
||||
// a struct out of your control's validation to be overridden
|
||||
// - this method is not thread-safe it is intended that these all be registered prior to any validation
|
||||
func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) { |
||||
|
||||
if v.structLevelFuncs == nil { |
||||
v.structLevelFuncs = make(map[reflect.Type]StructLevelFunc) |
||||
} |
||||
|
||||
for _, t := range types { |
||||
v.structLevelFuncs[reflect.TypeOf(t)] = fn |
||||
} |
||||
} |
||||
|
||||
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
|
||||
//
|
||||
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
|
||||
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { |
||||
|
||||
if v.customFuncs == nil { |
||||
v.customFuncs = make(map[reflect.Type]CustomTypeFunc) |
||||
} |
||||
|
||||
for _, t := range types { |
||||
v.customFuncs[reflect.TypeOf(t)] = fn |
||||
} |
||||
} |
||||
|
||||
// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
|
||||
func (v *Validate) Struct(s interface{}) (err error) { |
||||
|
||||
val := reflect.ValueOf(s) |
||||
top := val |
||||
|
||||
if val.Kind() == reflect.Ptr && !val.IsNil() { |
||||
val = val.Elem() |
||||
} |
||||
|
||||
typ := val.Type() |
||||
|
||||
if val.Kind() != reflect.Struct || typ == timeType { |
||||
return &InvalidValidationError{Type: typ} |
||||
} |
||||
|
||||
// good to validate
|
||||
vd := v.pool.Get().(*validate) |
||||
vd.top = top |
||||
vd.isPartial = false |
||||
// vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
|
||||
|
||||
vd.validateStruct(top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) |
||||
|
||||
if len(vd.errs) > 0 { |
||||
err = vd.errs |
||||
vd.errs = nil |
||||
} |
||||
|
||||
v.pool.Put(vd) |
||||
|
||||
return |
||||
} |
||||
|
||||
// StructPartial validates the fields passed in only, ignoring all others.
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
|
||||
func (v *Validate) StructPartial(s interface{}, fields ...string) (err error) { |
||||
|
||||
val := reflect.ValueOf(s) |
||||
top := val |
||||
|
||||
if val.Kind() == reflect.Ptr && !val.IsNil() { |
||||
val = val.Elem() |
||||
} |
||||
|
||||
typ := val.Type() |
||||
|
||||
if val.Kind() != reflect.Struct || typ == timeType { |
||||
return &InvalidValidationError{Type: typ} |
||||
} |
||||
|
||||
// good to validate
|
||||
vd := v.pool.Get().(*validate) |
||||
vd.top = top |
||||
vd.isPartial = true |
||||
vd.hasExcludes = false |
||||
vd.includeExclude = make(map[string]struct{}) |
||||
|
||||
name := typ.Name() |
||||
|
||||
if fields != nil { |
||||
for _, k := range fields { |
||||
|
||||
flds := strings.Split(k, namespaceSeparator) |
||||
if len(flds) > 0 { |
||||
|
||||
key := name + namespaceSeparator |
||||
for _, s := range flds { |
||||
|
||||
idx := strings.Index(s, leftBracket) |
||||
|
||||
if idx != -1 { |
||||
for idx != -1 { |
||||
key += s[:idx] |
||||
vd.includeExclude[key] = struct{}{} |
||||
|
||||
idx2 := strings.Index(s, rightBracket) |
||||
idx2++ |
||||
key += s[idx:idx2] |
||||
vd.includeExclude[key] = struct{}{} |
||||
s = s[idx2:] |
||||
idx = strings.Index(s, leftBracket) |
||||
} |
||||
} else { |
||||
|
||||
key += s |
||||
vd.includeExclude[key] = struct{}{} |
||||
} |
||||
|
||||
key += namespaceSeparator |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
vd.validateStruct(top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) |
||||
|
||||
if len(vd.errs) > 0 { |
||||
err = vd.errs |
||||
vd.errs = nil |
||||
} |
||||
|
||||
v.pool.Put(vd) |
||||
|
||||
return |
||||
} |
||||
|
||||
// StructExcept validates all fields except the ones passed in.
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
|
||||
func (v *Validate) StructExcept(s interface{}, fields ...string) (err error) { |
||||
|
||||
val := reflect.ValueOf(s) |
||||
top := val |
||||
|
||||
if val.Kind() == reflect.Ptr && !val.IsNil() { |
||||
val = val.Elem() |
||||
} |
||||
|
||||
typ := val.Type() |
||||
|
||||
if val.Kind() != reflect.Struct || typ == timeType { |
||||
return &InvalidValidationError{Type: typ} |
||||
} |
||||
|
||||
// good to validate
|
||||
vd := v.pool.Get().(*validate) |
||||
vd.top = top |
||||
vd.isPartial = true |
||||
vd.hasExcludes = true |
||||
vd.includeExclude = make(map[string]struct{}) |
||||
|
||||
name := typ.Name() |
||||
|
||||
for _, key := range fields { |
||||
vd.includeExclude[name+namespaceSeparator+key] = struct{}{} |
||||
} |
||||
|
||||
vd.validateStruct(top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) |
||||
|
||||
if len(vd.errs) > 0 { |
||||
err = vd.errs |
||||
vd.errs = nil |
||||
} |
||||
|
||||
v.pool.Put(vd) |
||||
|
||||
return |
||||
} |
||||
|
||||
// func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns []byte, actualNs []byte, cf *cField, ct *cTag) {
|
||||
|
||||
// Var validates a single variable using tag style validation.
|
||||
// eg.
|
||||
// var i int
|
||||
// validate.Var(i, "gt=1,lt=10")
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
|
||||
// validate Array, Slice and maps fields which may contain more than one error
|
||||
func (v *Validate) Var(field interface{}, tag string) (err error) { |
||||
|
||||
if len(tag) == 0 || tag == skipValidationTag { |
||||
return nil |
||||
} |
||||
|
||||
// find cached tag
|
||||
ctag, ok := v.tagCache.Get(tag) |
||||
if !ok { |
||||
v.tagCache.lock.Lock() |
||||
defer v.tagCache.lock.Unlock() |
||||
|
||||
// could have been multiple trying to access, but once first is done this ensures tag
|
||||
// isn't parsed again.
|
||||
ctag, ok = v.tagCache.Get(tag) |
||||
if !ok { |
||||
ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false) |
||||
v.tagCache.Set(tag, ctag) |
||||
} |
||||
} |
||||
|
||||
val := reflect.ValueOf(field) |
||||
|
||||
vd := v.pool.Get().(*validate) |
||||
vd.top = val |
||||
vd.isPartial = false |
||||
|
||||
vd.traverseField(val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag) |
||||
|
||||
if len(vd.errs) > 0 { |
||||
err = vd.errs |
||||
vd.errs = nil |
||||
} |
||||
|
||||
v.pool.Put(vd) |
||||
|
||||
return |
||||
} |
||||
|
||||
// VarWithValue validates a single variable, against another variable/field's value using tag style validation
|
||||
// eg.
|
||||
// s1 := "abcd"
|
||||
// s2 := "abcd"
|
||||
// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
|
||||
// validate Array, Slice and maps fields which may contain more than one error
|
||||
func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) (err error) { |
||||
|
||||
if len(tag) == 0 || tag == skipValidationTag { |
||||
return nil |
||||
} |
||||
|
||||
// find cached tag
|
||||
ctag, ok := v.tagCache.Get(tag) |
||||
if !ok { |
||||
v.tagCache.lock.Lock() |
||||
defer v.tagCache.lock.Unlock() |
||||
|
||||
// could have been multiple trying to access, but once first is done this ensures tag
|
||||
// isn't parsed again.
|
||||
ctag, ok = v.tagCache.Get(tag) |
||||
if !ok { |
||||
ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false) |
||||
v.tagCache.Set(tag, ctag) |
||||
} |
||||
} |
||||
|
||||
otherVal := reflect.ValueOf(other) |
||||
|
||||
vd := v.pool.Get().(*validate) |
||||
vd.top = otherVal |
||||
vd.isPartial = false |
||||
|
||||
vd.traverseField(otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag) |
||||
|
||||
if len(vd.errs) > 0 { |
||||
err = vd.errs |
||||
vd.errs = nil |
||||
} |
||||
|
||||
v.pool.Put(vd) |
||||
|
||||
return |
||||
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue