Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: go-playground/validator
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v10.19.0
Choose a base ref
...
head repository: go-playground/validator
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v10.20.0
Choose a head ref
  • 4 commits
  • 6 files changed
  • 4 contributors

Commits on Apr 30, 2024

  1. Fix ULID should case-insensitive (#1258)

    ## Fixes ULID should case-insensitive
    
    @go-playground/validator-maintainers
    akayj authored Apr 30, 2024

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    LastDragon-ru Aleksei Lebedev
    Copy the full SHA
    21b0b9f View commit details
  2. feat: add base32 validator tag (#1253)

    jamesatkin-myndup authored Apr 30, 2024

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    LastDragon-ru Aleksei Lebedev
    Copy the full SHA
    5187f87 View commit details
  3. Add EU countries validator (#1252)

    ## Enhances
    
    
    **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
    masv3971 authored Apr 30, 2024
    Copy the full SHA
    610b713 View commit details
  4. Update README.md

    Dean Karn authored Apr 30, 2024
    Copy the full SHA
    e20b948 View commit details
Showing with 242 additions and 5 deletions.
  1. +1 −1 README.md
  2. +46 −2 baked_in.go
  3. +27 −0 country_codes.go
  4. +9 −0 doc.go
  5. +3 −1 regexes.go
  6. +156 −1 validator_test.go
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package validator
=================
<img align="right" src="logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-10.19.0-green.svg)
![Project status](https://img.shields.io/badge/version-10.20.0-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
48 changes: 46 additions & 2 deletions baked_in.go
Original file line number Diff line number Diff line change
@@ -64,8 +64,9 @@ var (
// defines a common or complex set of validation(s) to simplify
// adding validation to structs.
bakedInAliases = map[string]string{
"iscolor": "hexcolor|rgb|rgba|hsl|hsla",
"country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric",
"iscolor": "hexcolor|rgb|rgba|hsl|hsla",
"country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric",
"eu_country_code": "iso3166_1_alpha2_eu|iso3166_1_alpha3_eu|iso3166_1_alpha_numeric_eu",
}

// bakedInValidators is the default map of ValidationFunc
@@ -133,6 +134,7 @@ var (
"urn_rfc2141": isUrnRFC2141, // RFC 2141
"file": isFile,
"filepath": isFilePath,
"base32": isBase32,
"base64": isBase64,
"base64url": isBase64URL,
"base64rawurl": isBase64RawURL,
@@ -216,8 +218,11 @@ var (
"datetime": isDatetime,
"timezone": isTimeZone,
"iso3166_1_alpha2": isIso3166Alpha2,
"iso3166_1_alpha2_eu": isIso3166Alpha2EU,
"iso3166_1_alpha3": isIso3166Alpha3,
"iso3166_1_alpha3_eu": isIso3166Alpha3EU,
"iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
"iso3166_1_alpha_numeric_eu": isIso3166AlphaNumericEU,
"iso3166_2": isIso31662,
"iso4217": isIso4217,
"iso4217_numeric": isIso4217Numeric,
@@ -1399,6 +1404,11 @@ func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool {
return reg.MatchString(field.String())
}

// isBase32 is the validation function for validating if the current field's value is a valid base 32.
func isBase32(fl FieldLevel) bool {
return base32Regex.MatchString(fl.Field().String())
}

// isBase64 is the validation function for validating if the current field's value is a valid base 64.
func isBase64(fl FieldLevel) bool {
return base64Regex.MatchString(fl.Field().String())
@@ -2762,12 +2772,24 @@ func isIso3166Alpha2(fl FieldLevel) bool {
return iso3166_1_alpha2[val]
}

// isIso3166Alpha2EU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 European Union country code.
func isIso3166Alpha2EU(fl FieldLevel) bool {
val := fl.Field().String()
return iso3166_1_alpha2_eu[val]
}

// isIso3166Alpha3 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 country code.
func isIso3166Alpha3(fl FieldLevel) bool {
val := fl.Field().String()
return iso3166_1_alpha3[val]
}

// isIso3166Alpha3EU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 European Union country code.
func isIso3166Alpha3EU(fl FieldLevel) bool {
val := fl.Field().String()
return iso3166_1_alpha3_eu[val]
}

// isIso3166AlphaNumeric is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric country code.
func isIso3166AlphaNumeric(fl FieldLevel) bool {
field := fl.Field()
@@ -2790,6 +2812,28 @@ func isIso3166AlphaNumeric(fl FieldLevel) bool {
return iso3166_1_alpha_numeric[code]
}

// isIso3166AlphaNumericEU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric European Union country code.
func isIso3166AlphaNumericEU(fl FieldLevel) bool {
field := fl.Field()

var code int
switch field.Kind() {
case reflect.String:
i, err := strconv.Atoi(field.String())
if err != nil {
return false
}
code = i % 1000
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
code = int(field.Int() % 1000)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
code = int(field.Uint() % 1000)
default:
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
return iso3166_1_alpha_numeric_eu[code]
}

// isIso31662 is the validation function for validating if the current field's value is a valid iso3166-2 code.
func isIso31662(fl FieldLevel) bool {
val := fl.Field().String()
27 changes: 27 additions & 0 deletions country_codes.go
Original file line number Diff line number Diff line change
@@ -54,6 +54,15 @@ var iso3166_1_alpha2 = map[string]bool{
"EH": true, "YE": true, "ZM": true, "ZW": true, "XK": true,
}

var iso3166_1_alpha2_eu = map[string]bool{
"AT": true, "BE": true, "BG": true, "HR": true, "CY": true,
"CZ": true, "DK": true, "EE": true, "FI": true, "FR": true,
"DE": true, "GR": true, "HU": true, "IE": true, "IT": true,
"LV": true, "LT": true, "LU": true, "MT": true, "NL": true,
"PL": true, "PT": true, "RO": true, "SK": true, "SI": true,
"ES": true, "SE": true,
}

var iso3166_1_alpha3 = map[string]bool{
// see: https://www.iso.org/iso-3166-country-codes.html
"AFG": true, "ALB": true, "DZA": true, "ASM": true, "AND": true,
@@ -107,6 +116,15 @@ var iso3166_1_alpha3 = map[string]bool{
"VNM": true, "VGB": true, "VIR": true, "WLF": true, "ESH": true,
"YEM": true, "ZMB": true, "ZWE": true, "ALA": true, "UNK": true,
}

var iso3166_1_alpha3_eu = map[string]bool{
"AUT": true, "BEL": true, "BGR": true, "HRV": true, "CYP": true,
"CZE": true, "DNK": true, "EST": true, "FIN": true, "FRA": true,
"DEU": true, "GRC": true, "HUN": true, "IRL": true, "ITA": true,
"LVA": true, "LTU": true, "LUX": true, "MLT": true, "NLD": true,
"POL": true, "PRT": true, "ROU": true, "SVK": true, "SVN": true,
"ESP": true, "SWE": true,
}
var iso3166_1_alpha_numeric = map[int]bool{
// see: https://www.iso.org/iso-3166-country-codes.html
4: true, 8: true, 12: true, 16: true, 20: true,
@@ -161,6 +179,15 @@ var iso3166_1_alpha_numeric = map[int]bool{
887: true, 894: true, 716: true, 248: true, 153: true,
}

var iso3166_1_alpha_numeric_eu = map[int]bool{
40: true, 56: true, 100: true, 191: true, 196: true,
200: true, 208: true, 233: true, 246: true, 250: true,
276: true, 300: true, 348: true, 372: true, 380: true,
428: true, 440: true, 442: true, 470: true, 528: true,
616: true, 620: true, 642: true, 703: true, 705: true,
724: true, 752: true,
}

var iso3166_2 = map[string]bool{
"AD-02": true, "AD-03": true, "AD-04": true, "AD-05": true, "AD-06": true,
"AD-07": true, "AD-08": true, "AE-AJ": true, "AE-AZ": true, "AE-DU": true,
9 changes: 9 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
@@ -916,6 +916,15 @@ according to the RFC 2141 spec.
Usage: urn_rfc2141
# Base32 String
This validates that a string value contains a valid bas324 value.
Although an empty string is valid base32 this will report an empty string
as an error, if you wish to accept an empty string as valid you can use
this with the omitempty tag.
Usage: base32
# Base64 String
This validates that a string value contains a valid base64 value.
4 changes: 3 additions & 1 deletion regexes.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ const (
hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
e164RegexString = "^\\+[1-9]?[0-9]{7,14}$"
base32RegexString = "^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=|[A-Z2-7]{8})$"
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$"
base64RawURLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2,4})$"
@@ -31,7 +32,7 @@ const (
uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
uLIDRegexString = "^[A-HJKMNP-TV-Z0-9]{26}$"
uLIDRegexString = "^(?i)[A-HJKMNP-TV-Z0-9]{26}$"
md4RegexString = "^[0-9a-f]{32}$"
md5RegexString = "^[0-9a-f]{32}$"
sha256RegexString = "^[0-9a-f]{64}$"
@@ -89,6 +90,7 @@ var (
hslaRegex = regexp.MustCompile(hslaRegexString)
e164Regex = regexp.MustCompile(e164RegexString)
emailRegex = regexp.MustCompile(emailRegexString)
base32Regex = regexp.MustCompile(base32RegexString)
base64Regex = regexp.MustCompile(base64RegexString)
base64URLRegex = regexp.MustCompile(base64URLRegexString)
base64RawURLRegex = regexp.MustCompile(base64RawURLRegexString)
157 changes: 156 additions & 1 deletion validator_test.go
Original file line number Diff line number Diff line change
@@ -4338,7 +4338,7 @@ func TestULIDValidation(t *testing.T) {
}{
{"", false},
{"01BX5ZZKBKACT-V9WEVGEMMVRZ", false},
{"01bx5zzkbkactav9wevgemmvrz", false},
{"01bx5zzkbkactav9wevgemmvrz", true},
{"a987Fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false},
{"01BX5ZZKBKACTAV9WEVGEMMVRZABC", false},
{"01BX5ZZKBKACTAV9WEVGEMMVRZABC", false},
@@ -5680,6 +5680,40 @@ func TestOneOfValidation(t *testing.T) {
}, "Bad field type float64")
}

func TestBase32Validation(t *testing.T) {
validate := New()

s := "ABCD2345"
errs := validate.Var(s, "base32")
Equal(t, errs, nil)

s = "AB======"
errs = validate.Var(s, "base32")
Equal(t, errs, nil)

s = "ABCD2==="
errs = validate.Var(s, "base32")
Equal(t, errs, nil)

s = "ABCD===="
errs = validate.Var(s, "base32")
Equal(t, errs, nil)

s = "ABCD234="
errs = validate.Var(s, "base32")
Equal(t, errs, nil)

s = ""
errs = validate.Var(s, "base32")
NotEqual(t, errs, nil)
AssertError(t, errs, "", "", "", "", "base32")

s = "ABCabc1890== foo bar"
errs = validate.Var(s, "base32")
NotEqual(t, errs, nil)
AssertError(t, errs, "", "", "", "", "base32")
}

func TestBase64Validation(t *testing.T) {
validate := New()

@@ -12482,6 +12516,33 @@ func TestIsIso3166Alpha2Validation(t *testing.T) {
}
}

func TestIsIso3166Alpha2EUValidation(t *testing.T) {
tests := []struct {
value string `validate:"iso3166_1_alpha2_eu"`
expected bool
}{
{"SE", true},
{"UK", false},
}

validate := New()

for i, test := range tests {

errs := validate.Var(test.value, "iso3166_1_alpha2_eu")

if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d iso3166_1_alpha2_eu failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d iso3166_1_alpha2_eu failed Error: %s", i, errs)
}
}
}
}

func TestIsIso31662Validation(t *testing.T) {
tests := []struct {
value string `validate:"iso3166_2"`
@@ -12538,6 +12599,34 @@ func TestIsIso3166Alpha3Validation(t *testing.T) {
}
}

func TestIsIso3166Alpha3EUValidation(t *testing.T) {
tests := []struct {
value string `validate:"iso3166_1_alpha3_eu"`
expected bool
}{
{"POL", true},
{"SWE", true},
{"UNK", false},
}

validate := New()

for i, test := range tests {

errs := validate.Var(test.value, "iso3166_1_alpha3_eu")

if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d iso3166_1_alpha3_eu failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d iso3166_1_alpha3_eu failed Error: %s", i, errs)
}
}
}
}

func TestIsIso3166AlphaNumericValidation(t *testing.T) {
tests := []struct {
value interface{}
@@ -12573,6 +12662,39 @@ func TestIsIso3166AlphaNumericValidation(t *testing.T) {
}, "Bad field type []string")
}

func TestIsIso3166AlphaNumericEUValidation(t *testing.T) {
tests := []struct {
value interface{}
expected bool
}{
{752, true}, //Sweden
{"752", true},
{826, false}, // UK
{"826", false},
}

validate := New()

for i, test := range tests {

errs := validate.Var(test.value, "iso3166_1_alpha_numeric_eu")

if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d iso3166_1_alpha_numeric_eu failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d iso3166_1_alpha_numeric_eu failed Error: %s", i, errs)
}
}
}

PanicMatches(t, func() {
_ = validate.Var([]string{"1"}, "iso3166_1_alpha_numeric_eu")
}, "Bad field type []string")
}

func TestCountryCodeValidation(t *testing.T) {
tests := []struct {
value interface{}
@@ -12606,6 +12728,39 @@ func TestCountryCodeValidation(t *testing.T) {
}
}

func TestEUCountryCodeValidation(t *testing.T) {
tests := []struct {
value interface{}
expected bool
}{
{724, true},
{0, false},
{1, false},
{"POL", true},
{"NO", false},
{"724", true},
{"1", false},
{"0", false},
}

validate := New()

for i, test := range tests {

errs := validate.Var(test.value, "eu_country_code")

if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d eu_country_code failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d eu_country_code failed Error: %s", i, errs)
}
}
}
}

func TestIsIso4217Validation(t *testing.T) {
tests := []struct {
value string `validate:"iso4217"`