diff --git a/baked_in.go b/baked_in.go index 9eff0e4..91fd479 100644 --- a/baked_in.go +++ b/baked_in.go @@ -166,6 +166,8 @@ var ( "html_encoded": isHTMLEncoded, "url_encoded": isURLEncoded, "dir": isDir, + "lowercase": isLowercase, + "uppercase": isUppercase, } ) @@ -2007,3 +2009,31 @@ func isDir(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } + +// isLowercase is the validation function for validating if the current field's value is a lowercase string. +func isLowercase(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + if field.String() == "" { + return false + } + return field.String() == strings.ToLower(field.String()) + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// isUppercase is the validation function for validating if the current field's value is an uppercase string. +func isUppercase(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + if field.String() == "" { + return false + } + return field.String() == strings.ToUpper(field.String()) + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} diff --git a/doc.go b/doc.go index ecd8092..0b7a27c 100644 --- a/doc.go +++ b/doc.go @@ -641,6 +641,18 @@ hashtag (#) Usage: hexcolor +Lowercase String + +This validates that a string value contains only lowercase characters. An empty string is not a valid lowercase string. + + Usage: lowercase + +Uppercase String + +This validates that a string value contains only uppercase characters. An empty string is not a valid uppercase string. + + Usage: uppercase + RGB String This validates that a string value contains a valid rgb color diff --git a/translations/en/en.go b/translations/en/en.go index 3b1058b..cf89fd2 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1321,6 +1321,16 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "lowercase", + translation: "{0} must be a lowercase string", + override: false, + }, + { + tag: "uppercase", + translation: "{0} must be an uppercase string", + override: false, + }, } for _, t := range translations { diff --git a/translations/en/en_test.go b/translations/en/en_test.go index cddcb84..769409a 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" + . "github.com/go-playground/assert/v2" english "github.com/go-playground/locales/en" ut "github.com/go-playground/universal-translator" - . "github.com/go-playground/assert/v2" "github.com/go-playground/validator/v10" ) @@ -141,6 +141,8 @@ func TestTranslations(t *testing.T) { UniqueSlice []string `validate:"unique"` UniqueArray [3]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"` + LowercaseString string `validate:"lowercase"` + UppercaseString string `validate:"uppercase"` } var test Test @@ -183,6 +185,9 @@ func TestTranslations(t *testing.T) { test.MultiByte = "1234feerf" + test.LowercaseString = "ABCDEFG" + test.UppercaseString = "abcdefg" + s := "toolong" test.StrPtrMaxLen = &s test.StrPtrLen = &s @@ -632,6 +637,14 @@ func TestTranslations(t *testing.T) { ns: "Test.UniqueMap", expected: "UniqueMap must contain unique values", }, + { + ns: "Test.LowercaseString", + expected: "LowercaseString must be a lowercase string", + }, + { + ns: "Test.UppercaseString", + expected: "UppercaseString must be an uppercase string", + }, } for _, tt := range tests { diff --git a/validator_test.go b/validator_test.go index 2c8b96b..f5d6d01 100644 --- a/validator_test.go +++ b/validator_test.go @@ -9003,3 +9003,77 @@ func TestGetTag(t *testing.T) { Equal(t, errs, nil) Equal(t, tag, "mytag") } + +func TestLowercaseValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {`abcdefg`, true}, + {`Abcdefg`, false}, + {"", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "lowercase") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d lowercase failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d lowercase failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "lowercase" { + t.Fatalf("Index: %d lowercase failed Error: %s", i, errs) + } + } + } + } + + PanicMatches(t, func() { + _ = validate.Var(2, "lowercase") + }, "Bad field type int") +} + +func TestUppercaseValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {`ABCDEFG`, true}, + {`aBCDEFG`, false}, + {"", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "uppercase") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d uppercase failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d uppercase failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "uppercase" { + t.Fatalf("Index: %d uppercase failed Error: %s", i, errs) + } + } + } + } + + PanicMatches(t, func() { + _ = validate.Var(2, "uppercase") + }, "Bad field type int") +}