aboutsummaryrefslogtreecommitdiff
path: root/vendor/gopkg.in/bluesuncorp/validator.v5/validator.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gopkg.in/bluesuncorp/validator.v5/validator.go')
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/validator.go935
1 files changed, 935 insertions, 0 deletions
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/validator.go b/vendor/gopkg.in/bluesuncorp/validator.v5/validator.go
new file mode 100644
index 0000000..db01f1f
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/validator.go
@@ -0,0 +1,935 @@
+/**
+ * Package validator
+ *
+ * MISC:
+ * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank
+ *
+ */
+
+package validator
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+ "time"
+ "unicode"
+)
+
+const (
+ utf8HexComma = "0x2C"
+ tagSeparator = ","
+ orSeparator = "|"
+ noValidationTag = "-"
+ tagKeySeparator = "="
+ structOnlyTag = "structonly"
+ omitempty = "omitempty"
+ required = "required"
+ fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag"
+ sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s"
+ mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s"
+ structErrMsg = "Struct:%s\n"
+ diveTag = "dive"
+ // diveSplit = "," + diveTag
+ arrayIndexFieldName = "%s[%d]"
+ mapIndexFieldName = "%s[%v]"
+)
+
+var structPool *pool
+
+// Pool holds a channelStructErrors.
+type pool struct {
+ pool chan *StructErrors
+}
+
+// NewPool creates a new pool of Clients.
+func newPool(max int) *pool {
+ return &pool{
+ pool: make(chan *StructErrors, max),
+ }
+}
+
+// Borrow a StructErrors from the pool.
+func (p *pool) Borrow() *StructErrors {
+ var c *StructErrors
+
+ select {
+ case c = <-p.pool:
+ default:
+ c = &StructErrors{
+ Errors: map[string]*FieldError{},
+ StructErrors: map[string]*StructErrors{},
+ }
+ }
+
+ return c
+}
+
+// Return returns a StructErrors to the pool.
+func (p *pool) Return(c *StructErrors) {
+
+ select {
+ case p.pool <- c:
+ default:
+ // let it go, let it go...
+ }
+}
+
+type cachedTags struct {
+ keyVals [][]string
+ isOrVal bool
+}
+
+type cachedField struct {
+ index int
+ name string
+ tags []*cachedTags
+ tag string
+ kind reflect.Kind
+ typ reflect.Type
+ isTime bool
+ isSliceOrArray bool
+ isMap bool
+ isTimeSubtype bool
+ sliceSubtype reflect.Type
+ mapSubtype reflect.Type
+ sliceSubKind reflect.Kind
+ mapSubKind reflect.Kind
+ dive bool
+ diveTag string
+}
+
+type cachedStruct struct {
+ children int
+ name string
+ kind reflect.Kind
+ fields []*cachedField
+}
+
+type structsCacheMap struct {
+ lock sync.RWMutex
+ m map[reflect.Type]*cachedStruct
+}
+
+func (s *structsCacheMap) Get(key reflect.Type) (*cachedStruct, bool) {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ value, ok := s.m[key]
+ return value, ok
+}
+
+func (s *structsCacheMap) Set(key reflect.Type, value *cachedStruct) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ s.m[key] = value
+}
+
+var structCache = &structsCacheMap{m: map[reflect.Type]*cachedStruct{}}
+
+type fieldsCacheMap struct {
+ lock sync.RWMutex
+ m map[string][]*cachedTags
+}
+
+func (s *fieldsCacheMap) Get(key string) ([]*cachedTags, bool) {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ value, ok := s.m[key]
+ return value, ok
+}
+
+func (s *fieldsCacheMap) Set(key string, value []*cachedTags) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ s.m[key] = value
+}
+
+var fieldsCache = &fieldsCacheMap{m: map[string][]*cachedTags{}}
+
+// FieldError contains a single field's validation error along
+// with other properties that may be needed for error message creation
+type FieldError struct {
+ Field string
+ Tag string
+ Kind reflect.Kind
+ Type reflect.Type
+ Param string
+ Value interface{}
+ IsPlaceholderErr bool
+ IsSliceOrArray bool
+ IsMap bool
+ SliceOrArrayErrs map[int]error // counld be FieldError, StructErrors
+ MapErrs map[interface{}]error // counld be FieldError, StructErrors
+}
+
+// This is intended for use in development + debugging and not intended to be a production error message.
+// it also allows FieldError to be used as an Error interface
+func (e *FieldError) Error() string {
+
+ if e.IsPlaceholderErr {
+
+ buff := bytes.NewBufferString("")
+
+ if e.IsSliceOrArray {
+
+ for j, err := range e.SliceOrArrayErrs {
+ buff.WriteString("\n")
+ buff.WriteString(fmt.Sprintf(sliceErrMsg, e.Field, j, "\n"+err.Error()))
+ }
+
+ } else if e.IsMap {
+
+ for key, err := range e.MapErrs {
+ buff.WriteString(fmt.Sprintf(mapErrMsg, e.Field, key, "\n"+err.Error()))
+ }
+ }
+
+ return strings.TrimSpace(buff.String())
+ }
+
+ return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag)
+}
+
+// StructErrors is hierarchical list of field and struct validation errors
+// for a non hierarchical representation please see the Flatten method for StructErrors
+type StructErrors struct {
+ // Name of the Struct
+ Struct string
+ // Struct Field Errors
+ Errors map[string]*FieldError
+ // Struct Fields of type struct and their errors
+ // key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank
+ StructErrors map[string]*StructErrors
+}
+
+// This is intended for use in development + debugging and not intended to be a production error message.
+// it also allows StructErrors to be used as an Error interface
+func (e *StructErrors) Error() string {
+ buff := bytes.NewBufferString(fmt.Sprintf(structErrMsg, e.Struct))
+
+ for _, err := range e.Errors {
+ buff.WriteString(err.Error())
+ buff.WriteString("\n")
+ }
+
+ for _, err := range e.StructErrors {
+ buff.WriteString(err.Error())
+ }
+
+ return strings.TrimSpace(buff.String())
+}
+
+// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name
+// for those that want/need it
+func (e *StructErrors) Flatten() map[string]*FieldError {
+
+ if e == nil {
+ return nil
+ }
+
+ errs := map[string]*FieldError{}
+
+ for _, f := range e.Errors {
+
+ errs[f.Field] = f
+ }
+
+ for key, val := range e.StructErrors {
+
+ otherErrs := val.Flatten()
+
+ for _, f2 := range otherErrs {
+
+ f2.Field = fmt.Sprintf("%s.%s", key, f2.Field)
+ errs[f2.Field] = f2
+ }
+ }
+
+ return errs
+}
+
+// Func accepts all values needed for file and cross field validation
+// top = top level struct when validating by struct otherwise nil
+// current = current level struct when validating by struct otherwise optional comparison value
+// f = field value for validation
+// param = parameter used in validation i.e. gt=0 param would be 0
+type Func func(top interface{}, current interface{}, f interface{}, param string) bool
+
+// 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 Validate struct {
+ // tagName being used.
+ tagName string
+ // validateFuncs is a map of validation functions and the tag keys
+ validationFuncs map[string]Func
+}
+
+// New creates a new Validate instance for use.
+func New(tagName string, funcs map[string]Func) *Validate {
+
+ structPool = newPool(10)
+
+ return &Validate{
+ tagName: tagName,
+ validationFuncs: funcs,
+ }
+}
+
+// SetTag sets tagName of the Validator to one of your choosing after creation
+// perhaps to dodge a tag name conflict in a specific section of code
+// NOTE: this method is not thread-safe
+func (v *Validate) SetTag(tagName string) {
+ v.tagName = tagName
+}
+
+// SetMaxStructPoolSize sets the struct pools max size. this may be usefull for fine grained
+// performance tuning towards your application, however, the default should be fine for
+// nearly all cases. only increase if you have a deeply nested struct structure.
+// NOTE: this method is not thread-safe
+// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed
+// and the max pool size will be passed into the New function
+func (v *Validate) SetMaxStructPoolSize(max int) {
+ structPool = newPool(max)
+}
+
+// AddFunction 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: this method is not thread-safe
+func (v *Validate) AddFunction(key string, f Func) error {
+
+ if len(key) == 0 {
+ return errors.New("Function Key cannot be empty")
+ }
+
+ if f == nil {
+ return errors.New("Function cannot be empty")
+ }
+
+ v.validationFuncs[key] = f
+
+ return nil
+}
+
+// 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.
+func (v *Validate) Struct(s interface{}) *StructErrors {
+
+ return v.structRecursive(s, s, s)
+}
+
+// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors
+func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors {
+
+ structValue := reflect.ValueOf(s)
+
+ if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
+ return v.structRecursive(top, current, structValue.Elem().Interface())
+ }
+
+ if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
+ panic("interface passed for validation is not a struct")
+ }
+
+ structType := reflect.TypeOf(s)
+
+ var structName string
+ var numFields int
+ var cs *cachedStruct
+ var isCached bool
+
+ cs, isCached = structCache.Get(structType)
+
+ if isCached {
+ structName = cs.name
+ numFields = cs.children
+ } else {
+ structName = structType.Name()
+ numFields = structValue.NumField()
+ cs = &cachedStruct{name: structName, children: numFields}
+ }
+
+ validationErrors := structPool.Borrow()
+ validationErrors.Struct = structName
+
+ for i := 0; i < numFields; i++ {
+
+ var valueField reflect.Value
+ var cField *cachedField
+ var typeField reflect.StructField
+
+ if isCached {
+ cField = cs.fields[i]
+ valueField = structValue.Field(cField.index)
+
+ if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
+ valueField = valueField.Elem()
+ }
+ } else {
+ valueField = structValue.Field(i)
+
+ if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
+ valueField = valueField.Elem()
+ }
+
+ typeField = structType.Field(i)
+
+ cField = &cachedField{index: i, tag: typeField.Tag.Get(v.tagName), isTime: (valueField.Type() == reflect.TypeOf(time.Time{}) || valueField.Type() == reflect.TypeOf(&time.Time{}))}
+
+ if cField.tag == noValidationTag {
+ cs.children--
+ continue
+ }
+
+ // if no validation and not a struct (which may containt fields for validation)
+ if cField.tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) {
+ cs.children--
+ continue
+ }
+
+ cField.name = typeField.Name
+ cField.kind = valueField.Kind()
+ cField.typ = valueField.Type()
+ }
+
+ // this can happen if the first cache value was nil
+ // but the second actually has a value
+ if cField.kind == reflect.Ptr {
+ cField.kind = valueField.Kind()
+ }
+
+ switch cField.kind {
+
+ case reflect.Struct, reflect.Interface:
+
+ if !unicode.IsUpper(rune(cField.name[0])) {
+ cs.children--
+ continue
+ }
+
+ if cField.isTime {
+
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+
+ } else {
+
+ if strings.Contains(cField.tag, structOnlyTag) {
+ cs.children--
+ continue
+ }
+
+ if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {
+
+ if strings.Contains(cField.tag, omitempty) {
+ goto CACHEFIELD
+ }
+
+ tags := strings.Split(cField.tag, tagSeparator)
+
+ if len(tags) > 0 {
+
+ var param string
+ vals := strings.SplitN(tags[0], tagKeySeparator, 2)
+
+ if len(vals) > 1 {
+ param = vals[1]
+ }
+
+ validationErrors.Errors[cField.name] = &FieldError{
+ Field: cField.name,
+ Tag: vals[0],
+ Param: param,
+ Value: valueField.Interface(),
+ Kind: valueField.Kind(),
+ Type: valueField.Type(),
+ }
+
+ goto CACHEFIELD
+ }
+ }
+
+ // if we get here, the field is interface and could be a struct or a field
+ // and we need to check the inner type and validate
+ if cField.kind == reflect.Interface {
+
+ valueField = valueField.Elem()
+
+ if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
+ valueField = valueField.Elem()
+ }
+
+ if valueField.Kind() == reflect.Struct {
+ goto VALIDATESTRUCT
+ }
+
+ // sending nil for cField as it was type interface and could be anything
+ // each time and so must be calculated each time and can't be cached reliably
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+
+ goto CACHEFIELD
+ }
+
+ VALIDATESTRUCT:
+ if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
+ validationErrors.StructErrors[cField.name] = structErrors
+ // free up memory map no longer needed
+ structErrors = nil
+ }
+ }
+
+ case reflect.Slice, reflect.Array:
+ cField.isSliceOrArray = true
+ cField.sliceSubtype = cField.typ.Elem()
+ cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{}))
+ cField.sliceSubKind = cField.sliceSubtype.Kind()
+
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+
+ case reflect.Map:
+ cField.isMap = true
+ cField.mapSubtype = cField.typ.Elem()
+ cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{}))
+ cField.mapSubKind = cField.mapSubtype.Kind()
+
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+
+ default:
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+ }
+
+ CACHEFIELD:
+ if !isCached {
+ cs.fields = append(cs.fields, cField)
+ }
+ }
+
+ structCache.Set(structType, cs)
+
+ if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
+ structPool.Return(validationErrors)
+ return nil
+ }
+
+ return validationErrors
+}
+
+// Field allows validation of a single field, still using tag style validation to check multiple errors
+func (v *Validate) Field(f interface{}, tag string) *FieldError {
+ return v.FieldWithValue(nil, f, tag)
+}
+
+// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
+func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError {
+ return v.fieldWithNameAndValue(nil, val, f, tag, "", true, nil)
+}
+
+func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, isSingleField bool, cacheField *cachedField) *FieldError {
+
+ var cField *cachedField
+ var isCached bool
+ var valueField reflect.Value
+
+ // This is a double check if coming from validate.Struct but need to be here in case function is called directly
+ if tag == noValidationTag {
+ return nil
+ }
+
+ if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") {
+ return nil
+ }
+
+ valueField = reflect.ValueOf(f)
+
+ if cacheField == nil {
+
+ if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
+ valueField = valueField.Elem()
+ f = valueField.Interface()
+ }
+
+ cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()}
+
+ switch cField.kind {
+ case reflect.Slice, reflect.Array:
+ isSingleField = false // cached tags mean nothing because it will be split up while diving
+ cField.isSliceOrArray = true
+ cField.sliceSubtype = cField.typ.Elem()
+ cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{}))
+ cField.sliceSubKind = cField.sliceSubtype.Kind()
+ case reflect.Map:
+ isSingleField = false // cached tags mean nothing because it will be split up while diving
+ cField.isMap = true
+ cField.mapSubtype = cField.typ.Elem()
+ cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{}))
+ cField.mapSubKind = cField.mapSubtype.Kind()
+ }
+ } else {
+ cField = cacheField
+ }
+
+ switch cField.kind {
+
+ case reflect.Struct, reflect.Interface, reflect.Invalid:
+
+ if cField.typ != reflect.TypeOf(time.Time{}) {
+ panic("Invalid field passed to fieldWithNameAndValue")
+ }
+ }
+
+ if len(cField.tags) == 0 {
+
+ if isSingleField {
+ cField.tags, isCached = fieldsCache.Get(tag)
+ }
+
+ if !isCached {
+
+ for _, t := range strings.Split(tag, tagSeparator) {
+
+ if t == diveTag {
+
+ cField.dive = true
+ cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",")
+ break
+ }
+
+ orVals := strings.Split(t, orSeparator)
+ cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))}
+ cField.tags = append(cField.tags, cTag)
+
+ for i, val := range orVals {
+ vals := strings.SplitN(val, tagKeySeparator, 2)
+
+ key := strings.TrimSpace(vals[0])
+
+ if len(key) == 0 {
+ panic(fmt.Sprintf("Invalid validation tag on field %s", name))
+ }
+
+ param := ""
+ if len(vals) > 1 {
+ param = strings.Replace(vals[1], utf8HexComma, ",", -1)
+ }
+
+ cTag.keyVals[i] = []string{key, param}
+ }
+ }
+
+ if isSingleField {
+ fieldsCache.Set(cField.tag, cField.tags)
+ }
+ }
+ }
+
+ var fieldErr *FieldError
+ var err error
+
+ for _, cTag := range cField.tags {
+
+ if cTag.isOrVal {
+
+ errTag := ""
+
+ for _, val := range cTag.keyVals {
+
+ fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, val[0], val[1], name)
+
+ if err == nil {
+ return nil
+ }
+
+ errTag += orSeparator + fieldErr.Tag
+ }
+
+ errTag = strings.TrimLeft(errTag, orSeparator)
+
+ fieldErr.Tag = errTag
+ fieldErr.Kind = cField.kind
+ fieldErr.Type = cField.typ
+
+ return fieldErr
+ }
+
+ if fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, cTag.keyVals[0][0], cTag.keyVals[0][1], name); err != nil {
+
+ fieldErr.Kind = cField.kind
+ fieldErr.Type = cField.typ
+
+ return fieldErr
+ }
+ }
+
+ if cField.dive {
+
+ if cField.isSliceOrArray {
+
+ if errs := v.traverseSliceOrArray(val, current, valueField, cField); errs != nil && len(errs) > 0 {
+
+ return &FieldError{
+ Field: cField.name,
+ Kind: cField.kind,
+ Type: cField.typ,
+ Value: f,
+ IsPlaceholderErr: true,
+ IsSliceOrArray: true,
+ SliceOrArrayErrs: errs,
+ }
+ }
+
+ } else if cField.isMap {
+ if errs := v.traverseMap(val, current, valueField, cField); errs != nil && len(errs) > 0 {
+
+ return &FieldError{
+ Field: cField.name,
+ Kind: cField.kind,
+ Type: cField.typ,
+ Value: f,
+ IsPlaceholderErr: true,
+ IsMap: true,
+ MapErrs: errs,
+ }
+ }
+ } else {
+ // throw error, if not a slice or map then should not have gotten here
+ panic("dive error! can't dive on a non slice or map")
+ }
+ }
+
+ return nil
+}
+
+func (v *Validate) traverseMap(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[interface{}]error {
+
+ errs := map[interface{}]error{}
+
+ for _, key := range valueField.MapKeys() {
+
+ idxField := valueField.MapIndex(key)
+
+ if cField.mapSubKind == reflect.Ptr && !idxField.IsNil() {
+ idxField = idxField.Elem()
+ cField.mapSubKind = idxField.Kind()
+ }
+
+ switch cField.mapSubKind {
+ case reflect.Struct, reflect.Interface:
+
+ if cField.isTimeSubtype {
+
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
+ errs[key.Interface()] = fieldError
+ }
+
+ continue
+ }
+
+ if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
+
+ if strings.Contains(cField.diveTag, omitempty) {
+ continue
+ }
+
+ tags := strings.Split(cField.diveTag, tagSeparator)
+
+ if len(tags) > 0 {
+
+ var param string
+ vals := strings.SplitN(tags[0], tagKeySeparator, 2)
+
+ if len(vals) > 1 {
+ param = vals[1]
+ }
+
+ errs[key.Interface()] = &FieldError{
+ Field: fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()),
+ Tag: vals[0],
+ Param: param,
+ Value: idxField.Interface(),
+ Kind: idxField.Kind(),
+ Type: cField.mapSubtype,
+ }
+ }
+
+ continue
+ }
+
+ // if we get here, the field is interface and could be a struct or a field
+ // and we need to check the inner type and validate
+ if idxField.Kind() == reflect.Interface {
+
+ idxField = idxField.Elem()
+
+ if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
+ idxField = idxField.Elem()
+ }
+
+ if idxField.Kind() == reflect.Struct {
+ goto VALIDATESTRUCT
+ }
+
+ // sending nil for cField as it was type interface and could be anything
+ // each time and so must be calculated each time and can't be cached reliably
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
+ errs[key.Interface()] = fieldError
+ }
+
+ continue
+ }
+
+ VALIDATESTRUCT:
+ if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
+ errs[key.Interface()] = structErrors
+ }
+
+ default:
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
+ errs[key.Interface()] = fieldError
+ }
+ }
+ }
+
+ return errs
+}
+
+func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[int]error {
+
+ errs := map[int]error{}
+
+ for i := 0; i < valueField.Len(); i++ {
+
+ idxField := valueField.Index(i)
+
+ if cField.sliceSubKind == reflect.Ptr && !idxField.IsNil() {
+ idxField = idxField.Elem()
+ cField.sliceSubKind = idxField.Kind()
+ }
+
+ switch cField.sliceSubKind {
+ case reflect.Struct, reflect.Interface:
+
+ if cField.isTimeSubtype {
+
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
+ errs[i] = fieldError
+ }
+
+ continue
+ }
+
+ if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
+
+ if strings.Contains(cField.diveTag, omitempty) {
+ continue
+ }
+
+ tags := strings.Split(cField.diveTag, tagSeparator)
+
+ if len(tags) > 0 {
+
+ var param string
+ vals := strings.SplitN(tags[0], tagKeySeparator, 2)
+
+ if len(vals) > 1 {
+ param = vals[1]
+ }
+
+ errs[i] = &FieldError{
+ Field: fmt.Sprintf(arrayIndexFieldName, cField.name, i),
+ Tag: vals[0],
+ Param: param,
+ Value: idxField.Interface(),
+ Kind: idxField.Kind(),
+ Type: cField.sliceSubtype,
+ }
+ }
+
+ continue
+ }
+
+ // if we get here, the field is interface and could be a struct or a field
+ // and we need to check the inner type and validate
+ if idxField.Kind() == reflect.Interface {
+
+ idxField = idxField.Elem()
+
+ if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
+ idxField = idxField.Elem()
+ }
+
+ if idxField.Kind() == reflect.Struct {
+ goto VALIDATESTRUCT
+ }
+
+ // sending nil for cField as it was type interface and could be anything
+ // each time and so must be calculated each time and can't be cached reliably
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
+ errs[i] = fieldError
+ }
+
+ continue
+ }
+
+ VALIDATESTRUCT:
+ if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
+ errs[i] = structErrors
+ }
+
+ default:
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
+ errs[i] = fieldError
+ }
+ }
+ }
+
+ return errs
+}
+
+func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{}, f interface{}, key string, param string, name string) (*FieldError, error) {
+
+ // OK to continue because we checked it's existance before getting into this loop
+ if key == omitempty {
+ return nil, nil
+ }
+
+ valFunc, ok := v.validationFuncs[key]
+ if !ok {
+ panic(fmt.Sprintf("Undefined validation function on field %s", name))
+ }
+
+ if err := valFunc(val, current, f, param); err {
+ return nil, nil
+ }
+
+ return &FieldError{
+ Field: name,
+ Tag: key,
+ Value: f,
+ Param: param,
+ }, errors.New(key)
+}