code cleanup + add missing tag cache with FieldWith func

Still looking good:

```

benchmark                                               old ns/op     new ns/op     delta
BenchmarkFieldSuccess-4                                 167           119           -28.74%
BenchmarkFieldFailure-4                                 701           650           -7.28%
BenchmarkFieldDiveSuccess-4                             2937          2482          -15.49%
BenchmarkFieldDiveFailure-4                             3536          3046          -13.86%
BenchmarkFieldCustomTypeSuccess-4                       341           296           -13.20%
BenchmarkFieldCustomTypeFailure-4                       679           666           -1.91%
BenchmarkFieldOrTagSuccess-4                            1157          1124          -2.85%
BenchmarkFieldOrTagFailure-4                            1109          1036          -6.58%
BenchmarkStructLevelValidationSuccess-4                 694           554           -20.17%
BenchmarkStructLevelValidationFailure-4                 1311          558           -57.44%
BenchmarkStructSimpleCustomTypeSuccess-4                894           654           -26.85%
BenchmarkStructSimpleCustomTypeFailure-4                1496          1286          -14.04%
BenchmarkStructPartialSuccess-4                         1229          1062          -13.59%
BenchmarkStructPartialFailure-4                         1838          1639          -10.83%
BenchmarkStructExceptSuccess-4                          961           919           -4.37%
BenchmarkStructExceptFailure-4                          1218          1072          -11.99%
BenchmarkStructSimpleCrossFieldSuccess-4                954           800           -16.14%
BenchmarkStructSimpleCrossFieldFailure-4                1569          1372          -12.56%
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4     1588          1274          -19.77%
BenchmarkStructSimpleCrossStructCrossFieldFailure-4     2217          1901          -14.25%
BenchmarkStructSimpleSuccess-4                          925           512           -44.65%
BenchmarkStructSimpleFailure-4                          1650          1318          -20.12%
BenchmarkStructSimpleSuccessParallel-4                  261           144           -44.83%
BenchmarkStructSimpleFailureParallel-4                  758           581           -23.35%
BenchmarkStructComplexSuccess-4                         5868          3451          -41.19%
BenchmarkStructComplexFailure-4                         10767         8351          -22.44%
BenchmarkStructComplexSuccessParallel-4                 1559          1065          -31.69%
BenchmarkStructComplexFailureParallel-4                 3747          2916          -22.18%

```
pull/252/head
joeybloggs 8 years ago
parent c0a414dc9e
commit 210684a828
  1. 256
      cache.go
  2. 141
      util.go
  3. 353
      validator.go

