From e078205c787cfb7e8b62e6711f9665f1585588d0 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Mon, 27 Jul 2015 17:20:42 -0400 Subject: [PATCH] Update Required & Invalid logic updated required validator to check for a nil value for types: slice, map, pointer, interface, channel and function. updated tranverseField to handle invalid field type. Changes to be committed: modified: baked_in.go modified: doc.go modified: validator.go modified: validator_test.go --- baked_in.go | 6 ++-- doc.go | 5 +-- validator.go | 22 +++++++++++--- validator_test.go | 77 +++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 98 insertions(+), 12 deletions(-) diff --git a/baked_in.go b/baked_in.go index bf9e512..9bbf399 100644 --- a/baked_in.go +++ b/baked_in.go @@ -403,10 +403,8 @@ func isAlpha(topStruct reflect.Value, currentStruct reflect.Value, field reflect func hasValue(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { - - case reflect.Slice, reflect.Map, reflect.Array: - return !field.IsNil() && int64(field.Len()) > 0 - + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return !field.IsNil() default: return field.IsValid() && field.Interface() != reflect.Zero(fieldType).Interface() } diff --git a/doc.go b/doc.go index 6733893..82804b8 100644 --- a/doc.go +++ b/doc.go @@ -123,9 +123,10 @@ Here is a list of the current built in validators: required will be applied to string required - This validates that the value is not the data types default value. + This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is - not "". For slices, arrays, and maps, ensures the length is not zero. + not "". For slices, maps, pointers, interfaces, channels and functions + ensures the value is not nil. (Usage: required) len diff --git a/validator.go b/validator.go index 781bea3..19f86a7 100644 --- a/validator.go +++ b/validator.go @@ -243,12 +243,10 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. kind = current.Kind() } - typ := current.Type() - // this also allows for tags 'required' and 'omitempty' to be used on // nested struct fields because when len(tags) > 0 below and the value is nil // then required failes and we check for omitempty just before that - if (kind == reflect.Ptr || kind == reflect.Interface) && current.IsNil() { + if ((kind == reflect.Ptr || kind == reflect.Interface) && current.IsNil()) || kind == reflect.Invalid { if strings.Contains(tag, omitempty) { return @@ -264,19 +262,35 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. param = vals[1] } + if kind == reflect.Invalid { + errs[errPrefix+name] = &FieldError{ + Field: name, + Tag: vals[0], + Param: param, + Kind: kind, + } + return + } + errs[errPrefix+name] = &FieldError{ Field: name, Tag: vals[0], Param: param, Value: current.Interface(), Kind: kind, - Type: typ, + Type: current.Type(), } return } + // if we get here tag length is zero and we can leave + if kind == reflect.Invalid { + return + } } + typ := current.Type() + switch kind { case reflect.Struct, reflect.Interface: diff --git a/validator_test.go b/validator_test.go index 79b5b66..6048606 100644 --- a/validator_test.go +++ b/validator_test.go @@ -119,6 +119,79 @@ func AssertError(t *testing.T, errs ValidationErrors, key, field, expectedTag st EqualSkip(t, 2, val.Tag, expectedTag) } +func TestSliceMapArrayChanFuncPtrInterfaceRequiredValidation(t *testing.T) { + + var m map[string]string + + errs := validate.Field(m, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + m = map[string]string{} + errs = validate.Field(m, "required") + Equal(t, errs, nil) + + var arr [5]string + errs = validate.Field(arr, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + arr[0] = "ok" + errs = validate.Field(arr, "required") + Equal(t, errs, nil) + + var s []string + errs = validate.Field(s, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + s = []string{} + errs = validate.Field(s, "required") + Equal(t, errs, nil) + + var c chan string + errs = validate.Field(c, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + c = make(chan string) + errs = validate.Field(c, "required") + Equal(t, errs, nil) + + var tst *int + errs = validate.Field(tst, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + one := 1 + tst = &one + errs = validate.Field(tst, "required") + Equal(t, errs, nil) + + var iface interface{} + + errs = validate.Field(iface, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + errs = validate.Field(iface, "omitempty,required") + Equal(t, errs, nil) + + errs = validate.Field(iface, "") + Equal(t, errs, nil) + + var f func(string) + + errs = validate.Field(f, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + f = func(name string) {} + + errs = validate.Field(f, "required") + Equal(t, errs, nil) +} + func TestDatePtrValidationIssueValidation(t *testing.T) { type Test struct { @@ -3262,14 +3335,14 @@ func TestStructSliceValidation(t *testing.T) { Min: []int{1, 2}, Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, MinMax: []int{1, 2, 3, 4, 5}, - OmitEmpty: []int{}, + OmitEmpty: nil, } errs := validate.Struct(tSuccess) Equal(t, errs, nil) tFail := &TestSlice{ - Required: []int{}, + Required: nil, Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, Min: []int{}, Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1},