diff --git a/baked_in.go b/baked_in.go index 2ab23e4..6c42ee5 100644 --- a/baked_in.go +++ b/baked_in.go @@ -5,7 +5,6 @@ import ( "net" "net/url" "reflect" - "strconv" "strings" "time" "unicode/utf8" @@ -27,6 +26,7 @@ var BakedInValidators = map[string]Func{ "gte": isGte, "eqfield": isEqField, "eqcsfield": isEqCrossStructField, + "necsfield": isNeCrossStructField, "nefield": isNeField, "gtefield": isGteField, "gtfield": isGtField, @@ -254,6 +254,11 @@ func isNe(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, fie return !isEq(v, topStruct, currentStruct, field, fieldType, fieldKind, param) } +func isNeCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + return !isEqCrossStructField(v, topStruct, current, field, fieldType, fieldKind, param) +} + func isEqCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { topField, topKind, ok := v.getStructFieldOK(topStruct, param) @@ -842,39 +847,3 @@ func hasMaxOf(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, return isLte(v, topStruct, currentStruct, field, fieldType, fieldKind, param) } - -// asInt retuns the parameter as a int64 -// or panics if it can't convert -func asInt(param string) int64 { - - i, err := strconv.ParseInt(param, 0, 64) - panicIf(err) - - return i -} - -// asUint returns the parameter as a uint64 -// or panics if it can't convert -func asUint(param string) uint64 { - - i, err := strconv.ParseUint(param, 0, 64) - panicIf(err) - - return i -} - -// asFloat returns the parameter as a float64 -// or panics if it can't convert -func asFloat(param string) float64 { - - i, err := strconv.ParseFloat(param, 64) - panicIf(err) - - return i -} - -func panicIf(err error) { - if err != nil { - panic(err.Error()) - } -} diff --git a/util.go b/util.go index 0d3f254..72c9f22 100644 --- a/util.go +++ b/util.go @@ -207,3 +207,39 @@ func (v *Validate) getStructFieldOK(current reflect.Value, namespace string) (re panic("Invalid field namespace") // return current, kind, false } + +// asInt retuns the parameter as a int64 +// or panics if it can't convert +func asInt(param string) int64 { + + i, err := strconv.ParseInt(param, 0, 64) + panicIf(err) + + return i +} + +// asUint returns the parameter as a uint64 +// or panics if it can't convert +func asUint(param string) uint64 { + + i, err := strconv.ParseUint(param, 0, 64) + panicIf(err) + + return i +} + +// asFloat returns the parameter as a float64 +// or panics if it can't convert +func asFloat(param string) float64 { + + i, err := strconv.ParseFloat(param, 64) + panicIf(err) + + return i +} + +func panicIf(err error) { + if err != nil { + panic(err.Error()) + } +} diff --git a/validator_test.go b/validator_test.go index a0dc26a..1132e45 100644 --- a/validator_test.go +++ b/validator_test.go @@ -192,6 +192,112 @@ func ValidateValuerType(field reflect.Value) interface{} { return nil } +func TestCrossStructNeFieldValidation(t *testing.T) { + + type Inner struct { + CreatedAt *time.Time + } + + type Test struct { + Inner *Inner + CreatedAt *time.Time `validate:"necsfield=Inner.CreatedAt"` + } + + now := time.Now().UTC() + then := now.Add(time.Hour * 5) + + inner := &Inner{ + CreatedAt: &then, + } + + test := &Test{ + Inner: inner, + CreatedAt: &now, + } + + errs := validate.Struct(test) + Equal(t, errs, nil) + + test.CreatedAt = &then + + errs = validate.Struct(test) + NotEqual(t, errs, nil) + AssertError(t, errs, "Test.CreatedAt", "CreatedAt", "necsfield") + + // var j uint64 + // var k float64 + s := "abcd" + i := 1 + j := 1 + k := 1.543 + arr := []string{"test"} + + // var j2 uint64 + // var k2 float64 + s2 := "abcd" + i2 := 1 + j2 := 1 + k2 := 1.543 + arr2 := []string{"test"} + arr3 := []string{"test", "test2"} + now2 := now + + errs = validate.FieldWithValue(s, s2, "necsfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "necsfield") + + errs = validate.FieldWithValue(i2, i, "necsfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "necsfield") + + errs = validate.FieldWithValue(j2, j, "necsfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "necsfield") + + errs = validate.FieldWithValue(k2, k, "necsfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "necsfield") + + errs = validate.FieldWithValue(arr2, arr, "necsfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "necsfield") + + errs = validate.FieldWithValue(now2, now, "necsfield") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "necsfield") + + errs = validate.FieldWithValue(arr3, arr, "necsfield") + Equal(t, errs, nil) + + type SInner struct { + Name string + } + + type TStruct struct { + Inner *SInner + CreatedAt *time.Time `validate:"necsfield=Inner"` + } + + sinner := &SInner{ + Name: "NAME", + } + + test2 := &TStruct{ + Inner: sinner, + CreatedAt: &now, + } + + errs = validate.Struct(test2) + Equal(t, errs, nil) + + test2.Inner = nil + errs = validate.Struct(test2) + Equal(t, errs, nil) + + errs = validate.FieldWithValue(nil, 1, "necsfield") + Equal(t, errs, nil) +} + func TestCrossStructEqFieldValidation(t *testing.T) { type Inner struct {