From b891b1cf3cd763bbd7e8e7137d8e8e8fb04d2792 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Fri, 8 May 2015 11:59:48 -0400 Subject: [PATCH] add eq baked in function + tests add eqfield baked in function + tests for issue #35 --- baked_in.go | 115 +++++++++++++++++++++++++++++++++++++++++++++ doc.go | 12 +++++ validator.go | 2 +- validator_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 1 deletion(-) diff --git a/baked_in.go b/baked_in.go index b733fd0..767f280 100644 --- a/baked_in.go +++ b/baked_in.go @@ -16,10 +16,12 @@ var BakedInValidators = map[string]Func{ "len": hasLengthOf, "min": hasMinOf, "max": hasMaxOf, + "eq": isEq, "lt": isLt, "lte": isLte, "gt": isGt, "gte": isGte, + "eqfield": isEqField, "gtefield": isGteField, "gtfield": isGtField, "ltefield": isLteField, @@ -40,6 +42,119 @@ var BakedInValidators = map[string]Func{ "base64": isBase64, } +func isEqField(top interface{}, current interface{}, field interface{}, param string) bool { + + if current == nil { + panic("struct not passed for cross validation") + } + + currentVal := reflect.ValueOf(current) + + if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { + currentVal = reflect.ValueOf(currentVal.Elem().Interface()) + } + + var currentFielVal reflect.Value + + switch currentVal.Kind() { + + case reflect.Struct: + + if currentVal.Type() == reflect.TypeOf(time.Time{}) { + currentFielVal = currentVal + break + } + + f := currentVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + currentFielVal = f + + default: + + currentFielVal = currentVal + } + + if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + + currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.String: + return fv.String() == currentFielVal.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() == currentFielVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() == currentFielVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() == currentFielVal.Float() + case reflect.Slice, reflect.Map, reflect.Array: + + return int64(fv.Len()) == int64(currentFielVal.Len()) + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := currentFielVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.Equal(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isEq(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + + return st.String() == param + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(st.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return st.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return st.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return st.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + func isBase64(top interface{}, current interface{}, field interface{}, param string) bool { return matchesRegex(base64Regex, field) } diff --git a/doc.go b/doc.go index 62b3eb9..d4ddf1b 100644 --- a/doc.go +++ b/doc.go @@ -192,6 +192,11 @@ Here is a list of the current built in validators: the string length is at least that number of characters. For slices, arrays, and maps, validates the number of items. (Usage: min=10) + eq + For strings & numbers, eq will ensure that the value is + equal to the parameter given. For slices, arrays, and maps, + validates the number of items. (Usage: eq=10) + gt For numbers, this will ensure that the value is greater than the parameter given. For strings, it checks that the string length @@ -221,6 +226,13 @@ Here is a list of the current built in validators: For time.Time ensures the time value is less than or equal to time.Now.UTC() (Usage: lte) + eqfield + This will validate the field value against another fields value either within + a struct or passed in field. + usage examples are for validation of a password and confirm password: + Validation on Password field using validate.Struct Usage(eqfield=ConfirmPassword) + Validating by field validate.FieldWithValue(password, confirmpassword, "eqfield") + gtfield Only valid for Numbers and time.Time types, this will validate the field value against another fields value either within a struct or passed in field. diff --git a/validator.go b/validator.go index 6350797..e787ffa 100644 --- a/validator.go +++ b/validator.go @@ -200,7 +200,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter } // if no validation and not a struct (which may containt fields for validation) - if tag == "" && valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface { + if tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) { continue } diff --git a/validator_test.go b/validator_test.go index 55c5cb6..efdf469 100644 --- a/validator_test.go +++ b/validator_test.go @@ -142,6 +142,122 @@ func isEqualFunc(val interface{}, current interface{}, field interface{}, param return current.(string) == field.(string) } +func (ms *MySuite) TestIsEqFieldValidation(c *C) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + var j2 uint64 + var k2 float64 + s2 := "abcd" + i2 := 1 + j2 = 1 + k2 = 1.543 + arr2 := []string{"test"} + arr3 := []string{"test", "test2"} + now2 := now + + err := validate.FieldWithValue(s, s2, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(i2, i, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(j2, j, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(k2, k, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(arr2, arr, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(now2, now, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(arr3, arr, "eqfield") + c.Assert(err, NotNil) + + type Test struct { + Start *time.Time `validate:"eqfield=End"` + End *time.Time + } + + sv := &Test{ + Start: &now, + End: &now, + } + + errs := validate.Struct(sv) + c.Assert(errs, IsNil) + + now3 := time.Now().UTC() + + sv = &Test{ + Start: &now, + End: &now3, + } + + errs = validate.Struct(sv) + c.Assert(errs, NotNil) + + channel := make(chan string) + + c.Assert(func() { validate.FieldWithValue(nil, 1, "eqfield") }, PanicMatches, "struct not passed for cross validation") + c.Assert(func() { validate.FieldWithValue(5, channel, "eqfield") }, PanicMatches, "Bad field type chan string") + c.Assert(func() { validate.FieldWithValue(5, now, "eqfield") }, PanicMatches, "Bad Top Level field type") + + type Test2 struct { + Start *time.Time `validate:"eqfield=NonExistantField"` + End *time.Time + } + + sv2 := &Test2{ + Start: &now, + End: &now, + } + + c.Assert(func() { validate.Struct(sv2) }, PanicMatches, "Field \"NonExistantField\" not found in struct") +} + +func (ms *MySuite) TestIsEqValidation(c *C) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + err := validate.Field(s, "eq=abcd") + c.Assert(err, IsNil) + + err = validate.Field(i, "eq=1") + c.Assert(err, IsNil) + + err = validate.Field(j, "eq=1") + c.Assert(err, IsNil) + + err = validate.Field(k, "eq=1.543") + c.Assert(err, IsNil) + + err = validate.Field(arr, "eq=1") + c.Assert(err, IsNil) + + err = validate.Field(arr, "eq=2") + c.Assert(err, NotNil) + + c.Assert(func() { validate.Field(now, "eq=now") }, PanicMatches, "Bad field type time.Time") +} + func (ms *MySuite) TestBase64Validation(c *C) { s := "dW5pY29ybg=="