@ -8,73 +8,6 @@ import (
"sync/atomic" "sync/atomic"
) )
// type cachedField struct {
// Idx int
// Name string
// AltName string
// CachedTag *cachedTag
// }
// type cachedStruct struct {
// Name string
// fields map[int]cachedField
// }
// type structCacheMap struct {
// lock sync.RWMutex
// m map[reflect.Type]*cachedStruct
// }
// func (s *structCacheMap) Get(key reflect.Type) (*cachedStruct, bool) {
// s.lock.RLock()
// value, ok := s.m[key]
// s.lock.RUnlock()
// return value, ok
// }
// func (s *structCacheMap) Set(key reflect.Type, value *cachedStruct) {
// s.lock.Lock()
// s.m[key] = value
// s.lock.Unlock()
// }
// type cachedTag struct {
// tag string
// isOmitEmpty bool
// isNoStructLevel bool
// isStructOnly bool
// diveTag string
// tags []*tagVals
// }
// type tagVals struct {
// tagVals [][]string
// isOrVal bool
// isAlias bool
// tag string
// }
// type tagCacheMap struct {
// lock sync.RWMutex
// m map[string]*cachedTag
// }
// func (s *tagCacheMap) Get(key string) (*cachedTag, bool) {
// s.lock.RLock()
// value, ok := s.m[key]
// s.lock.RUnlock()
// return value, ok
// }
// func (s *tagCacheMap) Set(key string, value *cachedTag) {
// s.lock.Lock()
// s.m[key] = value
// s.lock.Unlock()
// }
// ******* New Cache ***************
type structCache struct { type structCache struct {
lock sync.Mutex lock sync.Mutex
m atomic.Value // map[reflect.Type]*cStruct m atomic.Value // map[reflect.Type]*cStruct
@ -156,7 +89,7 @@ type cTag struct {
func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct {
v.structCache.lock.Lock() v.structCache.lock.Lock()
defer v.structCache.lock.Unlock() defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise!
typ := current.Type() typ := current.Type()
@ -164,7 +97,6 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
// isn't parsed again. // isn't parsed again.
cs, ok := v.structCache.Get(typ) cs, ok := v.structCache.Get(typ)
if ok { if ok {
// v.structCache.lock.Unlock()
return cs return cs
} }
@ -181,14 +113,12 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
fld = typ.Field(i) fld = typ.Field(i)
// if fld.PkgPath != blank {
if !fld.Anonymous && fld.PkgPath != blank { if !fld.Anonymous && fld.PkgPath != blank {
continue continue
} }
tag = fld.Tag.Get(v.tagName) tag = fld.Tag.Get(v.tagName)
// if len(tag) == 0 || tag == skipValidationTag {
if tag == skipValidationTag { if tag == skipValidationTag {
continue continue
} }
@ -205,24 +135,8 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
} }
} }
// fmt.Println("Finding Struct Tag", sName, "FLD:", fld.Name) // NOTE: cannot use shared tag cache, because tags may be equal, but things like alias may be different
// ctag, ok := v.tagCache.Get(tag) // and so only struct level caching can be used instead of combined with Field tag caching
// if !ok {
// fmt.Println("Not Found, Lock then check again for parallel operations")
// v.tagCache.lock.Lock()
// defer func() {
// fmt.Println("Ulocking")
// v.tagCache.lock.Unlock()
// fmt.Println("Unlocked")
// }()
// defer v.tagCache.lock.Unlock()
// if ctag, ok = v.tagCache.Get(tag); !ok {
// fmt.Println("parsing tag", tag)
// ctag = v.parseFieldTags(tag, fld.Name)
// NOTE: cannot use tag cache, because tag may be equal, but things like alias may be different
// and so only struct level caching can be used
if len(tag) > 0 { if len(tag) > 0 {
ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, blank, false) ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, blank, false)
@ -231,141 +145,48 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
// elements of the field. // elements of the field.
ctag = new(cTag) ctag = new(cTag)
} }
// fmt.Println("Done Parsing")
// v.tagCache.Set(tag, ctag)
// fmt.Println("Tag Cahed")
// }
// fmt.Println("Ulocking")
// v.tagCache.lock.Unlock()
// fmt.Println("Unlocked")
// }
// fmt.Println(tag, ctag)
cs.fields[i] = &cField{Idx: i, Name: fld.Name, AltName: customName, cTags: ctag} cs.fields[i] = &cField{Idx: i, Name: fld.Name, AltName: customName, cTags: ctag}
} }
// If not anonymous type; they have to be parsed every time because if interface
// a different struct could be used...
// if len(sName) > 0 {
// fmt.Println(typ)
v.structCache.Set(typ, cs) v.structCache.Set(typ, cs)
// }
// v.structCache.lock.Unlock()
return cs return cs
} }
// func (v *Validate) parseFieldTags(tag, fieldName string) (ctag *cTag) {
// // ctag := &cTag{tag: tag}
// // fmt.Println(tag)
// ctag, _ = v.parseFieldTagsRecursive(tag, fieldName, blank, false)
// v.tagCache.Set(tag, ctag)
// // fmt.Println(ctag)
// return
// }
// TODO: Optimize for to not Split but ust for over string chunk, by chunk // TODO: Optimize for to not Split but ust for over string chunk, by chunk
func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) { func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) {
// func (v *Validate) parseFieldTagsRecursive(ctag *cTag, tag, fieldName, alias string, isAlias bool) bool {
// if tag == blank {
// return
// }
// fmt.Println("Parsing, depath:", depth)
var t string var t string
var ok bool var ok bool
noAlias := len(alias) == 0 noAlias := len(alias) == 0
// var tmpCtag *cTag
// var start,end int
// var ctag *cTag
// var lastCtag *cTag
// ctag := &cTag{tag: tag}
tags := strings.Split(tag, tagSeparator) tags := strings.Split(tag, tagSeparator)
// fmt.Println(len(tags), tags)
for i := 0; i < len(tags); i++ { for i := 0; i < len(tags); i++ {
// for i := 0; i < len(tags); i++ {
t = tags[i] t = tags[i]
if noAlias { if noAlias {
alias = t alias = t
} }
// _, found := v.aliasValidators[t]
// fmt.Println(i, t, found)
// if len(t) == 0 {
// continue
// }
// if i == 0 {
// current = &cTag{aliasTag: alias, hasAlias: hasAlias}
// firstCtag = current
// } else {
// current.next = &cTag{aliasTag: alias, hasAlias: hasAlias}
// current = current.next
// }
if v.hasAliasValidators { if v.hasAliasValidators {
// check map for alias and process new tags, otherwise process as usual // check map for alias and process new tags, otherwise process as usual
if tagsVal, found := v.aliasValidators[t]; found { if tagsVal, found := v.aliasValidators[t]; found {
// fmt.Println(tagsVal)
if i == 0 { if i == 0 {
firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
} else { } else {
// fmt.Println("BEFORE ALIAS:", current)
// diveCurr := current
next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
// fmt.Println("ALIAS:", next, curr)
current.next, current = next, curr current.next, current = next, curr
// fmt.Println("AFTER current", diveCurr)
// current.next, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
} }
continue continue
// // tmpCtag, lastCtag = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
// // if ctag == nil {
// // ctag = tmpCtag
// // } else {
// // ctag.next = tmpCtag
// // }
} }
} }
// if i == 0 {
// firstCtag = current
// }
// type cTag struct {
// tag string
// aliasTag string
// hasAlias bool
// isOmitEmpty bool
// isNoStructLevel bool
// isStructOnly bool
// isDive bool
// isOrVal bool
// fn Func
// next *cTag
// }
// if lastCtag == nil {
// lastCtag = ctag
// }
if i == 0 { if i == 0 {
current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true}
firstCtag = current firstCtag = current
@ -378,12 +199,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
case diveTag: case diveTag:
current.isDive = true current.isDive = true
// fmt.Println("DIVE CURRENT", current)
continue continue
// cTag.diveTag = tag
// tVals := &tagVals{tagVals: [][]string{{t}}}
// cTag.tags = append(cTag.tags, tVals)
// return true
case omitempty: case omitempty:
current.isOmitEmpty = true current.isOmitEmpty = true
@ -406,45 +222,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
// if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
orVals := strings.Split(t, orSeparator) orVals := strings.Split(t, orSeparator)
// // if no or values
// if len(orVals) == 1 {
// current.fn, ok = v.validationFuncs[t]
// if !ok {
// panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, fieldName)))
// }
// } else {
// tagVal := &tagVals{isAlias: isAlias, isOrVal: len(orVals) > 1, tagVals: make([][]string, len(orVals))}
// cTag.tags = append(cTag.tags, tagVal)
// var key string
// var param string
for j := 0; j < len(orVals); j++ { for j := 0; j < len(orVals); j++ {
// for i, val := range orVals {
// if v.hasAliasValidators {
// // check map for alias and process new tags, otherwise process as usual
// if tagsVal, ok := v.aliasValidators[orVals[j]]; ok {
// if i == 0 {
// firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, orVals[j], true)
// } else {
// current.next, current = v.parseFieldTagsRecursive(tagsVal, fieldName, orVals[j], true)
// }
// continue
// // tmpCtag, lastCtag = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
// // if ctag == nil {
// // ctag = tmpCtag
// // } else {
// // ctag.next = tmpCtag
// // }
// }
// }
vals := strings.SplitN(orVals[j], tagKeySeparator, 2) vals := strings.SplitN(orVals[j], tagKeySeparator, 2)
@ -452,57 +230,31 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
alias = vals[0] alias = vals[0]
current.aliasTag = alias current.aliasTag = alias
} else { } else {
// alias = t
current.actualAliasTag = t current.actualAliasTag = t
} }
if j > 0 { if j > 0 {
current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true} current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true}
current = current.next current = current.next
// current.next=&
} }
// else{
current.tag = vals[0] current.tag = vals[0]
if len(current.tag) == 0 { if len(current.tag) == 0 {
panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName))) panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
} }
// _, found := v.validationFuncs[current.tag]
// fmt.Println("TAG", current.tag, "FOund:", found)
if current.fn, ok = v.validationFuncs[current.tag]; !ok { if current.fn, ok = v.validationFuncs[current.tag]; !ok {
// fmt.Println("I'm panicing!")
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, fieldName))) panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, fieldName)))
} }
current.isOrVal = len(orVals) > 1 current.isOrVal = len(orVals) > 1
// }
// tagVal.tag = key
// if isAlias {
// tagVal.tag = alias
// }
// if key == blank {
// panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
// }
if len(vals) > 1 { if len(vals) > 1 {
current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
} }
// tagVal.tagVals[i] = []string{key, param}
} }
// }
} }
} }
// if depth > 0 {
// // _, found := v.aliasValidators[t]
// fmt.Println("WTF", len(tags), tags, firstCtag, current)
// panic("WTF")
// }
return return
} }

