diff --git a/doc.go b/doc.go index ba6f934..74db756 100644 --- a/doc.go +++ b/doc.go @@ -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 diff --git a/validator.go b/validator.go index e2c07d0..f195647 100644 --- a/validator.go +++ b/validator.go @@ -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]" ) @@ -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 @@ -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 @@ -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)) diff --git a/validator_test.go b/validator_test.go index 6fb08bb..fb6fa9d 100644 --- a/validator_test.go +++ b/validator_test.go @@ -1,6 +1,7 @@ package validator import ( + "encoding/json" "fmt" "path" "reflect" @@ -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