diff --git a/util.go b/util.go index 59e4505..6ca21ce 100644 --- a/util.go +++ b/util.go @@ -232,18 +232,18 @@ func panicIf(err error) { } } -func (v *Validate) parseTags(tag, fieldName string) []*tagCache { +func (v *Validate) parseTags(tag, fieldName string) *cachedTag { - tags, _ := v.parseTagsRecursive(tag, fieldName, blank, false) - return tags -} + cTag := &cachedTag{} -func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool) ([]*tagCache, bool) { + v.parseTagsRecursive(cTag, tag, fieldName, blank, false) + return cTag +} - tags := []*tagCache{} +func (v *Validate) parseTagsRecursive(cTag *cachedTag, tag, fieldName, alias string, isAlias bool) bool { if len(tag) == 0 { - return tags, true + return true } for _, t := range strings.Split(tag, tagSeparator) { @@ -252,11 +252,10 @@ func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool // check map for alias and process new tags, otherwise process as usual if tagsVal, ok := v.config.aliasValidators[t]; ok { - aliasTags, leave := v.parseTagsRecursive(tagsVal, fieldName, t, true) - tags = append(tags, aliasTags...) + leave := v.parseTagsRecursive(cTag, tagsVal, fieldName, t, true) if leave { - return tags, leave + return leave } continue @@ -264,16 +263,20 @@ func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool } if t == diveTag { - tVals := &tagCache{diveTag: tag, tagVals: [][]string{{t}}} - tags = append(tags, tVals) - return tags, true + cTag.diveTag = tag + tVals := &tagVals{tagVals: [][]string{{t}}} + cTag.tags = append(cTag.tags, tVals) + return true + } + + if t == omitempty { + cTag.isOmitEmpty = true } // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" orVals := strings.Split(t, orSeparator) - cTag := &tagCache{isAlias: isAlias, isOrVal: len(orVals) > 1, tagVals: make([][]string, len(orVals))} - - tags = append(tags, cTag) + 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 @@ -282,10 +285,10 @@ func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool vals := strings.SplitN(val, tagKeySeparator, 2) key = vals[0] - cTag.tag = key + tagVal.tag = key if isAlias { - cTag.tag = alias + tagVal.tag = alias } if len(key) == 0 { @@ -296,9 +299,9 @@ func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) } - cTag.tagVals[i] = []string{key, param} + tagVal.tagVals[i] = []string{key, param} } } - return tags, false + return false } diff --git a/validator.go b/validator.go index 7382aa9..52e74ac 100644 --- a/validator.go +++ b/validator.go @@ -41,7 +41,7 @@ var ( timeType = reflect.TypeOf(time.Time{}) timePtrType = reflect.TypeOf(&time.Time{}) errsPool = &sync.Pool{New: newValidationErrors} - tagsCache = &tagCacheMap{m: map[string][]*tagCache{}} + tagsCache = &tagCacheMap{m: map[string]*cachedTag{}} emptyStructPtr = new(struct{}) ) @@ -50,28 +50,32 @@ func newValidationErrors() interface{} { return ValidationErrors{} } -type tagCache struct { +type cachedTag struct { + isOmitEmpty bool + diveTag string + tags []*tagVals +} + +type tagVals struct { tagVals [][]string isOrVal bool isAlias bool tag string - diveTag string - // actualTag string } type tagCacheMap struct { lock sync.RWMutex - m map[string][]*tagCache + m map[string]*cachedTag } -func (s *tagCacheMap) Get(key string) ([]*tagCache, bool) { +func (s *tagCacheMap) Get(key string) (*cachedTag, bool) { s.lock.RLock() defer s.lock.RUnlock() value, ok := s.m[key] return value, ok } -func (s *tagCacheMap) Set(key string, value []*tagCache) { +func (s *tagCacheMap) Set(key string, value *cachedTag) { s.lock.Lock() defer s.lock.Unlock() s.m[key] = value @@ -399,11 +403,11 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. return } - tags, isCached := tagsCache.Get(tag) + cTag, isCached := tagsCache.Get(tag) if !isCached { - tags = v.parseTags(tag, name) - tagsCache.Set(tag, tags) + cTag = v.parseTags(tag, name) + tagsCache.Set(tag, cTag) } current, kind := v.extractType(current) @@ -411,7 +415,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. switch kind { case reflect.Ptr, reflect.Interface, reflect.Invalid: - if strings.Contains(tag, omitempty) { + if cTag.isOmitEmpty { return } @@ -420,9 +424,9 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. if kind == reflect.Invalid { errs[errPrefix+name] = &FieldError{ Field: name, - Tag: tags[0].tag, - ActualTag: tags[0].tagVals[0][0], - Param: tags[0].tagVals[0][1], + Tag: cTag.tags[0].tag, + ActualTag: cTag.tags[0].tagVals[0][0], + Param: cTag.tags[0].tagVals[0][1], Kind: kind, } return @@ -430,9 +434,9 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. errs[errPrefix+name] = &FieldError{ Field: name, - Tag: tags[0].tag, - ActualTag: tags[0].tagVals[0][0], - Param: tags[0].tagVals[0][1], + Tag: cTag.tags[0].tag, + ActualTag: cTag.tags[0].tagVals[0][0], + Param: cTag.tags[0].tagVals[0][1], Value: current.Interface(), Kind: kind, Type: current.Type(), @@ -471,20 +475,19 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. var dive bool var diveSubTag string - for _, cTag := range tags { + for _, valTag := range cTag.tags { - if cTag.tagVals[0][0] == existsTag { + if valTag.tagVals[0][0] == existsTag { continue } - if cTag.tagVals[0][0] == diveTag { + if valTag.tagVals[0][0] == diveTag { dive = true - // fmt.Println(cTag.diveTag) diveSubTag = strings.TrimLeft(strings.SplitN(cTag.diveTag, diveTag, 2)[1], ",") break } - if cTag.tagVals[0][0] == omitempty { + if valTag.tagVals[0][0] == omitempty { if !hasValue(v, topStruct, currentStruct, current, typ, kind, blank) { return @@ -492,7 +495,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. continue } - if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, cTag, name) { + if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, valTag, name) { return } } @@ -530,16 +533,16 @@ func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Va } // 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 { +func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, valTag *tagVals, name string) bool { var valFunc Func var ok bool - if cTag.isOrVal { + if valTag.isOrVal { errTag := blank - for _, val := range cTag.tagVals { + for _, val := range valTag.tagVals { valFunc, ok = v.config.ValidationFuncs[val[0]] if !ok { @@ -553,10 +556,10 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect. errTag += orSeparator + val[0] } - if cTag.isAlias { + if valTag.isAlias { errs[errPrefix+name] = &FieldError{ Field: name, - Tag: cTag.tag, + Tag: valTag.tag, ActualTag: errTag[1:], Value: current.Interface(), Type: currentType, @@ -576,21 +579,21 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect. return true } - valFunc, ok = v.config.ValidationFuncs[cTag.tagVals[0][0]] + valFunc, ok = v.config.ValidationFuncs[valTag.tagVals[0][0]] if !ok { panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name))) } - if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, cTag.tagVals[0][1]) { + if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, valTag.tagVals[0][1]) { return false } errs[errPrefix+name] = &FieldError{ Field: name, - Tag: cTag.tag, - ActualTag: cTag.tagVals[0][0], + Tag: valTag.tag, + ActualTag: valTag.tagVals[0][0], Value: current.Interface(), - Param: cTag.tagVals[0][1], + Param: valTag.tagVals[0][1], Type: currentType, Kind: currentKind, }