@ -7,7 +7,6 @@ import (
) )
const ( const (
// dash = "-"
blank = "" blank = ""
namespaceSeparator = "." namespaceSeparator = "."
leftBracket = "[" leftBracket = "["
@ -59,15 +58,10 @@ func (v *Validate) ExtractType(current reflect.Value) (reflect.Value, reflect.Ki
default: default:
if v.hasCustomFuncs { if v.hasCustomFuncs {
// fmt.Println("Type", current.Type())
if fn, ok := v.customTypeFuncs[current.Type()]; ok {
// fmt.Println("OK")
if fn, ok := v.customTypeFuncs[current.Type()]; ok {
return v.ExtractType(reflect.ValueOf(fn(current))) return v.ExtractType(reflect.ValueOf(fn(current)))
} }
// fmt.Println("NOT OK")
} }
return current, current.Kind() return current, current.Kind()
@ -245,136 +239,3 @@ func panicIf(err error) {
panic(err.Error()) panic(err.Error())
} }
} }
// func (v *Validate) parseStruct(current reflect.Value, sName string) *cachedStruct {
// typ := current.Type()
// s := &cachedStruct{Name: sName, fields: map[int]cachedField{}}
// numFields := current.NumField()
// var fld reflect.StructField
// var tag string
// var customName string
// for i := 0; i < numFields; i++ {
// fld = typ.Field(i)
// if fld.PkgPath != blank {
// continue
// }
// tag = fld.Tag.Get(v.tagName)
// if tag == skipValidationTag {
// continue
// }
// customName = fld.Name
// if v.fieldNameTag != blank {
// name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]
// // dash check is for json "-" (aka skipValidationTag) means don't output in json
// if name != "" && name != skipValidationTag {
// customName = name
// }
// }
// cTag, ok := v.oldTagCache.Get(tag)
// if !ok {
// cTag = v.parseTags(tag, fld.Name)
// }
// s.fields[i] = cachedField{Idx: i, Name: fld.Name, AltName: customName, CachedTag: cTag}
// }
// v.oldStructCache.Set(typ, s)
// return s
// }
// func (v *Validate) parseTags(tag, fieldName string) *cachedTag {
// cTag := &cachedTag{tag: tag}
// v.parseTagsRecursive(cTag, tag, fieldName, blank, false)
// v.oldTagCache.Set(tag, cTag)
// return cTag
// }
// func (v *Validate) parseTagsRecursive(cTag *cachedTag, tag, fieldName, alias string, isAlias bool) bool {
// if tag == blank {
// return true
// }
// for _, t := range strings.Split(tag, tagSeparator) {
// if v.hasAliasValidators {
// // check map for alias and process new tags, otherwise process as usual
// if tagsVal, ok := v.aliasValidators[t]; ok {
// leave := v.parseTagsRecursive(cTag, tagsVal, fieldName, t, true)
// if leave {
// return leave
// }
// continue
// }
// }
// switch t {
// case diveTag:
// cTag.diveTag = tag
// tVals := &tagVals{tagVals: [][]string{{t}}}
// cTag.tags = append(cTag.tags, tVals)
// return true
// case omitempty:
// cTag.isOmitEmpty = true
// case structOnlyTag:
// cTag.isStructOnly = true
// case noStructLevelTag:
// cTag.isNoStructLevel = true
// }
// // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
// orVals := strings.Split(t, orSeparator)
// tagVal := &tagVals{isAlias: isAlias, isOrVal: len(orVals) > 1, tagVals: make([][]string, len(orVals))}
// cTag.tags = append(cTag.tags, tagVal)
// var key string
// var param string
// for i, val := range orVals {
// vals := strings.SplitN(val, tagKeySeparator, 2)
// key = vals[0]
// tagVal.tag = key
// if isAlias {
// tagVal.tag = alias
// }
// if key == blank {
// panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
// }
// if len(vals) > 1 {
// param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
// }
// tagVal.tagVals[i] = []string{key, param}
// }
// }
// return false
// }

