diff --git a/cache.go b/cache.go index 7b8612c..f4a2b48 100644 --- a/cache.go +++ b/cache.go @@ -17,7 +17,6 @@ const ( typeStructOnly typeDive typeOr - typeExists ) const ( @@ -25,10 +24,6 @@ const ( undefinedValidation = "Undefined validation function '%s' on field '%s'" ) -// var ( -// validatable = reflect.TypeOf((*Validatable)(nil)).Elem() -// ) - type structCache struct { lock sync.Mutex m atomic.Value // map[reflect.Type]*cStruct @@ -114,26 +109,6 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr cs = &cStruct{Name: sName, fields: make(map[int]*cField), fn: v.structLevelFuncs[typ]} - // newVal := reflect.New(typ) - - // // fmt.Println("implements:", reflect.PtrTo(typ).Implements(validatable)) - // tv1, tv2 := newVal.Interface().(Validatable) - // // tv1, tv2 := reflect.PtrTo(typ).Elem().(Validatable) - // fmt.Println("implements2:", tv1, tv2) - // // fmt.Println("Testing if Validatable", tv1, tv2, reflect.PtrTo(typ), current, current.Type(), typ) - - // if vable, ok := reflect.New(typ).Interface().(Validatable); ok { - - // // fmt.Println("Validatable", cs.fn) - - // if cs.fn != nil { - // log.Println("warning: struct level validation overriding 'Validatabe' interface function") - // } else { - // cs.fn = vable.Validate - // // fmt.Println(cs.fn) - // } - // } - numFields := current.NumField() var ctag *cTag diff --git a/struct_level.go b/struct_level.go index e4d9366..921a544 100644 --- a/struct_level.go +++ b/struct_level.go @@ -159,10 +159,3 @@ func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNames v.errs = append(v.errs, err) } } - -// Validatable is the interface a struct can implement and -// be validated just like registering a StructLevel validation -// (they actually have the exact same signature.) -type Validatable interface { - Validate(sl StructLevel) -} diff --git a/validator.go b/validator.go index 4f82f69..7cfcd4d 100644 --- a/validator.go +++ b/validator.go @@ -180,10 +180,6 @@ OUTER: switch ct.typeof { - case typeExists: - ct = ct.next - continue - case typeOmitEmpty: // set Field Level fields diff --git a/validator_instance.go b/validator_instance.go index 54a9f77..0e68290 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -200,10 +200,8 @@ func (v *Validate) Struct(s interface{}) (err error) { val = val.Elem() } - typ := val.Type() - - if val.Kind() != reflect.Struct || typ == timeType { - return &InvalidValidationError{Type: typ} + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} } // good to validate @@ -212,7 +210,7 @@ func (v *Validate) Struct(s interface{}) (err error) { vd.isPartial = false // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept - vd.validateStruct(top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) + vd.validateStruct(top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) if len(vd.errs) > 0 { err = vd.errs @@ -239,10 +237,8 @@ func (v *Validate) StructPartial(s interface{}, fields ...string) (err error) { val = val.Elem() } - typ := val.Type() - - if val.Kind() != reflect.Struct || typ == timeType { - return &InvalidValidationError{Type: typ} + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} } // good to validate @@ -252,6 +248,7 @@ func (v *Validate) StructPartial(s interface{}, fields ...string) (err error) { vd.hasExcludes = false vd.includeExclude = make(map[string]struct{}) + typ := val.Type() name := typ.Name() if fields != nil { @@ -316,10 +313,8 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) (err error) { val = val.Elem() } - typ := val.Type() - - if val.Kind() != reflect.Struct || typ == timeType { - return &InvalidValidationError{Type: typ} + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} } // good to validate @@ -329,6 +324,7 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) (err error) { vd.hasExcludes = true vd.includeExclude = make(map[string]struct{}) + typ := val.Type() name := typ.Name() for _, key := range fields { @@ -347,8 +343,6 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) (err error) { return } -// func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns []byte, actualNs []byte, cf *cField, ct *cTag) { - // Var validates a single variable using tag style validation. // eg. // var i int diff --git a/validator_test.go b/validator_test.go index 29808b1..7a8ce21 100644 --- a/validator_test.go +++ b/validator_test.go @@ -304,85 +304,43 @@ type TestStructReturnValidationErrors struct { Inner1 *TestStructReturnValidationErrorsInner1 `json:"Inner1JSON"` } -// type TestValidatablePtr struct { -// Value string -// } - -// func (tv *TestValidatablePtr) Validate(sl StructLevel) { - -// fmt.Printf("HERE *********** %p %#v\n", tv, tv) -// if len(tv.Value) == 0 { -// sl.ReportError(reflect.ValueOf(tv.Value), "Value", "Value", "required") -// } -// } - -// type TestValidatableNoPtr struct { -// Value string -// } - -// func (tv TestValidatableNoPtr) Validate(sl StructLevel) { - -// fmt.Println("HERE2 ***********") -// if len(tv.Value) == 0 { -// sl.ReportError(reflect.ValueOf(tv.Value), "Value", "Value", "required") -// } -// } - -// var _ Validatable = new(TestValidatablePtr) - -// func TestStructLevelValidatableInterfaceValidations(t *testing.T) { - -// // validate := New() - -// // tv := &TestValidatablePtr{Value: "tst3"} - -// // fmt.Println("VALIDATING***************") -// // err := validate.Struct(tv) -// // NotEqual(t, err, nil) - -// // var tv2 TestValidatableNoPtr - -// // fmt.Println("VALIDATING***************") -// // err = validate.Struct(tv2) -// // NotEqual(t, err, nil) - -// // tv3 := &TestValidatablePtr{Value: "test"} - -// // fmt.Println("VALIDATING***************") -// // err = validate.Struct(tv3) -// // Equal(t, err, nil) +type StructLevelInvalidErr struct { + Value string +} -// // v1 := New() -// // v1.RegisterStructValidation(StructValidationTestStruct, TestStruct{}) +func StructLevelInvalidError(sl StructLevel) { -// // tst := &TestStruct{ -// // String: "good value", -// // } + top := sl.Top().Interface().(StructLevelInvalidErr) + s := sl.Current().Interface().(StructLevelInvalidErr) -// // errs := v1.Struct(tst) -// // NotEqual(t, errs, nil) -// // AssertError(t, errs, "TestStruct.StringVal", "TestStruct.String", "StringVal", "String", "badvalueteststruct") + if top.Value == s.Value { + sl.ReportError(nil, "Value", "Value", "required") + } +} -// // v2 := New() -// // v2.RegisterStructValidation(StructValidationNoTestStructCustomName, TestStruct{}) +func TestStructLevelInvalidError(t *testing.T) { -// // errs = v2.Struct(tst) -// // NotEqual(t, errs, nil) -// // AssertError(t, errs, "TestStruct.String", "TestStruct.String", "String", "String", "badvalueteststruct") + validate := New() + validate.RegisterStructValidation(StructLevelInvalidError, StructLevelInvalidErr{}) -// // v3 := New() -// // v3.RegisterStructValidation(StructValidationTestStructInvalid, TestStruct{}) + var test StructLevelInvalidErr -// // errs = v3.Struct(tst) -// // NotEqual(t, errs, nil) -// // AssertError(t, errs, "TestStruct.StringVal", "TestStruct.String", "StringVal", "String", "badvalueteststruct") + err := validate.Struct(test) + NotEqual(t, err, nil) -// // v4 := New() -// // v4.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{}) + errs, ok := err.(ValidationErrors) + Equal(t, ok, true) -// // errs = v4.Struct(tst) -// // Equal(t, errs, nil) -// } + fe := errs[0] + Equal(t, fe.Field(), "Value") + Equal(t, fe.StructField(), "Value") + Equal(t, fe.Namespace(), "StructLevelInvalidErr.Value") + Equal(t, fe.StructNamespace(), "StructLevelInvalidErr.Value") + Equal(t, fe.Tag(), "required") + Equal(t, fe.ActualTag(), "required") + Equal(t, fe.Kind(), reflect.Invalid) + Equal(t, fe.Type(), reflect.TypeOf(nil)) +} func TestNameNamespace(t *testing.T) { @@ -1041,6 +999,24 @@ func TestCrossStructLteFieldValidation(t *testing.T) { AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "ltecsfield") AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "ltecsfield") AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "ltecsfield") + + type Other struct { + Value string + } + + type Test2 struct { + Value Other + Time time.Time `validate:"ltecsfield=Value"` + } + + tst := Test2{ + Value: Other{Value: "StringVal"}, + Time: then, + } + + errs = validate.Struct(tst) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "ltecsfield") } func TestCrossStructLtFieldValidation(t *testing.T) { @@ -1119,6 +1095,24 @@ func TestCrossStructLtFieldValidation(t *testing.T) { AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "ltcsfield") AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "ltcsfield") AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "ltcsfield") + + type Other struct { + Value string + } + + type Test2 struct { + Value Other + Time time.Time `validate:"ltcsfield=Value"` + } + + tst := Test2{ + Value: Other{Value: "StringVal"}, + Time: then, + } + + errs = validate.Struct(tst) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "ltcsfield") } func TestCrossStructGteFieldValidation(t *testing.T) { @@ -1209,6 +1203,24 @@ func TestCrossStructGteFieldValidation(t *testing.T) { AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "gtecsfield") AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "gtecsfield") AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "gtecsfield") + + type Other struct { + Value string + } + + type Test2 struct { + Value Other + Time time.Time `validate:"gtecsfield=Value"` + } + + tst := Test2{ + Value: Other{Value: "StringVal"}, + Time: then, + } + + errs = validate.Struct(tst) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "gtecsfield") } func TestCrossStructGtFieldValidation(t *testing.T) { @@ -1287,6 +1299,24 @@ func TestCrossStructGtFieldValidation(t *testing.T) { AssertError(t, errs, "Test.Uint", "Test.Uint", "Uint", "Uint", "gtcsfield") AssertError(t, errs, "Test.Float", "Test.Float", "Float", "Float", "gtcsfield") AssertError(t, errs, "Test.Array", "Test.Array", "Array", "Array", "gtcsfield") + + type Other struct { + Value string + } + + type Test2 struct { + Value Other + Time time.Time `validate:"gtcsfield=Value"` + } + + tst := Test2{ + Value: Other{Value: "StringVal"}, + Time: then, + } + + errs = validate.Struct(tst) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "gtcsfield") } func TestCrossStructNeFieldValidation(t *testing.T) { @@ -4028,6 +4058,23 @@ func TestIsNeFieldValidation(t *testing.T) { errs = validate.Struct(sv2) Equal(t, errs, nil) + + type Other struct { + Value string + } + + type Test3 struct { + Value Other + Time time.Time `validate:"nefield=Value"` + } + + tst := Test3{ + Value: Other{Value: "StringVal"}, + Time: now, + } + + errs = validate.Struct(tst) + Equal(t, errs, nil) } func TestIsNeValidation(t *testing.T) { @@ -4465,6 +4512,24 @@ func TestGtField(t *testing.T) { errs = validate.Struct(timeTest2) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest2.End", "TimeTest2.End", "End", "End", "gtfield") + + type Other struct { + Value string + } + + type Test struct { + Value Other + Time time.Time `validate:"gtfield=Value"` + } + + tst := Test{ + Value: Other{Value: "StringVal"}, + Time: end, + } + + errs = validate.Struct(tst) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test.Time", "Test.Time", "Time", "Time", "gtfield") } func TestLtField(t *testing.T) { @@ -6093,6 +6158,22 @@ func TestStructSliceValidation(t *testing.T) { AssertError(t, errs, "TestSlice.Max", "TestSlice.Max", "Max", "Max", "max") AssertError(t, errs, "TestSlice.MinMax", "TestSlice.MinMax", "MinMax", "MinMax", "min") AssertError(t, errs, "TestSlice.OmitEmpty", "TestSlice.OmitEmpty", "OmitEmpty", "OmitEmpty", "max") + + fe := getError(errs, "TestSlice.Len", "TestSlice.Len") + NotEqual(t, fe, nil) + Equal(t, fe.Field(), "Len") + Equal(t, fe.StructField(), "Len") + Equal(t, fe.Namespace(), "TestSlice.Len") + Equal(t, fe.StructNamespace(), "TestSlice.Len") + Equal(t, fe.Tag(), "len") + Equal(t, fe.ActualTag(), "len") + Equal(t, fe.Param(), "10") + Equal(t, fe.Kind(), reflect.Slice) + Equal(t, fe.Type(), reflect.TypeOf([]int{})) + + _, ok := fe.Value().([]int) + Equal(t, ok, true) + } func TestInvalidStruct(t *testing.T) { @@ -6106,6 +6187,18 @@ func TestInvalidStruct(t *testing.T) { err := validate.Struct(s.Test) NotEqual(t, err, nil) Equal(t, err.Error(), "validator: (nil string)") + + err = validate.Struct(nil) + NotEqual(t, err, nil) + Equal(t, err.Error(), "validator: (nil)") + + err = validate.StructPartial(nil, "SubTest.Test") + NotEqual(t, err, nil) + Equal(t, err.Error(), "validator: (nil)") + + err = validate.StructExcept(nil, "SubTest.Test") + NotEqual(t, err, nil) + Equal(t, err.Error(), "validator: (nil)") } func TestInvalidValidatorFunction(t *testing.T) {