Skip to content

Commit 4c1bd61

Browse files
Add support for validating against uuid values that are structs which implement the Stringer interface. (#1189)
## Fixes Or Enhances This adds the ability to validate UUIDs that have an underlying struct value but also implement the Stringer interface. This should cover most UUID implementations (google's uuid, etc). Implements: #900, specifically #900 (comment). **Make sure that you've checked the boxes below before you submit PR:** - [x] Tests exist or have been written that cover this particular change. @go-playground/validator-maintainers
1 parent adda84d commit 4c1bd61

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

baked_in.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -508,47 +508,47 @@ func isASCII(fl FieldLevel) bool {
508508

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

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

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

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

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

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

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

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

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

554554
// isMD4 is the validation function for validating if the field's value is a valid MD4.

util.go

+11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package validator
22

33
import (
4+
"fmt"
45
"reflect"
6+
"regexp"
57
"strconv"
68
"strings"
79
"time"
@@ -292,3 +294,12 @@ func panicIf(err error) {
292294
panic(err.Error())
293295
}
294296
}
297+
298+
// Checks if field value matches regex. If fl.Field can be cast to Stringer, it uses the Stringer interfaces
299+
// String() return value. Otherwise, it uses fl.Field's String() value.
300+
func fieldMatchesRegexByStringerValOrString(regex *regexp.Regexp, fl FieldLevel) bool {
301+
if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
302+
return regex.MatchString(stringer.String())
303+
}
304+
return regex.MatchString(fl.Field().String())
305+
}

validator_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -4105,6 +4105,16 @@ func TestUUID3Validation(t *testing.T) {
41054105
}
41064106
}
41074107

4108+
type uuidTestType struct {
4109+
val string
4110+
}
4111+
4112+
func (u uuidTestType) String() string {
4113+
return u.val
4114+
}
4115+
4116+
var _ fmt.Stringer = uuidTestType{}
4117+
41084118
func TestUUIDValidation(t *testing.T) {
41094119
tests := []struct {
41104120
param string
@@ -4141,6 +4151,25 @@ func TestUUIDValidation(t *testing.T) {
41414151
}
41424152
}
41434153
}
4154+
4155+
// Test UUID validation on uuid structs type that implements Stringer interface.
4156+
structWithValidUUID := struct {
4157+
UUID uuidTestType `validate:"uuid"`
4158+
}{
4159+
UUID: uuidTestType{val: "a987fbc9-4bed-3078-cf07-9141ba07c9f3"},
4160+
}
4161+
structWithInvalidUUID := struct {
4162+
UUID uuidTestType `validate:"uuid"`
4163+
}{
4164+
UUID: uuidTestType{val: "934859"},
4165+
}
4166+
4167+
if err := validate.Struct(structWithValidUUID); err != nil {
4168+
t.Fatalf("UUID failed Error: %s", err)
4169+
}
4170+
if err := validate.Struct(structWithInvalidUUID); err == nil {
4171+
t.Fatal("UUID failed Error expected but received nil")
4172+
}
41444173
}
41454174

41464175
func TestUUID5RFC4122Validation(t *testing.T) {

0 commit comments

Comments
 (0)