@ -148,8 +148,6 @@ type Validate struct {
hasCustomFuncs bool hasCustomFuncs bool
hasAliasValidators bool hasAliasValidators bool
hasStructLevelFuncs bool hasStructLevelFuncs bool
// oldTagCache *tagCacheMap
// oldStructCache *structCacheMap
tagCache *tagCache tagCache *tagCache
structCache *structCache structCache *structCache
errsPool *sync.Pool errsPool *sync.Pool
@ -234,8 +232,6 @@ func New(config *Config) *Validate {
fieldNameTag: config.FieldNameTag, fieldNameTag: config.FieldNameTag,
tagCache: tc, tagCache: tc,
structCache: sc, structCache: sc,
// oldTagCache: &tagCacheMap{m: map[string]*cachedTag{}},
// oldStructCache: &structCacheMap{m: map[reflect.Type]*cachedStruct{}},
errsPool: &sync.Pool{New: func() interface{} { errsPool: &sync.Pool{New: func() interface{} {
return ValidationErrors{} return ValidationErrors{}
}}} }}}
@ -364,8 +360,6 @@ func (v *Validate) Field(field interface{}, tag string) error {
} }
} }
// fmt.Println("CTAG:", ctag)
v.traverseField(fieldVal, fieldVal, fieldVal, blank, blank, errs, false, false, nil, nil, defaultCField, ctag) v.traverseField(fieldVal, fieldVal, fieldVal, blank, blank, errs, false, false, nil, nil, defaultCField, ctag)
if len(errs) == 0 { if len(errs) == 0 {
@ -400,6 +394,7 @@ func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string
ctag, ok = v.tagCache.Get(tag) ctag, ok = v.tagCache.Get(tag)
if !ok { if !ok {
ctag, _ = v.parseFieldTagsRecursive(tag, blank, blank, false) ctag, _ = v.parseFieldTagsRecursive(tag, blank, blank, false)
v.tagCache.Set(tag, ctag)
} }
} }
@ -533,20 +528,15 @@ func (v *Validate) ensureValidStruct(topStruct reflect.Value, currentStruct refl
// tranverseStruct traverses a structs fields and then passes them to be validated by traverseField // 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, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]*struct{}, cs *cStruct, ct *cTag) { func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]*struct{}, cs *cStruct, ct *cTag) {
// var ok bool var ok bool
first := len(nsPrefix) == 0 first := len(nsPrefix) == 0
typ := current.Type() typ := current.Type()
var ok bool
// var sName string
cs, ok = v.structCache.Get(typ) cs, ok = v.structCache.Get(typ)
if !ok { if !ok {
cs = v.extractStructCache(current, typ.Name()) cs = v.extractStructCache(current, typ.Name())
} }
// sName := typ.Name()
if useStructName { if useStructName {
errPrefix += cs.Name + namespaceSeparator errPrefix += cs.Name + namespaceSeparator
@ -560,55 +550,6 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec
// if present // if present
if first || ct == nil || !ct.isStructOnly { if first || ct == nil || !ct.isStructOnly {
// var fld reflect.StructField
// is anonymous struct, cannot parse or cache as
// it has no name to index by
// if sName == blank {
// var customName string
// var ok bool
// numFields := current.NumField()
// for i := 0; i < numFields; i++ {
// fld = typ.Field(i)
// if !fld.Anonymous && fld.PkgPath != blank {
// continue
// }
// if partial {
// _, ok = includeExclude[errPrefix+fld.Name]
// if (ok && exclude) || (!ok && !exclude) {
// continue
// }
// }
// customName = fld.Name
// if v.fieldNameTag != blank {
// name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]
// // dash check is for json "-" means don't output in json
// if name != blank && name != dash {
// customName = name
// }
// }
// v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, nsPrefix, errs, true, fld.Tag.Get(v.tagName), fld.Name, customName, partial, exclude, includeExclude, nil)
// }
// } else {
// s, ok := v.oldStructCache.Get(typ)
// if !ok {
// s = v.parseStruct(current, sName)
// ccs := v.extractStructCache(current, sName)
// fmt.Println(ccs, ccs.fields[0], s.fields[0].CachedTag)
// }
for _, f := range cs.fields { for _, f := range cs.fields {
if partial { if partial {
@ -619,48 +560,26 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec
continue continue
} }
} }
// fld = typ.Field(f.Idx)
// fmt.Println(errPrefix+f.Name, f.cTags)
v.traverseField(topStruct, currentStruct, current.Field(f.Idx), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, f, f.cTags) v.traverseField(topStruct, currentStruct, current.Field(f.Idx), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, f, f.cTags)
} }
// }
} }
// check if any struct level validations, after all field validations already checked. // check if any struct level validations, after all field validations already checked.
if cs.fn != nil { if cs.fn != nil {
// if fsn, ok := v.structLevelFuncs[current.Type()]; ok {
cs.fn(v, &StructLevel{v: v, TopStruct: topStruct, CurrentStruct: current, errPrefix: errPrefix, nsPrefix: nsPrefix, errs: errs}) cs.fn(v, &StructLevel{v: v, TopStruct: topStruct, CurrentStruct: current, errPrefix: errPrefix, nsPrefix: nsPrefix, errs: errs})
// }
} }
} }
// 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 // 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, nsPrefix string, errs ValidationErrors, partial bool, exclude bool, includeExclude map[string]*struct{}, cs *cStruct, cf *cField, ct *cTag) { func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, partial bool, exclude bool, includeExclude map[string]*struct{}, cs *cStruct, cf *cField, ct *cTag) {
// if tag == skipValidationTag {
// return
// }
// if cTag == nil {
// var isCached bool
// cTag, isCached = v.oldTagCache.Get(tag)
// if !isCached {
// cTag = v.parseTags(tag, name)
// }
// }
current, kind := v.ExtractType(current) current, kind := v.ExtractType(current)
var typ reflect.Type var typ reflect.Type
// fmt.Println("TS:", ct, kind)
switch kind { switch kind {
case reflect.Ptr, reflect.Interface, reflect.Invalid: case reflect.Ptr, reflect.Interface, reflect.Invalid:
// return
if ct == nil { if ct == nil {
return return
} }
@ -669,7 +588,6 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
return return
} }
// if !ct.hasTag {
if ct.hasTag { if ct.hasTag {
ns := errPrefix + cf.Name ns := errPrefix + cf.Name
@ -704,8 +622,6 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
return return
} }
// return
// if we get here tag length is zero and we can leave // if we get here tag length is zero and we can leave
if kind == reflect.Invalid { if kind == reflect.Invalid {
return return
@ -718,15 +634,12 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
if ct != nil { if ct != nil {
ct = ct.next ct = ct.next
// fmt.Println("CHECKING NO STRUCT LEVEL", ct.isNoStructLevel, ct.next)
} }
// if ct != nil && (ct.isNoStructLevel || (ct.next != nil && ct.next.isNoStructLevel)) {
if ct != nil && ct.isNoStructLevel { if ct != nil && ct.isNoStructLevel {
return return
} }
// func (v *Validate) tranverseStruct(topStruct reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]*struct{}, cs *cStruct, ct *cTag)
v.tranverseStruct(topStruct, current, current, errPrefix+cf.Name+namespaceSeparator, nsPrefix+cf.AltName+namespaceSeparator, errs, false, partial, exclude, includeExclude, cs, ct) v.tranverseStruct(topStruct, current, current, errPrefix+cf.Name+namespaceSeparator, nsPrefix+cf.AltName+namespaceSeparator, errs, false, partial, exclude, includeExclude, cs, ct)
return return
} }
@ -761,12 +674,8 @@ OUTER:
if ct.isDive { if ct.isDive {
// fmt.Println("IN DIVE:", ct)
ct = ct.next ct = ct.next
// fmt.Println("NEXT:", ct)
// traverse slice or map here // traverse slice or map here
// or panic ;) // or panic ;)
switch kind { switch kind {
@ -775,14 +684,12 @@ OUTER:
for i := 0; i < current.Len(); i++ { for i := 0; i < current.Len(); i++ {
v.traverseField(topStruct, currentStruct, current.Index(i), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, &cField{Name: fmt.Sprintf(arrayIndexFieldName, cf.Name, i), AltName: fmt.Sprintf(arrayIndexFieldName, cf.AltName, i)}, ct) v.traverseField(topStruct, currentStruct, current.Index(i), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, &cField{Name: fmt.Sprintf(arrayIndexFieldName, cf.Name, i), AltName: fmt.Sprintf(arrayIndexFieldName, cf.AltName, i)}, ct)
} }
// v.traverseSlice(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
case reflect.Map: case reflect.Map:
for _, key := range current.MapKeys() { for _, key := range current.MapKeys() {
v.traverseField(topStruct, currentStruct, current.MapIndex(key), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, &cField{Name: fmt.Sprintf(mapIndexFieldName, cf.Name, key.Interface()), AltName: fmt.Sprintf(mapIndexFieldName, cf.AltName, key.Interface())}, ct) v.traverseField(topStruct, currentStruct, current.MapIndex(key), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, &cField{Name: fmt.Sprintf(mapIndexFieldName, cf.Name, key.Interface()), AltName: fmt.Sprintf(mapIndexFieldName, cf.AltName, key.Interface())}, ct)
// v.traverseField(topStruct, currentStruct, current.MapIndex(key), errPrefix, nsPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface()), fmt.Sprintf(mapIndexFieldName, customName, key.Interface()), partial, exclude, includeExclude, cTag)
} }
// v.traverseMap(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
default: default:
// throw error, if not a slice or map then should not have gotten here // throw error, if not a slice or map then should not have gotten here
// bad dive tag // bad dive tag
@ -796,44 +703,8 @@ OUTER:
errTag := blank errTag := blank
// for _, val := range valTag.tagVals {
for { for {
// fmt.Println(ct)
// if ct == nil {
// // if we get here, no valid 'or' value and no more tags
// ns := errPrefix + cf.Name
// if ct.hasAlias {
// errs[ns] = &FieldError{
// FieldNamespace: ns,
// NameNamespace: nsPrefix + cf.AltName,
// Name: cf.AltName,
// Field: cf.Name,
// Tag: ct.aliasTag,
// ActualTag: ct.tag,
// Value: current.Interface(),
// Type: typ,
// Kind: kind,
// }
// } else {
// errs[errPrefix+cf.Name] = &FieldError{
// FieldNamespace: ns,
// NameNamespace: nsPrefix + cf.AltName,
// Name: cf.AltName,
// Field: cf.Name,
// Tag: errTag[1:],
// ActualTag: errTag[1:],
// Value: current.Interface(),
// Type: typ,
// Kind: kind,
// }
// }
// return
// }
if !ct.isOrVal { if !ct.isOrVal {
// if we get here, no valid 'or' value, but more tags // if we get here, no valid 'or' value, but more tags
@ -866,15 +737,8 @@ OUTER:
} }
continue OUTER continue OUTER
// break
// goto VAL
} }
// valFunc, ok = v.validationFuncs[val[0]]
// if !ok {
// panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
// }
if ct.fn(v, topStruct, currentStruct, current, typ, kind, ct.param) { if ct.fn(v, topStruct, currentStruct, current, typ, kind, ct.param) {
// drain rest of the 'or' values, then continue or leave // drain rest of the 'or' values, then continue or leave
@ -888,8 +752,6 @@ OUTER:
if !ct.isOrVal { if !ct.isOrVal {
continue OUTER continue OUTER
// break
// goto VAL
} }
} }
} }
@ -932,45 +794,7 @@ OUTER:
ct = ct.next ct = ct.next
} }
// if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, val[1]) { }
// return false
// }
// errTag += orSeparator + val[0]
// }
// ns := errPrefix + name
// if valTag.isAlias {
// errs[ns] = &FieldError{
// FieldNamespace: ns,
// NameNamespace: nsPrefix + customName,
// Name: customName,
// Field: name,
// Tag: valTag.tag,
// ActualTag: errTag[1:],
// Value: current.Interface(),
// Type: currentType,
// Kind: currentKind,
// }
// } else {
// errs[errPrefix+name] = &FieldError{
// FieldNamespace: ns,
// NameNamespace: nsPrefix + customName,
// Name: customName,
// Field: name,
// Tag: errTag[1:],
// ActualTag: errTag[1:],
// Value: current.Interface(),
// Type: currentType,
// Kind: currentKind,
// }
// NOT SURE ABOUT THE BELOW LINE BEING COMMENT, LINT SAYS IT's Unreachable but....
// return
}
// fmt.Println(ct)
if !ct.fn(v, topStruct, currentStruct, current, typ, kind, ct.param) { if !ct.fn(v, topStruct, currentStruct, current, typ, kind, ct.param) {
@ -992,176 +816,7 @@ OUTER:
return return
} }
// VAL:
// valFunc, ok = v.validationFuncs[valTag.tagVals[0][0]]
// if !ok {
// panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
// }
// if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, valTag.tagVals[0][1]) {
// return false
// }
// ns := errPrefix + name
// errs[ns] = &FieldError{
// FieldNamespace: ns,
// NameNamespace: nsPrefix + customName,
// Name: customName,
// Field: name,
// Tag: valTag.tag,
// ActualTag: valTag.tagVals[0][0],
// Value: current.Interface(),
// Param: valTag.tagVals[0][1],
// Type: currentType,
// Kind: currentKind,
// }
// if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, nsPrefix, errs, valTag, name, customName) {
// return
// }
ct = ct.next ct = ct.next
} }
// var dive bool
// var diveSubTag string
// for _, valTag := range cTag.tags {
// // if valTag.tagVals[0][0] == existsTag {
// // continue
// // }
// // if valTag.tagVals[0][0] == diveTag {
// // dive = true
// // diveSubTag = strings.TrimLeft(strings.SplitN(cTag.diveTag, diveTag, 2)[1], ",")
// // break
// // }
// // if valTag.tagVals[0][0] == omitempty {
// // if !HasValue(v, topStruct, currentStruct, current, typ, kind, blank) {
// // return
// // }
// // continue
// // }
// if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, nsPrefix, errs, valTag, name, customName) {
// return
// }
// }
// if dive {
// // traverse slice or map here
// // or panic ;)
// switch kind {
// case reflect.Slice, reflect.Array:
// v.traverseSlice(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
// case reflect.Map:
// v.traverseMap(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
// default:
// // throw error, if not a slice or map then should not have gotten here
// // bad dive tag
// panic("dive error! can't dive on a non slice or map")
// }
// }
} }
// // 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, nsPrefix string, errs ValidationErrors, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
// for i := 0; i < current.Len(); i++ {
// v.traverseField(topStruct, currentStruct, current.Index(i), errPrefix, nsPrefix, errs, false, tag, fmt.Sprintf(arrayIndexFieldName, name, i), fmt.Sprintf(arrayIndexFieldName, customName, i), partial, exclude, includeExclude, cTag)
// }
// }
// // 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, nsPrefix string, errs ValidationErrors, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
// for _, key := range current.MapKeys() {
// v.traverseField(topStruct, currentStruct, current.MapIndex(key), errPrefix, nsPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface()), fmt.Sprintf(mapIndexFieldName, customName, key.Interface()), partial, exclude, includeExclude, cTag)
// }
// }
// // 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, nsPrefix string, errs ValidationErrors, valTag *tagVals, name, customName string) bool {
// var valFunc Func
// var ok bool
// if valTag.isOrVal {
// errTag := blank
// for _, val := range valTag.tagVals {
// valFunc, ok = v.validationFuncs[val[0]]
// if !ok {
// panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
// }
// if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, val[1]) {
// return false
// }
// errTag += orSeparator + val[0]
// }
// ns := errPrefix + name
// if valTag.isAlias {
// errs[ns] = &FieldError{
// FieldNamespace: ns,
// NameNamespace: nsPrefix + customName,
// Name: customName,
// Field: name,
// Tag: valTag.tag,
// ActualTag: errTag[1:],
// Value: current.Interface(),
// Type: currentType,
// Kind: currentKind,
// }
// } else {
// errs[errPrefix+name] = &FieldError{
// FieldNamespace: ns,
// NameNamespace: nsPrefix + customName,
// Name: customName,
// Field: name,
// Tag: errTag[1:],
// ActualTag: errTag[1:],
// Value: current.Interface(),
// Type: currentType,
// Kind: currentKind,
// }
// }
// return true
// }
// valFunc, ok = v.validationFuncs[valTag.tagVals[0][0]]
// if !ok {
// panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
// }
// if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, valTag.tagVals[0][1]) {
// return false
// }
// ns := errPrefix + name
// errs[ns] = &FieldError{
// FieldNamespace: ns,
// NameNamespace: nsPrefix + customName,
// Name: customName,
// Field: name,
// Tag: valTag.tag,
// ActualTag: valTag.tagVals[0][0],
// Value: current.Interface(),
// Param: valTag.tagVals[0][1],
// Type: currentType,
// Kind: currentKind,
// }
// return true
// }

Loading…
Cancel
Save