Updated documentation for new v6

pull/114/head
joeybloggs 10 years ago
parent 933fe0b7a9
commit 41b4a43989
  1. 62
      README.md
  2. 105
      doc.go
  3. 34
      examples/simple.go
  4. 177
      examples_test.go
  5. 1
      regexes.go
  6. 86
      validator.go

@ -2,9 +2,9 @@ Package validator
================
[![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v5.1)](https://travis-ci.org/bluesuncorp/validator)
[![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v5)](https://coveralls.io/r/bluesuncorp/validator?branch=v5)
[![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v5?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v5)
[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v6)](https://travis-ci.org/bluesuncorp/validator)
[![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v6)](https://coveralls.io/r/bluesuncorp/validator?branch=v6)
[![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v6?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v6)
Package validator implements value validations for structs and individual fields based on tags.
@ -19,20 +19,20 @@ Installation
Use go get.
go get gopkg.in/bluesuncorp/validator.v5
go get gopkg.in/bluesuncorp/validator.v6
or to update
go get -u gopkg.in/bluesuncorp/validator.v5
go get -u gopkg.in/bluesuncorp/validator.v6
Then import the validator package into your own code.
import "gopkg.in/bluesuncorp/validator.v5"
import "gopkg.in/bluesuncorp/validator.v6"
Usage and documentation
------
Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v5 for detailed usage docs.
Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v6 for detailed usage docs.
##### Example:
```go
@ -41,7 +41,7 @@ package main
import (
"fmt"
"gopkg.in/bluesuncorp/validator.v5"
"gopkg.in/bluesuncorp/validator.v6"
)
// User contains user information
@ -66,7 +66,12 @@ var validate *validator.Validate
func main() {
validate = validator.New("validate", validator.BakedInValidators)
config := validator.Config{
TagName: "validate",
ValidationFuncs: validator.BakedInValidators,
}
validate = validator.New(config)
address := &Address{
Street: "Eavesdown Docks",
@ -83,31 +88,14 @@ func main() {
Addresses: []*Address{address},
}
// returns nil or *StructErrors
// returns nil or ValidationErrors ( map[string]*FieldError )
errs := validate.Struct(user)
if errs != nil {
// err will be of type *FieldError
err := errs.Errors["Age"]
fmt.Println(err.Error()) // output: Field validation for "Age" failed on the "lte" tag
fmt.Println(err.Field) // output: Age
fmt.Println(err.Tag) // output: lte
fmt.Println(err.Kind) // output: uint8
fmt.Println(err.Type) // output: uint8
fmt.Println(err.Param) // output: 130
fmt.Println(err.Value) // output: 135
// or if you prefer you can use the Flatten function
// NOTE: I find this usefull when using a more hard static approach of checking field errors.
// The above, is best for passing to some generic code to say parse the errors. i.e. I pass errs
// to a routine which loops through the errors, creates and translates the error message into the
// users locale and returns a map of map[string]string // field and error which I then use
// within the HTML rendering.
flat := errs.Flatten()
fmt.Println(flat) // output: map[Age:Field validation for "Age" failed on the "lte" tag Addresses[0].Address.City:Field validation for "City" failed on the "required" tag]
err = flat["Addresses[0].Address.City"]
fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag
// Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag
err := errs["User.Addresses[0].City"]
fmt.Println(err.Field) // output: City
fmt.Println(err.Tag) // output: required
fmt.Println(err.Kind) // output: string
@ -126,14 +114,18 @@ func main() {
Benchmarks
------
###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3
NOTE: allocations for structs are up from v5, however ns/op for parallel operations are way down.
It was a decicion not to cache struct info because although it reduced allocation to v5 levels, it
hurt parallel performance too much.
```go
$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkValidateField-4 3000000 429 ns/op 192 B/op 2 allocs/op
BenchmarkValidateStructSimple-4 500000 2877 ns/op 657 B/op 10 allocs/op
BenchmarkTemplateParallelSimple-4 500000 3097 ns/op 657 B/op 10 allocs/op
BenchmarkValidateStructLarge-4 100000 15228 ns/op 4350 B/op 62 allocs/op
BenchmarkTemplateParallelLarge-4 100000 14257 ns/op 4354 B/op 62 allocs/op
BenchmarkField-4 5000000 314 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTag-4 500000 2425 ns/op 20 B/op 2 allocs/op
BenchmarkStructSimple-4 500000 3117 ns/op 553 B/op 14 allocs/op
BenchmarkStructSimpleParallel-4 1000000 1149 ns/op 553 B/op 14 allocs/op
BenchmarkStructComplex-4 100000 19580 ns/op 3230 B/op 102 allocs/op
BenchmarkStructComplexParallel-4 200000 6686 ns/op 3232 B/op 102 allocs/op
```
How to Contribute

105
doc.go

@ -1,59 +1,9 @@
/*
Package validator implements value validations for structs and individual fields based on tags. It can also handle Cross Field and Cross Struct validation for nested structs.
Package validator implements value validations for structs and individual fields based on tags.
It can also handle Cross Field and Cross Struct validation for nested structs and has the ability
to dive into arrays and maps of any type.
Validate
validate := validator.New("validate", validator.BakedInValidators)
errs := validate.Struct(//your struct)
valErr := validate.Field(field, "omitempty,min=1,max=10")
A simple example usage:
type UserDetail struct {
Details string `validate:"-"`
}
type User struct {
Name string `validate:"required,max=60"`
PreferedName string `validate:"omitempty,max=60"`
Sub UserDetail
}
user := &User {
Name: "",
}
// errs will contain a hierarchical list of errors
// using the StructErrors struct
// or nil if no errors exist
errs := validate.Struct(user)
// in this case 1 error Name is required
errs.Struct will be "User"
errs.StructErrors will be empty <-- fields that were structs
errs.Errors will have 1 error of type FieldError
NOTE: Anonymous Structs - they don't have names so expect the Struct name
within StructErrors to be blank.
Error Handling
The error can be used like so
fieldErr, _ := errs["Name"]
fieldErr.Field // "Name"
fieldErr.ErrorTag // "required"
Both StructErrors and FieldError implement the Error interface but it's
intended use is for development + debugging, not a production error message.
fieldErr.Error() // Field validation for "Name" failed on the "required" tag
errs.Error()
// Struct: User
// Field validation for "Name" failed on the "required" tag
Why not a better error message? because this library intends for you to handle your own error messages
Why not a better error message? because this library intends for you to handle your own error messages.
Why should I handle my own errors? Many reasons, for us building an internationalized application
I needed to know the field and what validation failed so that I could provide an error in the users specific language.
@ -66,21 +16,12 @@ I needed to know the field and what validation failed so that I could provide an
return "Translated string based on field"
}
The hierarchical error structure is hard to work with sometimes.. Agreed Flatten function to the rescue!
Flatten will return a map of FieldError's but the field name will be namespaced.
// if UserDetail Details field failed validation
Field will be "Sub.Details"
// for Name
Field will be "Name"
Custom Functions
Custom functions can be added
//Structure
func customFunc(top interface{}, current interface{}, field interface{}, param string) bool {
// Structure
func customFunc(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if whatever {
return false
@ -89,7 +30,7 @@ Custom functions can be added
return true
}
validate.AddFunction("custom tag name", customFunc)
validate.RegisterValidation("custom tag name", customFunc)
// NOTES: using the same tag name as an existing function
// will overwrite the existing one
@ -97,7 +38,7 @@ Cross Field Validation
Cross Field Validation can be implemented, for example Start & End Date range validation
// NOTE: when calling validate.Struct(val) val will be the top level struct passed
// NOTE: when calling validate.Struct(val) topStruct will be the top level struct passed
// into the function
// when calling validate.FieldWithValue(val, field, tag) val will be
// whatever you pass, struct, field...
@ -106,18 +47,7 @@ Cross Field Validation can be implemented, for example Start & End Date range va
// Because of the specific requirements and field names within each persons project that
// uses this library it is likely that custom functions will need to be created for your
// Cross Field Validation needs, however there are some build in Generic Cross Field validations,
// see Baked In Validators and Tags below
func isDateRangeValid(val interface{}, field interface{}, param string) bool {
myStruct := val.(myStructType)
if myStruct.Start.After(field.(time.Time)) {
return false
}
return true
}
// see Baked In Validators eqfield, nefield, gtfield, gtefield, ltfield, ltefield and Tags below
Multiple Validators
@ -135,7 +65,7 @@ Bad Validator definitions are not handled by the library
Field `validate:"min=10,max=0"`
}
// this definition of min max will never validate
// this definition of min max will never succeed
Baked In Validators and Tags
@ -148,6 +78,11 @@ included within the parameter i.e. excludesall=, you will need to use the UTF-8
representation 0x2C, which is replaced in the code as a comma, so the above will
become excludesall=0x2C
NOTE3: pipe is the default separator of or validation tags, if you wish to have a pipe
included within the parameter i.e. excludesall=| you will need to use the UTF-8 hex
representation 0x7C, which is replaced in the code as a pipe, so the above will
become excludesall=0x7C
Here is a list of the current built in validators:
-
@ -162,14 +97,14 @@ Here is a list of the current built in validators:
structonly
When a field that is a nest struct in encountered and contains this flag
any validation on the nested struct such as "required" will be run, but
none of the nested struct fields will be validated. This is usefull if
inside of you program you know the struct will be valid, but need to
verify it has been assigned.
any validation on the nested struct will be run, but none of the nested
struct fields will be validated. This is usefull if inside of you program
you know the struct will be valid, but need to verify it has been assigned.
NOTE: only "required" and "omitempty" can be used on a struct itself.
omitempty
Allows conditional validation, for example if a field is not set with
a value (Determined by the required validator) then other validation
a value (Determined by the "required" validator) then other validation
such as min or max won't run, but if a value is set validation will run.
(Usage: omitempty)

@ -3,7 +3,7 @@ package main
import (
"fmt"
"gopkg.in/bluesuncorp/validator.v5"
"gopkg.in/bluesuncorp/validator.v6"
)
// User contains user information
@ -28,7 +28,12 @@ var validate *validator.Validate
func main() {
validate = validator.New("validate", validator.BakedInValidators)
config := validator.Config{
TagName: "validate",
ValidationFuncs: validator.BakedInValidators,
}
validate = validator.New(config)
address := &Address{
Street: "Eavesdown Docks",
@ -45,31 +50,14 @@ func main() {
Addresses: []*Address{address},
}
// returns nil or *StructErrors
// returns nil or ValidationErrors ( map[string]*FieldError )
errs := validate.Struct(user)
if errs != nil {
// err will be of type *FieldError
err := errs.Errors["Age"]
fmt.Println(err.Error()) // output: Field validation for "Age" failed on the "lte" tag
fmt.Println(err.Field) // output: Age
fmt.Println(err.Tag) // output: lte
fmt.Println(err.Kind) // output: uint8
fmt.Println(err.Type) // output: uint8
fmt.Println(err.Param) // output: 130
fmt.Println(err.Value) // output: 135
// or if you prefer you can use the Flatten function
// NOTE: I find this usefull when using a more hard static approach of checking field errors.
// The above, is best for passing to some generic code to say parse the errors. i.e. I pass errs
// to a routine which loops through the errors, creates and translates the error message into the
// users locale and returns a map of map[string]string // field and error which I then use
// within the HTML rendering.
flat := errs.Flatten()
fmt.Println(flat) // output: map[Age:Field validation for "Age" failed on the "lte" tag Addresses[0].Address.City:Field validation for "City" failed on the "required" tag]
err = flat["Addresses[0].Address.City"]
fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag
// Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag
err := errs["User.Addresses[0].City"]
fmt.Println(err.Field) // output: City
fmt.Println(err.Tag) // output: required
fmt.Println(err.Kind) // output: string

@ -1,89 +1,92 @@
package validator_test
// func ExampleValidate_new() {
// validator.New("validate", validator.BakedInValidators)
// }
// func ExampleValidate_addFunction() {
// // This should be stored somewhere globally
// var validate *validator.Validate
// validate = validator.New("validate", validator.BakedInValidators)
// fn := func(top interface{}, current interface{}, field interface{}, param string) bool {
// return field.(string) == "hello"
// }
// validate.AddFunction("valueishello", fn)
// message := "hello"
// err := validate.Field(message, "valueishello")
// fmt.Println(err)
// //Output:
// //<nil>
// }
// func ExampleValidate_field() {
// // This should be stored somewhere globally
// var validate *validator.Validate
// validate = validator.New("validate", validator.BakedInValidators)
// i := 0
// err := validate.Field(i, "gt=1,lte=10")
// 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
// validate = validator.New("validate", validator.BakedInValidators)
// 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},
// }
// structError := validate.Struct(user)
// for _, fieldError := range structError.Errors {
// fmt.Println(fieldError.Field) // Phone
// fmt.Println(fieldError.Tag) // required
// //... and so forth
// //Output:
// //Phone
// //required
// }
// }
import (
"fmt"
"../validator"
)
func ExampleValidate_new() {
config := validator.Config{
TagName: "validate",
ValidationFuncs: validator.BakedInValidators,
}
validator.New(config)
}
func ExampleValidate_field() {
// This should be stored somewhere globally
var validate *validator.Validate
config := validator.Config{
TagName: "validate",
ValidationFuncs: validator.BakedInValidators,
}
validate = validator.New(config)
i := 0
errs := validate.Field(i, "gt=1,lte=10")
err := errs[""]
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",
ValidationFuncs: validator.BakedInValidators,
}
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 {
fmt.Println(v.Field) // Phone
fmt.Println(v.Tag) // required
//... and so forth
//Output:
//Phone
//required
}
}

@ -59,6 +59,5 @@ var (
)
func matchesRegex(regex *regexp.Regexp, value string) bool {
// fieldAsString := field.(string) //this will panic inherently
return regex.MatchString(value)
}

@ -48,16 +48,36 @@ func newValidationErrors() interface{} {
return map[string]*FieldError{}
}
// Validate implements the Validate Struct
// NOTE: Fields within are not thread safe and that is on purpose
// Functions and Tags should all be predifined before use, so subscribe to the philosiphy
// or make it thread safe on your end
type tagCache struct {
tagVals [][]string
isOrVal bool
}
type tagCacheMap struct {
lock sync.RWMutex
m map[string][]*tagCache
}
func (s *tagCacheMap) Get(key string) ([]*tagCache, bool) {
s.lock.RLock()
defer s.lock.RUnlock()
value, ok := s.m[key]
return value, ok
}
func (s *tagCacheMap) Set(key string, value []*tagCache) {
s.lock.Lock()
defer s.lock.Unlock()
s.m[key] = value
}
// Validate contains the validator settings passed in using the Config struct
type Validate struct {
config Config
}
// Config contains the options that Validator with use
// passed to the New function
// Config contains the options that a Validator instance will use.
// It is passed to the New() function
type Config struct {
TagName string
ValidationFuncs map[string]Func
@ -71,20 +91,21 @@ type Config struct {
type Func func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool
// ValidationErrors is a type of map[string]*FieldError
// it exists to allow for multiple errors passed from this library
// and yet still comply to the error interface
// it exists to allow for multiple errors to be passed from this library
// and yet still subscribe to the error interface
type ValidationErrors map[string]*FieldError
// This is intended for use in development + debugging and not intended to be a production error message.
// 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 in the ValidationErrors
// the FieldError found within the ValidationErrors map
func (ve ValidationErrors) Error() string {
buff := bytes.NewBufferString("")
for key, err := range ve {
buff.WriteString(fmt.Sprintf(fieldErrMsg, key, err.Field, err.Tag))
buff.WriteString("\n")
}
return strings.TrimSpace(buff.String())
@ -107,7 +128,7 @@ func New(config Config) *Validate {
}
// RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key
// NOTE: if the key already exists, it will get replaced.
// NOTE: if the key already exists, the previous validation function will be replaced.
// NOTE: this method is not thread-safe
func (v *Validate) RegisterValidation(key string, f Func) error {
@ -124,7 +145,9 @@ func (v *Validate) RegisterValidation(key string, f Func) error {
return nil
}
// Field allows validation of a single field, still using tag style validation to check multiple errors
// Field validates a single field using tag style validation and returns ValidationErrors
// NOTE: it returns ValidationErrors instead of a single FieldError because this can also
// validate Array, Slice and maps fields which may contain more than one error
func (v *Validate) Field(field interface{}, tag string) ValidationErrors {
errs := errsPool.Get().(map[string]*FieldError)
@ -140,7 +163,9 @@ func (v *Validate) Field(field interface{}, tag string) ValidationErrors {
return errs
}
// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
// FieldWithValue validates a single field, against another fields value using tag style validation and returns ValidationErrors
// NOTE: it returns ValidationErrors instead of a single FieldError because this can also
// validate Array, Slice and maps fields which may contain more than one error
func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors {
errs := errsPool.Get().(map[string]*FieldError)
@ -156,10 +181,7 @@ func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string
return errs
}
// Struct validates a struct, even it's nested structs, and returns a struct containing the errors
// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
// the Array or Map.
// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
func (v *Validate) Struct(current interface{}) ValidationErrors {
errs := errsPool.Get().(map[string]*FieldError)
@ -175,6 +197,7 @@ func (v *Validate) Struct(current interface{}) ValidationErrors {
return errs
}
// tranverseStruct traverses a structs fields and then passes them to be validated by traverseField
func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, useStructName bool) {
if current.Kind() == reflect.Ptr && !current.IsNil() {
@ -206,29 +229,7 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec
}
}
type tagCache struct {
tagVals [][]string
isOrVal bool
}
type tagCacheMap struct {
lock sync.RWMutex
m map[string][]*tagCache
}
func (s *tagCacheMap) Get(key string) ([]*tagCache, bool) {
s.lock.RLock()
defer s.lock.RUnlock()
value, ok := s.m[key]
return value, ok
}
func (s *tagCacheMap) Set(key string, value []*tagCache) {
s.lock.Lock()
defer s.lock.Unlock()
s.m[key] = value
}
// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) {
if tag == skipValidationTag {
@ -399,6 +400,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
}
}
// traverseSlice traverses a Slice or Array's elements and passes them to traverseField for validation
func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) {
for i := 0; i < current.Len(); i++ {
@ -413,6 +415,7 @@ func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.
}
}
// traverseMap traverses a map's elements and passes them to traverseField for validation
func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) {
for _, key := range current.MapKeys() {
@ -427,8 +430,7 @@ func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Va
}
}
// validateField validates a field based on the provided key tag and param and return true if there is an error false if all ok
// func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, key string, param string, name string) bool {
// validateField validates a field based on the provided tag's key and param values and returns true if there is an error or false if all ok
func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, cTag *tagCache, name string) bool {
if cTag.isOrVal {

Loading…
Cancel
Save