Repository: go-playground/validator
Pull request #572 (2020-02-05 20:42:47)

Add isLowercase and isUppercase validation
- [x] Tests exist or have been written that cover this particular change.
- [x] doc.go was updated
- [x] en translation and translation tests were updated

Change Details:

This PR adds 2 new validations that checks if a string value contains only lowercase or uppercase characters.

@go-playground/admins


Patch:
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 799882c..8893378 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 {


Test patch:
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")
+}