Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 124 additions & 16 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,6 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
structName = structType.Name()
numFields = structValue.NumField()
cs = &cachedStruct{name: structName, children: numFields}
structCache.Set(structType, cs)
}

validationErrors := structPool.Borrow()
Expand Down Expand Up @@ -429,24 +428,62 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
continue
}

if valueField.Kind() == reflect.Ptr && valueField.IsNil() {
if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {

if strings.Contains(cField.tag, omitempty) {
continue
goto CACHEFIELD
}

if strings.Contains(cField.tag, required) {
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: required,
Tag: vals[0],
Param: param,
Value: valueField.Interface(),
Kind: valueField.Kind(),
Type: valueField.Type(),
}

continue
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
Expand Down Expand Up @@ -486,11 +523,14 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
}
}

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
Expand Down Expand Up @@ -709,26 +749,60 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField
continue
}

if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {

if strings.Contains(cField.tag, omitempty) {
if strings.Contains(cField.diveTag, omitempty) {
continue
}

if strings.Contains(cField.tag, required) {
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: required,
Tag: vals[0],
Param: param,
Value: idxField.Interface(),
Kind: reflect.Ptr,
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
}
Expand Down Expand Up @@ -768,26 +842,60 @@ func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, va
continue
}

if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {

if strings.Contains(cField.tag, omitempty) {
if strings.Contains(cField.diveTag, omitempty) {
continue
}

if strings.Contains(cField.tag, required) {
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: required,
Tag: vals[0],
Param: param,
Value: idxField.Interface(),
Kind: reflect.Ptr,
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
}
Expand Down
Loading