Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for validating against uuid values that are structs which implement the Stringer interface. #1189

Merged
merged 1 commit into from Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 9 additions & 9 deletions baked_in.go
Expand Up @@ -508,47 +508,47 @@ func isASCII(fl FieldLevel) bool {

// isUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
func isUUID5(fl FieldLevel) bool {
return uUID5Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID5Regex, fl)
}

// isUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
func isUUID4(fl FieldLevel) bool {
return uUID4Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID4Regex, fl)
}

// isUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
func isUUID3(fl FieldLevel) bool {
return uUID3Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID3Regex, fl)
}

// isUUID is the validation function for validating if the field's value is a valid UUID of any version.
func isUUID(fl FieldLevel) bool {
return uUIDRegex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUIDRegex, fl)
}

// isUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID.
func isUUID5RFC4122(fl FieldLevel) bool {
return uUID5RFC4122Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID5RFC4122Regex, fl)
}

// isUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID.
func isUUID4RFC4122(fl FieldLevel) bool {
return uUID4RFC4122Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID4RFC4122Regex, fl)
}

// isUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID.
func isUUID3RFC4122(fl FieldLevel) bool {
return uUID3RFC4122Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID3RFC4122Regex, fl)
}

// isUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version.
func isUUIDRFC4122(fl FieldLevel) bool {
return uUIDRFC4122Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUIDRFC4122Regex, fl)
}

// isULID is the validation function for validating if the field's value is a valid ULID.
func isULID(fl FieldLevel) bool {
return uLIDRegex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uLIDRegex, fl)
}

// isMD4 is the validation function for validating if the field's value is a valid MD4.
Expand Down
11 changes: 11 additions & 0 deletions util.go
@@ -1,7 +1,9 @@
package validator

import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -292,3 +294,12 @@ func panicIf(err error) {
panic(err.Error())
}
}

// Checks if field value matches regex. If fl.Field can be cast to Stringer, it uses the Stringer interfaces
// String() return value. Otherwise, it uses fl.Field's String() value.
func fieldMatchesRegexByStringerValOrString(regex *regexp.Regexp, fl FieldLevel) bool {
if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
return regex.MatchString(stringer.String())
}
return regex.MatchString(fl.Field().String())
}
29 changes: 29 additions & 0 deletions validator_test.go
Expand Up @@ -4105,6 +4105,16 @@ func TestUUID3Validation(t *testing.T) {
}
}

type uuidTestType struct {
val string
}

func (u uuidTestType) String() string {
return u.val
}

var _ fmt.Stringer = uuidTestType{}

func TestUUIDValidation(t *testing.T) {
tests := []struct {
param string
Expand Down Expand Up @@ -4141,6 +4151,25 @@ func TestUUIDValidation(t *testing.T) {
}
}
}

// Test UUID validation on uuid structs type that implements Stringer interface.
structWithValidUUID := struct {
UUID uuidTestType `validate:"uuid"`
}{
UUID: uuidTestType{val: "a987fbc9-4bed-3078-cf07-9141ba07c9f3"},
}
structWithInvalidUUID := struct {
UUID uuidTestType `validate:"uuid"`
}{
UUID: uuidTestType{val: "934859"},
}

if err := validate.Struct(structWithValidUUID); err != nil {
t.Fatalf("UUID failed Error: %s", err)
}
if err := validate.Struct(structWithInvalidUUID); err == nil {
t.Fatal("UUID failed Error expected but received nil")
}
}

func TestUUID5RFC4122Validation(t *testing.T) {
Expand Down