diff --git a/baked_in.go b/baked_in.go index 0e394bd..82868cc 100644 --- a/baked_in.go +++ b/baked_in.go @@ -424,10 +424,12 @@ func hasValue(top interface{}, current interface{}, field interface{}, param str st := reflect.ValueOf(field) switch st.Kind() { - - case reflect.Slice, reflect.Map, reflect.Array: - return field != nil && int64(st.Len()) > 0 - + case reflect.Invalid: + return false + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return !st.IsNil() + case reflect.Array: + return field != reflect.Zero(reflect.TypeOf(field)).Interface() default: return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface() } diff --git a/doc.go b/doc.go index f45ef34..ba6f934 100644 --- a/doc.go +++ b/doc.go @@ -193,9 +193,10 @@ Here is a list of the current built in validators: within its SliceOrArrayErrs or MapErrs fields. 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 ccd2a55..e2c07d0 100644 --- a/validator.go +++ b/validator.go @@ -627,7 +627,11 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f f = valueField.Interface() } - cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()} + cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag} + + if cField.kind != reflect.Invalid { + cField.typ = valueField.Type() + } switch cField.kind { case reflect.Slice, reflect.Array: @@ -648,8 +652,14 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f } switch cField.kind { + case reflect.Invalid: + return &FieldError{ + Field: cField.name, + Tag: cField.tag, + Kind: cField.kind, + } - case reflect.Struct, reflect.Interface, reflect.Invalid: + case reflect.Struct, reflect.Interface: if cField.typ != reflect.TypeOf(time.Time{}) { panic("Invalid field passed to fieldWithNameAndValue") diff --git a/validator_test.go b/validator_test.go index 8bfff9b..6fb08bb 100644 --- a/validator_test.go +++ b/validator_test.go @@ -231,6 +231,82 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e 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) + + errs = validate.Field(iface, "len=1") + NotEqual(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 TestBadKeyValidation(t *testing.T) { type Test struct { Name string `validate:"required, "` @@ -3735,14 +3811,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, } err := validate.Struct(tSuccess) Equal(t, err, 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}, @@ -3810,7 +3886,7 @@ func TestPoolObjectMaxSizeValidation(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, } for i := 0; i < 2; i++ {