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
8 changes: 8 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ Here is a list of the current built in validators:
inside of you program you know the struct will be valid, but need to
verify it has been assigned.

exists
Is a special tag without a validation function attached. It is used when a field
is a Pointer, Interface or Invalid and you wish to validate that it exists.
Example: want to ensure a bool exists if you define the bool as a pointer and
use exists it will ensure there is a value; couldn't use required as it would
fail when the bool was false. exists will fail is the value is a Pointer, Interface
or Invalid and is nil. (Usage: exists)

omitempty
Allows conditional validation, for example if a field is not set with
a value (Determined by the required validator) then other validation
Expand Down
32 changes: 32 additions & 0 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s"
structErrMsg = "Struct:%s\n"
diveTag = "dive"
existsTag = "exists"
arrayIndexFieldName = "%s[%d]"
mapIndexFieldName = "%s[%v]"
)
Expand Down Expand Up @@ -722,7 +723,22 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f

for _, val := range cTag.keyVals {

// if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
// if val[0] == existsTag {
// if (cField.kind == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {
// fieldErr = &FieldError{
// Field: name,
// Tag: val[0],
// Value: f,
// Param: val[1],
// }
// err = errors.New(fieldErr.Tag)
// }

// } else {

fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, val[0], val[1], name)
// }

if err == nil {
return nil
Expand All @@ -740,6 +756,18 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
return fieldErr
}

if cTag.keyVals[0][0] == existsTag {
if (cField.kind == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {
return &FieldError{
Field: name,
Tag: cTag.keyVals[0][0],
Value: f,
Param: cTag.keyVals[0][1],
}
}
continue
}

if fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, cTag.keyVals[0][0], cTag.keyVals[0][1], name); err != nil {

fieldErr.Kind = cField.kind
Expand Down Expand Up @@ -981,6 +1009,10 @@ func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{
return nil, nil
}

// if key == existsTag {
// continue
// }

valFunc, ok := v.validationFuncs[key]
if !ok {
panic(fmt.Sprintf("Undefined validation function on field %s", name))
Expand Down
31 changes: 31 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validator

import (
"encoding/json"
"fmt"
"path"
"reflect"
Expand Down Expand Up @@ -231,6 +232,36 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e
EqualSkip(t, 2, val.Tag, expectedTag)
}

func TestExistsValidation(t *testing.T) {

jsonText := "{ \"truthiness2\": true }"

type Thing struct {
Truthiness *bool `json:"truthiness" validate:"exists,required"`
}

var ting Thing

err := json.Unmarshal([]byte(jsonText), &ting)
Equal(t, err, nil)
NotEqual(t, ting, nil)
Equal(t, ting.Truthiness, nil)

errs := validate.Struct(ting)
NotEqual(t, errs, nil)
AssertFieldError(t, errs, "Truthiness", "exists")

jsonText = "{ \"truthiness\": true }"

err = json.Unmarshal([]byte(jsonText), &ting)
Equal(t, err, nil)
NotEqual(t, ting, nil)
Equal(t, ting.Truthiness, true)

errs = validate.Struct(ting)
Equal(t, errs, nil)
}

func TestSliceMapArrayChanFuncPtrInterfaceRequiredValidation(t *testing.T) {

var m map[string]string
Expand Down