Merge branch 'master' into update-id

pull/819/head
Dean Karn 3 years ago committed by GitHub
commit 4641f5681c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      .github/workflows/workflow.yml
  2. 9
      README.md
  3. 303
      baked_in.go
  4. 59
      doc.go
  5. 4
      go.mod
  6. 10
      go.sum
  7. 20
      regexes.go
  8. 15
      translations/en/en.go
  9. 19
      translations/en/en_test.go
  10. 5
      translations/es/es.go
  11. 7
      translations/es/es_test.go
  12. 5
      translations/fa/fa.go
  13. 5
      translations/fa/fa_test.go
  14. 5
      translations/fr/fr.go
  15. 5
      translations/fr/fr_test.go
  16. 5
      translations/id/id.go
  17. 4
      translations/id/id_test.go
  18. 1244
      translations/it/it.go
  19. 724
      translations/it/it_test.go
  20. 13
      translations/ja/ja.go
  21. 15
      translations/ja/ja_test.go
  22. 5
      translations/nl/nl.go
  23. 7
      translations/nl/nl_test.go
  24. 5
      translations/pt/pt.go
  25. 5
      translations/pt/pt_test.go
  26. 10
      translations/pt_BR/pt_BR.go
  27. 13
      translations/pt_BR/pt_BR_test.go
  28. 5
      translations/ru/ru.go
  29. 5
      translations/ru/ru_test.go
  30. 5
      translations/tr/tr.go
  31. 7
      translations/tr/tr_test.go
  32. 35
      translations/zh/zh.go
  33. 5
      translations/zh/zh_test.go
  34. 5
      translations/zh_tw/zh_tw.go
  35. 5
      translations/zh_tw/zh_tw_test.go
  36. 2
      util.go
  37. 11
      validator.go
  38. 14
      validator_instance.go
  39. 424
      validator_test.go

@ -8,7 +8,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.15.x, 1.16.x] go-version: [1.17.x, 1.18.x]
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
@ -32,7 +32,7 @@ jobs:
run: go test -race -covermode=atomic -coverprofile="profile.cov" ./... run: go test -race -covermode=atomic -coverprofile="profile.cov" ./...
- name: Send Coverage - name: Send Coverage
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.16.x' if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.18.x'
uses: shogo82148/actions-goveralls@v1 uses: shogo82148/actions-goveralls@v1
with: with:
path-to-profile: profile.cov path-to-profile: profile.cov
@ -41,8 +41,11 @@ jobs:
name: lint name: lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/setup-go@v3
with:
go-version: 1.16
- uses: actions/checkout@v3
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v3
with: with:
version: v1.41.1 version: v1.45.2

@ -1,7 +1,7 @@
Package validator Package validator
================= =================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/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) <img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/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.9.0-green.svg) ![Project status](https://img.shields.io/badge/version-10.10.1-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator) [![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) [![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) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
@ -130,7 +130,7 @@ Baked-in Validations
| contains | Contains | | contains | Contains |
| containsany | Contains Any | | containsany | Contains Any |
| containsrune | Contains Rune | | containsrune | Contains Rune |
| endsnotwith | Ends With | | endsnotwith | Ends Not With |
| endswith | Ends With | | endswith | Ends With |
| excludes | Excludes | | excludes | Excludes |
| excludesall | Excludes All | | excludesall | Excludes All |
@ -153,6 +153,7 @@ Baked-in Validations
| bcp47_language_tag | Language tag (BCP 47) | | bcp47_language_tag | Language tag (BCP 47) |
| btc_addr | Bitcoin Address | | btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) | | btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| datetime | Datetime | | datetime | Datetime |
| e164 | e164 formatted phone number | | e164 | e164 formatted phone number |
| email | E-mail String | email | E-mail String
@ -189,6 +190,8 @@ Baked-in Validations
| uuid5 | Universally Unique Identifier UUID v5 | | uuid5 | Universally Unique Identifier UUID v5 |
| uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 | | uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
| uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 | | uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
| semver | Semantic Versioning 2.0.0 |
| ulid | Universally Unique Lexicographically Sortable Identifier ULID |
### Comparisons: ### Comparisons:
| Tag | Description | | Tag | Description |
@ -217,6 +220,8 @@ Baked-in Validations
| required_with_all | Required With All | | required_with_all | Required With All |
| required_without | Required Without | | required_without | Required Without |
| required_without_all | Required Without All | | required_without_all | Required Without All |
| excluded_if | Excluded If |
| excluded_unless | Excluded Unless |
| excluded_with | Excluded With | | excluded_with | Excluded With |
| excluded_with_all | Excluded With All | | excluded_with_all | Excluded With All |
| excluded_without | Excluded Without | | excluded_without | Excluded Without |

@ -75,6 +75,8 @@ var (
"required_with_all": requiredWithAll, "required_with_all": requiredWithAll,
"required_without": requiredWithout, "required_without": requiredWithout,
"required_without_all": requiredWithoutAll, "required_without_all": requiredWithoutAll,
"excluded_if": excludedIf,
"excluded_unless": excludedUnless,
"excluded_with": excludedWith, "excluded_with": excludedWith,
"excluded_with_all": excludedWithAll, "excluded_with_all": excludedWithAll,
"excluded_without": excludedWithout, "excluded_without": excludedWithout,
@ -148,6 +150,7 @@ var (
"uuid3_rfc4122": isUUID3RFC4122, "uuid3_rfc4122": isUUID3RFC4122,
"uuid4_rfc4122": isUUID4RFC4122, "uuid4_rfc4122": isUUID4RFC4122,
"uuid5_rfc4122": isUUID5RFC4122, "uuid5_rfc4122": isUUID5RFC4122,
"ulid": isULID,
"ascii": isASCII, "ascii": isASCII,
"printascii": isPrintableASCII, "printascii": isPrintableASCII,
"multibyte": hasMultiByteCharacter, "multibyte": hasMultiByteCharacter,
@ -198,6 +201,9 @@ var (
"postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2, "postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2,
"postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field, "postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
"bic": isIsoBicFormat, "bic": isIsoBicFormat,
"semver": isSemverFormat,
"dns_rfc1035_label": isDnsRFC1035LabelFormat,
"credit_card": isCreditCard,
} }
) )
@ -498,6 +504,11 @@ func isUUIDRFC4122(fl FieldLevel) bool {
return uUIDRFC4122Regex.MatchString(fl.Field().String()) return uUIDRFC4122Regex.MatchString(fl.Field().String())
} }
// 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())
}
// isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN. // isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN.
func isISBN(fl FieldLevel) bool { func isISBN(fl FieldLevel) bool {
return isISBN10(fl) || isISBN13(fl) return isISBN10(fl) || isISBN13(fl)
@ -808,12 +819,7 @@ func isNeField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
if fieldType != currentField.Type() {
return true
}
if fieldType == timeType {
t := currentField.Interface().(time.Time) t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time) fieldTime := field.Interface().(time.Time)
@ -821,6 +827,10 @@ func isNeField(fl FieldLevel) bool {
return !fieldTime.Equal(t) return !fieldTime.Equal(t)
} }
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return true
}
} }
// default reflect.String: // default reflect.String:
@ -861,18 +871,18 @@ func isLteCrossStructField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
if fieldType != topField.Type() {
return false
}
if fieldType == timeType { fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
topTime := topField.Interface().(time.Time)
return fieldTime.Before(topTime) || fieldTime.Equal(topTime) return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
} }
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
} }
// default reflect.String: // default reflect.String:
@ -909,18 +919,18 @@ func isLtCrossStructField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
fieldTime := field.Interface().(time.Time) fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Interface().(time.Time) topTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.Before(topTime) return fieldTime.Before(topTime)
} }
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
} }
// default reflect.String: // default reflect.String:
@ -956,18 +966,18 @@ func isGteCrossStructField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
fieldTime := field.Interface().(time.Time) fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Interface().(time.Time) topTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.After(topTime) || fieldTime.Equal(topTime) return fieldTime.After(topTime) || fieldTime.Equal(topTime)
} }
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
} }
// default reflect.String: // default reflect.String:
@ -1003,18 +1013,18 @@ func isGtCrossStructField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
fieldTime := field.Interface().(time.Time) fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Interface().(time.Time) topTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.After(topTime) return fieldTime.After(topTime)
} }
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
} }
// default reflect.String: // default reflect.String:
@ -1053,18 +1063,18 @@ func isNeCrossStructField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
if fieldType != topField.Type() {
return true
}
if fieldType == timeType {
t := field.Interface().(time.Time) t := field.Convert(timeType).Interface().(time.Time)
fieldTime := topField.Interface().(time.Time) fieldTime := topField.Convert(timeType).Interface().(time.Time)
return !fieldTime.Equal(t) return !fieldTime.Equal(t)
} }
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return true
}
} }
// default reflect.String: // default reflect.String:
@ -1103,18 +1113,18 @@ func isEqCrossStructField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
t := field.Interface().(time.Time) t := field.Convert(timeType).Interface().(time.Time)
fieldTime := topField.Interface().(time.Time) fieldTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.Equal(t) return fieldTime.Equal(t)
} }
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
} }
// default reflect.String: // default reflect.String:
@ -1153,19 +1163,18 @@ func isEqField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType { t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.Equal(t) return fieldTime.Equal(t)
} }
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
} }
// default reflect.String: // default reflect.String:
@ -1534,6 +1543,22 @@ func requiredIf(fl FieldLevel) bool {
return hasValue(fl) return hasValue(fl)
} }
// excludedIf is the validation function
// The field under validation must not be present or is empty only if all the other specified fields are equal to the value following with the specified field.
func excludedIf(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
if len(params)%2 != 0 {
panic(fmt.Sprintf("Bad param number for excluded_if %s", fl.FieldName()))
}
for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return false
}
}
return true
}
// requiredUnless is the validation function // requiredUnless is the validation function
// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field. // The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
func requiredUnless(fl FieldLevel) bool { func requiredUnless(fl FieldLevel) bool {
@ -1550,6 +1575,21 @@ func requiredUnless(fl FieldLevel) bool {
return hasValue(fl) return hasValue(fl)
} }
// excludedUnless is the validation function
// The field under validation must not be present or is empty unless all the other specified fields are equal to the value following with the specified field.
func excludedUnless(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
if len(params)%2 != 0 {
panic(fmt.Sprintf("Bad param number for excluded_unless %s", fl.FieldName()))
}
for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return true
}
}
return !hasValue(fl)
}
// excludedWith is the validation function // excludedWith is the validation function
// The field under validation must not be present or is empty if any of the other specified fields are present. // The field under validation must not be present or is empty if any of the other specified fields are present.
func excludedWith(fl FieldLevel) bool { func excludedWith(fl FieldLevel) bool {
@ -1669,18 +1709,18 @@ func isGteField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType { t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.After(t) || fieldTime.Equal(t) return fieldTime.After(t) || fieldTime.Equal(t)
} }
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
} }
// default reflect.String // default reflect.String
@ -1716,18 +1756,18 @@ func isGtField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType { t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.After(t) return fieldTime.After(t)
} }
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
} }
// default reflect.String // default reflect.String
@ -1769,10 +1809,10 @@ func isGte(fl FieldLevel) bool {
case reflect.Struct: case reflect.Struct:
if field.Type() == timeType { if field.Type().ConvertibleTo(timeType) {
now := time.Now().UTC() now := time.Now().UTC()
t := field.Interface().(time.Time) t := field.Convert(timeType).Interface().(time.Time)
return t.After(now) || t.Equal(now) return t.After(now) || t.Equal(now)
} }
@ -1815,9 +1855,9 @@ func isGt(fl FieldLevel) bool {
return field.Float() > p return field.Float() > p
case reflect.Struct: case reflect.Struct:
if field.Type() == timeType { if field.Type().ConvertibleTo(timeType) {
return field.Interface().(time.Time).After(time.Now().UTC()) return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC())
} }
} }
@ -1895,18 +1935,18 @@ func isLteField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType { t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.Before(t) || fieldTime.Equal(t) return fieldTime.Before(t) || fieldTime.Equal(t)
} }
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
} }
// default reflect.String // default reflect.String
@ -1942,18 +1982,18 @@ func isLtField(fl FieldLevel) bool {
fieldType := field.Type() fieldType := field.Type()
// Not Same underlying type i.e. struct and time if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType { t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.Before(t) return fieldTime.Before(t)
} }
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
} }
// default reflect.String // default reflect.String
@ -1995,10 +2035,10 @@ func isLte(fl FieldLevel) bool {
case reflect.Struct: case reflect.Struct:
if field.Type() == timeType { if field.Type().ConvertibleTo(timeType) {
now := time.Now().UTC() now := time.Now().UTC()
t := field.Interface().(time.Time) t := field.Convert(timeType).Interface().(time.Time)
return t.Before(now) || t.Equal(now) return t.Before(now) || t.Equal(now)
} }
@ -2042,9 +2082,9 @@ func isLt(fl FieldLevel) bool {
case reflect.Struct: case reflect.Struct:
if field.Type() == timeType { if field.Type().ConvertibleTo(timeType) {
return field.Interface().(time.Time).Before(time.Now().UTC()) return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC())
} }
} }
@ -2351,6 +2391,12 @@ func isIso3166AlphaNumeric(fl FieldLevel) bool {
var code int var code int
switch field.Kind() { 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: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
code = int(field.Int() % 1000) code = int(field.Int() % 1000)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
@ -2407,3 +2453,56 @@ func isIsoBicFormat(fl FieldLevel) bool {
return bicRegex.MatchString(bicString) return bicRegex.MatchString(bicString)
} }
// isSemverFormat is the validation function for validating if the current field's value is a valid semver version, defined in Semantic Versioning 2.0.0
func isSemverFormat(fl FieldLevel) bool {
semverString := fl.Field().String()
return semverRegex.MatchString(semverString)
}
// isDnsRFC1035LabelFormat is the validation function
// for validating if the current field's value is
// a valid dns RFC 1035 label, defined in RFC 1035.
func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
val := fl.Field().String()
return dnsRegexRFC1035Label.MatchString(val)
}
// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
func isCreditCard(fl FieldLevel) bool {
val := fl.Field().String()
var creditCard bytes.Buffer
segments := strings.Split(val, " ")
for _, segment := range segments {
if len(segment) < 3 {
return false
}
creditCard.WriteString(segment)
}
ccDigits := strings.Split(creditCard.String(), "")
size := len(ccDigits)
if size < 12 || size > 19 {
return false
}
sum := 0
for i, digit := range ccDigits {
value, err := strconv.Atoi(digit)
if err != nil {
return false
}
if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 {
v := value * 2
if v >= 10 {
sum += 1 + (v % 10)
} else {
sum += v
}
} else {
sum += value
}
}
return (sum % 10) == 0
}

@ -349,6 +349,40 @@ Example:
// require the field if the Field1 and Field2 is not present: // require the field if the Field1 and Field2 is not present:
Usage: required_without_all=Field1 Field2 Usage: required_without_all=Field1 Field2
Excluded If
The field under validation must not be present or not empty only if all
the other specified fields are equal to the value following the specified
field. For strings ensures value is not "". For slices, maps, pointers,
interfaces, channels and functions ensures the value is not nil.
Usage: excluded_if
Examples:
// exclude the field if the Field1 is equal to the parameter given:
Usage: excluded_if=Field1 foobar
// exclude the field if the Field1 and Field2 is equal to the value respectively:
Usage: excluded_if=Field1 foo Field2 bar
Excluded Unless
The field under validation must not be present or empty unless all
the other specified fields are equal to the value following the specified
field. For strings ensures value is not "". For slices, maps, pointers,
interfaces, channels and functions ensures the value is not nil.
Usage: excluded_unless
Examples:
// exclude the field unless the Field1 is equal to the parameter given:
Usage: excluded_unless=Field1 foobar
// exclude the field unless the Field1 and Field2 is equal to the value respectively:
Usage: excluded_unless=Field1 foo Field2 bar
Is Default Is Default
This validates that the value is the default value and is almost the This validates that the value is the default value and is almost the
@ -1007,6 +1041,12 @@ This validates that a string value contains a valid version 5 UUID. Uppercase U
Usage: uuid5 Usage: uuid5
Universally Unique Lexicographically Sortable Identifier ULID
This validates that a string value contains a valid ULID value.
Usage: ulid
ASCII ASCII
This validates that a string value contains only ASCII characters. This validates that a string value contains only ASCII characters.
@ -1255,6 +1295,13 @@ More information on https://www.iso.org/standard/60390.html
Usage: bic Usage: bic
RFC 1035 label
This validates that a string value is a valid dns RFC 1035 label, defined in RFC 1035.
More information on https://datatracker.ietf.org/doc/html/rfc1035
Usage: dns_rfc1035_label
TimeZone TimeZone
This validates that a string value is a valid time zone based on the time zone database present on the system. This validates that a string value is a valid time zone based on the time zone database present on the system.
@ -1263,6 +1310,18 @@ More information on https://golang.org/pkg/time/#LoadLocation
Usage: timezone Usage: timezone
Semantic Version
This validates that a string value is a valid semver version, defined in Semantic Versioning 2.0.0.
More information on https://semver.org/
Usage: semver
Credit Card
This validates that a string value contains a valid credit card number using Luhn algoritm.
Usage: credit_card
Alias Validators and Tags Alias Validators and Tags

@ -11,9 +11,9 @@ require (
github.com/leodido/go-urn v1.2.1 github.com/leodido/go-urn v1.2.1
github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/stretchr/testify v1.7.0 // indirect github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
golang.org/x/text v0.3.6 golang.org/x/text v0.3.7
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
) )

@ -28,17 +28,19 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

@ -10,7 +10,7 @@ const (
numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$"
numberRegexString = "^[0-9]+$" numberRegexString = "^[0-9]+$"
hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$" hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$"
hexColorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" hexColorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"
rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$" rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$"
rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
hslRegexString = "^hsl\\(\\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*\\)$" hslRegexString = "^hsl\\(\\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*\\)$"
@ -29,6 +29,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}$" 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}$" 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}$" 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}$"
aSCIIRegexString = "^[\x00-\x7F]*$" aSCIIRegexString = "^[\x00-\x7F]*$"
printableASCIIRegexString = "^[\x20-\x7E]*$" printableASCIIRegexString = "^[\x20-\x7E]*$"
multibyteRegexString = "[^\x00-\x7F]" multibyteRegexString = "[^\x00-\x7F]"
@ -36,12 +37,12 @@ const (
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$` sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952 hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123 hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62}){1}(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62})(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.') fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$` ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$` ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$` ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
@ -51,6 +52,8 @@ const (
jWTRegexString = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$" jWTRegexString = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$"
splitParamsRegexString = `'[^']*'|\S+` splitParamsRegexString = `'[^']*'|\S+`
bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$` bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
) )
var ( var (
@ -80,6 +83,7 @@ var (
uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString) uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString)
uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString) uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString)
uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString) uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString)
uLIDRegex = regexp.MustCompile(uLIDRegexString)
aSCIIRegex = regexp.MustCompile(aSCIIRegexString) aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString) printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
multibyteRegex = regexp.MustCompile(multibyteRegexString) multibyteRegex = regexp.MustCompile(multibyteRegexString)
@ -102,4 +106,6 @@ var (
jWTRegex = regexp.MustCompile(jWTRegexString) jWTRegex = regexp.MustCompile(jWTRegexString)
splitParamsRegex = regexp.MustCompile(splitParamsRegexString) splitParamsRegex = regexp.MustCompile(splitParamsRegexString)
bicRegex = regexp.MustCompile(bicRegexString) bicRegex = regexp.MustCompile(bicRegexString)
semverRegex = regexp.MustCompile(semverRegexString)
dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label)
) )

@ -28,6 +28,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} is a required field", translation: "{0} is a required field",
override: false, override: false,
}, },
{
tag: "required_if",
translation: "{0} is a required field",
override: false,
},
{ {
tag: "len", tag: "len",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
@ -1136,6 +1141,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid version 5 UUID", translation: "{0} must be a valid version 5 UUID",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} must be a valid ULID",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} must contain only ascii characters", translation: "{0} must contain only ascii characters",
@ -1341,6 +1351,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return t return t
}, },
}, },
{
tag: "boolean",
translation: "{0} must be a valid boolean value",
override: false,
},
} }
for _, t := range translations { for _, t := range translations {

@ -27,6 +27,7 @@ func TestTranslations(t *testing.T) {
GteCSFieldString string GteCSFieldString string
LtCSFieldString string LtCSFieldString string
LteCSFieldString string LteCSFieldString string
RequiredIf string
} }
type Test struct { type Test struct {
@ -34,6 +35,7 @@ func TestTranslations(t *testing.T) {
RequiredString string `validate:"required"` RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"` RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"` RequiredMultiple []string `validate:"required"`
RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"`
LenString string `validate:"len=1"` LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"` LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"` LenMultiple []string `validate:"len=7"`
@ -103,6 +105,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -148,6 +151,7 @@ func TestTranslations(t *testing.T) {
PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
BooleanString string `validate:"boolean"`
} }
var test Test var test Test
@ -200,6 +204,9 @@ func TestTranslations(t *testing.T) {
test.UniqueSlice = []string{"1234", "1234"} test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"} test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01" test.Datetime = "2008-Feb-01"
test.BooleanString = "A"
test.Inner.RequiredIf = "abcd"
err = validate.Struct(test) err = validate.Struct(test)
NotEqual(t, err, nil) NotEqual(t, err, nil)
@ -323,6 +330,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 must be a valid version 5 UUID", expected: "UUID5 must be a valid version 5 UUID",
}, },
{
ns: "Test.ULID",
expected: "ULID must be a valid ULID",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN must be a valid ISBN number", expected: "ISBN must be a valid ISBN number",
@ -587,6 +598,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.RequiredString", ns: "Test.RequiredString",
expected: "RequiredString is a required field", expected: "RequiredString is a required field",
}, },
{
ns: "Test.RequiredIf",
expected: "RequiredIf is a required field",
},
{ {
ns: "Test.RequiredNumber", ns: "Test.RequiredNumber",
expected: "RequiredNumber is a required field", expected: "RequiredNumber is a required field",
@ -671,6 +686,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.PostCodeByField", ns: "Test.PostCodeByField",
expected: "PostCodeByField does not match postcode format of country in PostCodeCountry field", expected: "PostCodeByField does not match postcode format of country in PostCodeCountry field",
}, },
{
ns: "Test.BooleanString",
expected: "BooleanString must be a valid boolean value",
},
} }
for _, tt := range tests { for _, tt := range tests {

@ -1178,6 +1178,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} debe ser una versión válida 5 UUID", translation: "{0} debe ser una versión válida 5 UUID",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} debe ser un ULID válido",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} debe contener sólo caracteres ascii", translation: "{0} debe contener sólo caracteres ascii",

@ -4,9 +4,9 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
spanish "github.com/go-playground/locales/es" spanish "github.com/go-playground/locales/es"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "github.com/go-playground/assert/v2"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
) )
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -312,6 +313,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 debe ser una versión válida 5 UUID", expected: "UUID5 debe ser una versión válida 5 UUID",
}, },
{
ns: "Test.ULID",
expected: "ULID debe ser un ULID válido",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN debe ser un número ISBN válido", expected: "ISBN debe ser un número ISBN válido",

@ -1136,6 +1136,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} باید یک UUID نسخه 5 معتبر باشد", translation: "{0} باید یک UUID نسخه 5 معتبر باشد",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} باید یک ULID معتبر باشد",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} باید فقط شامل کاراکترهای اسکی باشد", translation: "{0} باید فقط شامل کاراکترهای اسکی باشد",

@ -103,6 +103,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -322,6 +323,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 باید یک UUID نسخه 5 معتبر باشد", expected: "UUID5 باید یک UUID نسخه 5 معتبر باشد",
}, },
{
ns: "Test.ULID",
expected: "ULID باید یک ULID معتبر باشد",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN باید یک شابک معتبر باشد", expected: "ISBN باید یک شابک معتبر باشد",

@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} doit être un UUID version 5 valid", translation: "{0} doit être un UUID version 5 valid",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} doit être une ULID valide",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} ne doit contenir que des caractères ascii", translation: "{0} ne doit contenir que des caractères ascii",

@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 doit être un UUID version 5 valid", expected: "UUID5 doit être un UUID version 5 valid",
}, },
{
ns: "Test.ULID",
expected: "ULID doit être une ULID valide",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN doit être un numéro ISBN valid", expected: "ISBN doit être un numéro ISBN valid",

@ -1136,6 +1136,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} harus berupa UUID versi 5 yang valid", translation: "{0} harus berupa UUID versi 5 yang valid",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} harus berupa ULID yang valid",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} hanya boleh berisi karakter ascii", translation: "{0} hanya boleh berisi karakter ascii",

@ -323,6 +323,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 harus berupa UUID versi 5 yang valid", expected: "UUID5 harus berupa UUID versi 5 yang valid",
}, },
{
ns: "Test.ULID",
expected: "ULID harus berupa ULID yang valid",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN harus berupa nomor ISBN yang valid", expected: "ISBN harus berupa nomor ISBN yang valid",

File diff suppressed because it is too large Load Diff

@ -0,0 +1,724 @@
package en
import (
"testing"
"time"
. "github.com/go-playground/assert/v2"
italian "github.com/go-playground/locales/it"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
)
func TestTranslations(t *testing.T) {
it := italian.New()
uni := ut.New(it, it)
trans, _ := uni.GetTranslator("en")
validate := validator.New()
err := RegisterDefaultTranslations(validate, trans)
Equal(t, err, nil)
type Inner struct {
EqCSFieldString string
NeCSFieldString string
GtCSFieldString string
GteCSFieldString string
LtCSFieldString string
LteCSFieldString string
}
type Test struct {
Inner Inner
RequiredString string `validate:"required"`
RequiredNumber int `validate:"required"`
RequiredMultiple []string `validate:"required"`
LenString string `validate:"len=1"`
LenNumber float64 `validate:"len=1113.00"`
LenMultiple []string `validate:"len=7"`
MinString string `validate:"min=1"`
MinNumber float64 `validate:"min=1113.00"`
MinMultiple []string `validate:"min=7"`
MaxString string `validate:"max=3"`
MaxNumber float64 `validate:"max=1113.00"`
MaxMultiple []string `validate:"max=7"`
EqString string `validate:"eq=3"`
EqNumber float64 `validate:"eq=2.33"`
EqMultiple []string `validate:"eq=7"`
NeString string `validate:"ne="`
NeNumber float64 `validate:"ne=0.00"`
NeMultiple []string `validate:"ne=0"`
LtString string `validate:"lt=3"`
LtNumber float64 `validate:"lt=5.56"`
LtMultiple []string `validate:"lt=2"`
LtTime time.Time `validate:"lt"`
LteString string `validate:"lte=3"`
LteNumber float64 `validate:"lte=5.56"`
LteMultiple []string `validate:"lte=2"`
LteTime time.Time `validate:"lte"`
GtString string `validate:"gt=3"`
GtNumber float64 `validate:"gt=5.56"`
GtMultiple []string `validate:"gt=2"`
GtTime time.Time `validate:"gt"`
GteString string `validate:"gte=3"`
GteNumber float64 `validate:"gte=5.56"`
GteMultiple []string `validate:"gte=2"`
GteTime time.Time `validate:"gte"`
EqFieldString string `validate:"eqfield=MaxString"`
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
NeFieldString string `validate:"nefield=EqFieldString"`
GtFieldString string `validate:"gtfield=MaxString"`
GteFieldString string `validate:"gtefield=MaxString"`
LtFieldString string `validate:"ltfield=MaxString"`
LteFieldString string `validate:"ltefield=MaxString"`
AlphaString string `validate:"alpha"`
AlphanumString string `validate:"alphanum"`
NumericString string `validate:"numeric"`
NumberString string `validate:"number"`
HexadecimalString string `validate:"hexadecimal"`
HexColorString string `validate:"hexcolor"`
RGBColorString string `validate:"rgb"`
RGBAColorString string `validate:"rgba"`
HSLColorString string `validate:"hsl"`
HSLAColorString string `validate:"hsla"`
Email string `validate:"email"`
URL string `validate:"url"`
URI string `validate:"uri"`
Base64 string `validate:"base64"`
Contains string `validate:"contains=purpose"`
ContainsAny string `validate:"containsany=!@#$"`
Excludes string `validate:"excludes=text"`
ExcludesAll string `validate:"excludesall=!@#$"`
ExcludesRune string `validate:"excludesrune=☻"`
ISBN string `validate:"isbn"`
ISBN10 string `validate:"isbn10"`
ISBN13 string `validate:"isbn13"`
UUID string `validate:"uuid"`
UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"`
DataURI string `validate:"datauri"`
Latitude string `validate:"latitude"`
Longitude string `validate:"longitude"`
SSN string `validate:"ssn"`
IP string `validate:"ip"`
IPv4 string `validate:"ipv4"`
IPv6 string `validate:"ipv6"`
CIDR string `validate:"cidr"`
CIDRv4 string `validate:"cidrv4"`
CIDRv6 string `validate:"cidrv6"`
TCPAddr string `validate:"tcp_addr"`
TCPAddrv4 string `validate:"tcp4_addr"`
TCPAddrv6 string `validate:"tcp6_addr"`
UDPAddr string `validate:"udp_addr"`
UDPAddrv4 string `validate:"udp4_addr"`
UDPAddrv6 string `validate:"udp6_addr"`
IPAddr string `validate:"ip_addr"`
IPAddrv4 string `validate:"ip4_addr"`
IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
StrPtrMinLen *string `validate:"min=10"`
StrPtrMaxLen *string `validate:"max=1"`
StrPtrLen *string `validate:"len=2"`
StrPtrLt *string `validate:"lt=1"`
StrPtrLte *string `validate:"lte=1"`
StrPtrGt *string `validate:"gt=10"`
StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"`
UniqueSlice []string `validate:"unique"`
UniqueArray [3]string `validate:"unique"`
UniqueMap map[string]string `validate:"unique"`
BooleanString string `validate:"boolean"`
JSONString string `validate:"json"`
JWTString string `validate:"jwt"`
LowercaseString string `validate:"lowercase"`
UppercaseString string `validate:"uppercase"`
StartsWithString string `validate:"startswith=foo"`
StartsNotWithString string `validate:"startsnotwith=foo"`
EndsWithString string `validate:"endswith=foo"`
EndsNotWithString string `validate:"endsnotwith=foo"`
Datetime string `validate:"datetime=2006-01-02"`
PostCode string `validate:"postcode_iso3166_alpha2=SG"`
PostCodeCountry string
PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"`
}
var test Test
test.Inner.EqCSFieldString = "1234"
test.Inner.GtCSFieldString = "1234"
test.Inner.GteCSFieldString = "1234"
test.MaxString = "1234"
test.MaxNumber = 2000
test.MaxMultiple = make([]string, 9)
test.LtString = "1234"
test.LtNumber = 6
test.LtMultiple = make([]string, 3)
test.LtTime = time.Now().Add(time.Hour * 24)
test.LteString = "1234"
test.LteNumber = 6
test.LteMultiple = make([]string, 3)
test.LteTime = time.Now().Add(time.Hour * 24)
test.LtFieldString = "12345"
test.LteFieldString = "12345"
test.LtCSFieldString = "1234"
test.LteCSFieldString = "1234"
test.AlphaString = "abc3"
test.AlphanumString = "abc3!"
test.NumericString = "12E.00"
test.NumberString = "12E"
test.Excludes = "this is some test text"
test.ExcludesAll = "This is Great!"
test.ExcludesRune = "Love it ☻"
test.ASCII = "カタカナ"
test.PrintableASCII = "カタカナ"
test.MultiByte = "1234feerf"
test.LowercaseString = "ABCDEFG"
test.UppercaseString = "abcdefg"
test.StartsWithString = "hello"
test.StartsNotWithString = "foo-hello"
test.EndsWithString = "hello"
test.EndsNotWithString = "hello-foo"
s := "toolong"
test.StrPtrMaxLen = &s
test.StrPtrLen = &s
test.UniqueSlice = []string{"1234", "1234"}
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
test.Datetime = "2008-Feb-01"
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(validator.ValidationErrors)
Equal(t, ok, true)
tests := []struct {
ns string
expected string
}{
{
ns: "Test.IsColor",
expected: "IsColor deve essere un colore valido",
},
{
ns: "Test.MAC",
expected: "MAC deve contenere un indirizzo MAC valido",
},
{
ns: "Test.IPAddr",
expected: "IPAddr deve essere un indirizzo IP risolvibile",
},
{
ns: "Test.IPAddrv4",
expected: "IPAddrv4 deve essere un indirizzo IPv4 risolvibile",
},
{
ns: "Test.IPAddrv6",
expected: "IPAddrv6 deve essere un indirizzo IPv6 risolvibile",
},
{
ns: "Test.UDPAddr",
expected: "UDPAddr deve essere un indirizzo UDP valido",
},
{
ns: "Test.UDPAddrv4",
expected: "UDPAddrv4 deve essere un indirizzo IPv4 UDP valido",
},
{
ns: "Test.UDPAddrv6",
expected: "UDPAddrv6 deve essere un indirizzo IPv6 UDP valido",
},
{
ns: "Test.TCPAddr",
expected: "TCPAddr deve essere un indirizzo TCP valido",
},
{
ns: "Test.TCPAddrv4",
expected: "TCPAddrv4 deve essere un indirizzo IPv4 TCP valido",
},
{
ns: "Test.TCPAddrv6",
expected: "TCPAddrv6 deve essere un indirizzo IPv6 TCP valido",
},
{
ns: "Test.CIDR",
expected: "CIDR deve contenere una notazione CIDR valida",
},
{
ns: "Test.CIDRv4",
expected: "CIDRv4 deve contenere una notazione CIDR per un indirizzo IPv4 valida",
},
{
ns: "Test.CIDRv6",
expected: "CIDRv6 deve contenere una notazione CIDR per un indirizzo IPv6 valida",
},
{
ns: "Test.SSN",
expected: "SSN deve essere un numero SSN valido",
},
{
ns: "Test.IP",
expected: "IP deve essere un indirizzo IP valido",
},
{
ns: "Test.IPv4",
expected: "IPv4 deve essere un indirizzo IPv4 valido",
},
{
ns: "Test.IPv6",
expected: "IPv6 deve essere un indirizzo IPv6 valido",
},
{
ns: "Test.DataURI",
expected: "DataURI deve contenere un Data URI valido",
},
{
ns: "Test.Latitude",
expected: "Latitude deve contenere una latitudine valida",
},
{
ns: "Test.Longitude",
expected: "Longitude deve contenere una longitudine valida",
},
{
ns: "Test.MultiByte",
expected: "MultiByte deve contenere caratteri multibyte",
},
{
ns: "Test.ASCII",
expected: "ASCII deve contenere solo caratteri ascii",
},
{
ns: "Test.PrintableASCII",
expected: "PrintableASCII deve contenere solo caratteri ascii stampabili",
},
{
ns: "Test.UUID",
expected: "UUID deve essere un UUID valido",
},
{
ns: "Test.UUID3",
expected: "UUID3 deve essere un UUID versione 3 valido",
},
{
ns: "Test.UUID4",
expected: "UUID4 deve essere un UUID versione 4 valido",
},
{
ns: "Test.UUID5",
expected: "UUID5 deve essere un UUID versione 5 valido",
},
{
ns: "Test.ULID",
expected: "ULID deve essere un ULID valido",
},
{
ns: "Test.ISBN",
expected: "ISBN deve essere un numero ISBN valido",
},
{
ns: "Test.ISBN10",
expected: "ISBN10 deve essere un numero ISBN-10 valido",
},
{
ns: "Test.ISBN13",
expected: "ISBN13 deve essere un numero ISBN-13 valido",
},
{
ns: "Test.Excludes",
expected: "Excludes non deve contenere il testo 'text'",
},
{
ns: "Test.ExcludesAll",
expected: "ExcludesAll non deve contenere alcuno dei seguenti caratteri '!@#$'",
},
{
ns: "Test.ExcludesRune",
expected: "ExcludesRune non deve contenere '☻'",
},
{
ns: "Test.ContainsAny",
expected: "ContainsAny deve contenere almeno uno dei seguenti caratteri '!@#$'",
},
{
ns: "Test.Contains",
expected: "Contains deve contenere il testo 'purpose'",
},
{
ns: "Test.Base64",
expected: "Base64 deve essere una stringa Base64 valida",
},
{
ns: "Test.Email",
expected: "Email deve essere un indirizzo email valido",
},
{
ns: "Test.URL",
expected: "URL deve essere un URL valido",
},
{
ns: "Test.URI",
expected: "URI deve essere un URI valido",
},
{
ns: "Test.RGBColorString",
expected: "RGBColorString deve essere un colore RGB valido",
},
{
ns: "Test.RGBAColorString",
expected: "RGBAColorString deve essere un colore RGBA valido",
},
{
ns: "Test.HSLColorString",
expected: "HSLColorString deve essere un colore HSL valido",
},
{
ns: "Test.HSLAColorString",
expected: "HSLAColorString deve essere un colore HSLA valido",
},
{
ns: "Test.HexadecimalString",
expected: "HexadecimalString deve essere un esadecimale valido",
},
{
ns: "Test.HexColorString",
expected: "HexColorString deve essere un colore HEX valido",
},
{
ns: "Test.NumberString",
expected: "NumberString deve essere un numero valido",
},
{
ns: "Test.NumericString",
expected: "NumericString deve essere un valore numerico valido",
},
{
ns: "Test.AlphanumString",
expected: "AlphanumString può contenere solo caratteri alfanumerici",
},
{
ns: "Test.AlphaString",
expected: "AlphaString può contenere solo caratteri alfabetici",
},
{
ns: "Test.LtFieldString",
expected: "LtFieldString deve essere minore di MaxString",
},
{
ns: "Test.LteFieldString",
expected: "LteFieldString deve essere minore o uguale a MaxString",
},
{
ns: "Test.GtFieldString",
expected: "GtFieldString deve essere maggiore di MaxString",
},
{
ns: "Test.GteFieldString",
expected: "GteFieldString deve essere maggiore o uguale a MaxString",
},
{
ns: "Test.NeFieldString",
expected: "NeFieldString deve essere diverso da EqFieldString",
},
{
ns: "Test.LtCSFieldString",
expected: "LtCSFieldString deve essere minore di Inner.LtCSFieldString",
},
{
ns: "Test.LteCSFieldString",
expected: "LteCSFieldString deve essere minore o uguale a Inner.LteCSFieldString",
},
{
ns: "Test.GtCSFieldString",
expected: "GtCSFieldString deve essere maggiore di Inner.GtCSFieldString",
},
{
ns: "Test.GteCSFieldString",
expected: "GteCSFieldString deve essere maggiore o uguale a Inner.GteCSFieldString",
},
{
ns: "Test.NeCSFieldString",
expected: "NeCSFieldString deve essere diverso da Inner.NeCSFieldString",
},
{
ns: "Test.EqCSFieldString",
expected: "EqCSFieldString deve essere uguale a Inner.EqCSFieldString",
},
{
ns: "Test.EqFieldString",
expected: "EqFieldString deve essere uguale a MaxString",
},
{
ns: "Test.GteString",
expected: "GteString deve essere lungo almeno 3 caratteri",
},
{
ns: "Test.GteNumber",
expected: "GteNumber deve essere maggiore o uguale a 5,56",
},
{
ns: "Test.GteMultiple",
expected: "GteMultiple deve contenere almeno 2 elementi",
},
{
ns: "Test.GteTime",
expected: "GteTime deve essere uguale o successivo alla Data/Ora corrente",
},
{
ns: "Test.GtString",
expected: "GtString deve essere lungo più di 3 caratteri",
},
{
ns: "Test.GtNumber",
expected: "GtNumber deve essere maggiore di 5,56",
},
{
ns: "Test.GtMultiple",
expected: "GtMultiple deve contenere più di 2 elementi",
},
{
ns: "Test.GtTime",
expected: "GtTime deve essere successivo alla Data/Ora corrente",
},
{
ns: "Test.LteString",
expected: "LteString deve essere lungo al massimo 3 caratteri",
},
{
ns: "Test.LteNumber",
expected: "LteNumber deve essere minore o uguale a 5,56",
},
{
ns: "Test.LteMultiple",
expected: "LteMultiple deve contenere al massimo 2 elementi",
},
{
ns: "Test.LteTime",
expected: "LteTime deve essere uguale o precedente alla Data/Ora corrente",
},
{
ns: "Test.LtString",
expected: "LtString deve essere lungo meno di 3 caratteri",
},
{
ns: "Test.LtNumber",
expected: "LtNumber deve essere minore di 5,56",
},
{
ns: "Test.LtMultiple",
expected: "LtMultiple deve contenere meno di 2 elementi",
},
{
ns: "Test.LtTime",
expected: "LtTime deve essere precedente alla Data/Ora corrente",
},
{
ns: "Test.NeString",
expected: "NeString deve essere diverso da ",
},
{
ns: "Test.NeNumber",
expected: "NeNumber deve essere diverso da 0.00",
},
{
ns: "Test.NeMultiple",
expected: "NeMultiple deve essere diverso da 0",
},
{
ns: "Test.EqString",
expected: "EqString non è uguale a 3",
},
{
ns: "Test.EqNumber",
expected: "EqNumber non è uguale a 2.33",
},
{
ns: "Test.EqMultiple",
expected: "EqMultiple non è uguale a 7",
},
{
ns: "Test.MaxString",
expected: "MaxString deve essere lungo al massimo 3 caratteri",
},
{
ns: "Test.MaxNumber",
expected: "MaxNumber deve essere minore o uguale a 1.113,00",
},
{
ns: "Test.MaxMultiple",
expected: "MaxMultiple deve contenere al massimo 7 elementi",
},
{
ns: "Test.MinString",
expected: "MinString deve essere lungo almeno 1 carattere",
},
{
ns: "Test.MinNumber",
expected: "MinNumber deve essere maggiore o uguale a 1.113,00",
},
{
ns: "Test.MinMultiple",
expected: "MinMultiple deve contenere almeno 7 elementi",
},
{
ns: "Test.LenString",
expected: "LenString deve essere lungo 1 carattere",
},
{
ns: "Test.LenNumber",
expected: "LenNumber deve essere uguale a 1.113,00",
},
{
ns: "Test.LenMultiple",
expected: "LenMultiple deve contenere 7 elementi",
},
{
ns: "Test.RequiredString",
expected: "RequiredString è un campo obbligatorio",
},
{
ns: "Test.RequiredNumber",
expected: "RequiredNumber è un campo obbligatorio",
},
{
ns: "Test.RequiredMultiple",
expected: "RequiredMultiple è un campo obbligatorio",
},
{
ns: "Test.StrPtrMinLen",
expected: "StrPtrMinLen deve essere lungo almeno 10 caratteri",
},
{
ns: "Test.StrPtrMaxLen",
expected: "StrPtrMaxLen deve essere lungo al massimo 1 carattere",
},
{
ns: "Test.StrPtrLen",
expected: "StrPtrLen deve essere lungo 2 caratteri",
},
{
ns: "Test.StrPtrLt",
expected: "StrPtrLt deve essere lungo meno di 1 carattere",
},
{
ns: "Test.StrPtrLte",
expected: "StrPtrLte deve essere lungo al massimo 1 carattere",
},
{
ns: "Test.StrPtrGt",
expected: "StrPtrGt deve essere lungo più di 10 caratteri",
},
{
ns: "Test.StrPtrGte",
expected: "StrPtrGte deve essere lungo almeno 10 caratteri",
},
{
ns: "Test.OneOfString",
expected: "OneOfString deve essere uno di [red green]",
},
{
ns: "Test.OneOfInt",
expected: "OneOfInt deve essere uno di [5 63]",
},
{
ns: "Test.UniqueSlice",
expected: "UniqueSlice deve contenere valori unici",
},
{
ns: "Test.UniqueArray",
expected: "UniqueArray deve contenere valori unici",
},
{
ns: "Test.UniqueMap",
expected: "UniqueMap deve contenere valori unici",
},
{
ns: "Test.BooleanString",
expected: "BooleanString deve rappresentare un valore booleano",
},
{
ns: "Test.JSONString",
expected: "JSONString deve essere una stringa json valida",
},
{
ns: "Test.JWTString",
expected: "JWTString deve essere una stringa jwt valida",
},
{
ns: "Test.LowercaseString",
expected: "LowercaseString deve essere una stringa minuscola",
},
{
ns: "Test.UppercaseString",
expected: "UppercaseString deve essere una stringa maiuscola",
},
{
ns: "Test.StartsWithString",
expected: "StartsWithString deve iniziare con foo",
},
{
ns: "Test.StartsNotWithString",
expected: "StartsNotWithString non deve iniziare con foo",
},
{
ns: "Test.EndsWithString",
expected: "EndsWithString deve terminare con foo",
},
{
ns: "Test.EndsNotWithString",
expected: "EndsNotWithString non deve terminare con foo",
},
{
ns: "Test.Datetime",
expected: "Datetime non corrisponde al formato 2006-01-02",
},
{
ns: "Test.PostCode",
expected: "PostCode non corrisponde al formato del codice postale dello stato SG",
},
{
ns: "Test.PostCodeByField",
expected: "PostCodeByField non corrisponde al formato del codice postale dello stato nel campo PostCodeCountry",
},
}
for _, tt := range tests {
var fe validator.FieldError
for _, e := range errs {
if tt.ns == e.Namespace() {
fe = e
break
}
}
NotEqual(t, fe, nil)
Equal(t, tt.expected, fe.Translate(trans))
}
}

@ -136,7 +136,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
} }
if err = ut.Add("min-number", "{0}は{1}より大きくなければなりません", false); err != nil { if err = ut.Add("min-number", "{0}は{1}より大きくなければなりません", false); err != nil {
return return
} }
@ -227,7 +227,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
} }
if err = ut.Add("max-number", "{0}は{1}より小さくなければなりません", false); err != nil { if err = ut.Add("max-number", "{0}は{1}より小さくなければなりません", false); err != nil {
return return
} }
@ -525,7 +525,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
} }
if err = ut.Add("lte-number", "{0}は{1}より小さくなければなりません", false); err != nil { if err = ut.Add("lte-number", "{0}は{1}より小さくなければなりません", false); err != nil {
return return
} }
@ -765,7 +765,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return return
} }
if err = ut.Add("gte-number", "{0}は{1}より大きくなければなりません", false); err != nil { if err = ut.Add("gte-number", "{0}は{1}より大きくなければなりません", false); err != nil {
return return
} }
@ -1229,6 +1229,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}はバージョンが5の正しいUUIDでなければなりません", translation: "{0}はバージョンが5の正しいUUIDでなければなりません",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0}は正しいULIDでなければなりません",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0}はASCII文字のみを含まなければなりません", translation: "{0}はASCII文字のみを含まなければなりません",

@ -4,9 +4,9 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
ja_locale "github.com/go-playground/locales/ja" ja_locale "github.com/go-playground/locales/ja"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "github.com/go-playground/assert/v2"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
) )
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5はバージョンが5の正しいUUIDでなければなりません", expected: "UUID5はバージョンが5の正しいUUIDでなければなりません",
}, },
{
ns: "Test.ULID",
expected: "ULIDは正しいULIDでなければなりません",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBNは正しいISBN番号でなければなりません", expected: "ISBNは正しいISBN番号でなければなりません",
@ -448,7 +453,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.GteNumber", ns: "Test.GteNumber",
expected: "GteNumberは5.56より大きくなければなりません", expected: "GteNumberは5.56より大きくなければなりません",
}, },
{ {
ns: "Test.GteMultiple", ns: "Test.GteMultiple",
@ -480,7 +485,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.LteNumber", ns: "Test.LteNumber",
expected: "LteNumberは5.56より小さくなければなりません", expected: "LteNumberは5.56より小さくなければなりません",
}, },
{ {
ns: "Test.LteMultiple", ns: "Test.LteMultiple",
@ -536,7 +541,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.MaxNumber", ns: "Test.MaxNumber",
expected: "MaxNumberは1,113.00より小さくなければなりません", expected: "MaxNumberは1,113.00より小さくなければなりません",
}, },
{ {
ns: "Test.MaxMultiple", ns: "Test.MaxMultiple",
@ -548,7 +553,7 @@ func TestTranslations(t *testing.T) {
}, },
{ {
ns: "Test.MinNumber", ns: "Test.MinNumber",
expected: "MinNumberは1,113.00より大きくなければなりません", expected: "MinNumberは1,113.00より大きくなければなりません",
}, },
{ {
ns: "Test.MinMultiple", ns: "Test.MinMultiple",

@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} moet een geldige versie 5 UUID zijn", translation: "{0} moet een geldige versie 5 UUID zijn",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} moet een geldige ULID zijn",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} mag alleen ascii karakters bevatten", translation: "{0} mag alleen ascii karakters bevatten",

@ -4,9 +4,9 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
english "github.com/go-playground/locales/en" english "github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "github.com/go-playground/assert/v2"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
) )
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -306,6 +307,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 moet een geldige versie 5 UUID zijn", expected: "UUID5 moet een geldige versie 5 UUID zijn",
}, },
{
ns: "Test.ULID",
expected: "ULID moet een geldige ULID zijn",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN moet een geldig ISBN nummer zijn", expected: "ISBN moet een geldig ISBN nummer zijn",

@ -1178,6 +1178,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} deve ser um UUID versão 5 válido", translation: "{0} deve ser um UUID versão 5 válido",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} deve ser um ULID válido",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} deve conter apenas caracteres ascii", translation: "{0} deve conter apenas caracteres ascii",

@ -105,6 +105,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -321,6 +322,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 deve ser um UUID versão 5 válido", expected: "UUID5 deve ser um UUID versão 5 válido",
}, },
{
ns: "Test.ULID",
expected: "ULID deve ser um ULID válido",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN deve ser um número de ISBN válido", expected: "ISBN deve ser um número de ISBN válido",

@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} deve ser um UUID versão 5 válido", translation: "{0} deve ser um UUID versão 5 válido",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} deve ser uma ULID válida",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} deve conter apenas caracteres ascii", translation: "{0} deve conter apenas caracteres ascii",
@ -1311,6 +1316,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return s return s
}, },
}, },
{
tag: "boolean",
translation: "{0} deve ser um valor booleano válido",
override: false,
},
} }
for _, t := range translations { for _, t := range translations {

@ -4,10 +4,10 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
brazilian_portuguese "github.com/go-playground/locales/pt_BR" brazilian_portuguese "github.com/go-playground/locales/pt_BR"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
. "github.com/go-playground/assert/v2"
) )
func TestTranslations(t *testing.T) { func TestTranslations(t *testing.T) {
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -138,6 +139,7 @@ func TestTranslations(t *testing.T) {
StrPtrGte *string `validate:"gte=10"` StrPtrGte *string `validate:"gte=10"`
OneOfString string `validate:"oneof=red green"` OneOfString string `validate:"oneof=red green"`
OneOfInt int `validate:"oneof=5 63"` OneOfInt int `validate:"oneof=5 63"`
BooleanString string `validate:"boolean"`
} }
var test Test var test Test
@ -170,6 +172,7 @@ func TestTranslations(t *testing.T) {
test.AlphanumString = "abc3!" test.AlphanumString = "abc3!"
test.NumericString = "12E.00" test.NumericString = "12E.00"
test.NumberString = "12E" test.NumberString = "12E"
test.BooleanString = "A"
test.Excludes = "este é um texto de teste" test.Excludes = "este é um texto de teste"
test.ExcludesAll = "Isso é Ótimo!" test.ExcludesAll = "Isso é Ótimo!"
@ -306,6 +309,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 deve ser um UUID versão 5 válido", expected: "UUID5 deve ser um UUID versão 5 válido",
}, },
{
ns: "Test.ULID",
expected: "ULID deve ser uma ULID válida",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN deve ser um número ISBN válido", expected: "ISBN deve ser um número ISBN válido",
@ -614,6 +621,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.OneOfInt", ns: "Test.OneOfInt",
expected: "OneOfInt deve ser um de [5 63]", expected: "OneOfInt deve ser um de [5 63]",
}, },
{
ns: "Test.BooleanString",
expected: "BooleanString deve ser um valor booleano válido",
},
} }
for _, tt := range tests { for _, tt := range tests {

@ -1291,6 +1291,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} должен быть UUID 5 версии", translation: "{0} должен быть UUID 5 версии",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} должен быть ULID",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} должен содержать только ascii символы", translation: "{0} должен содержать только ascii символы",

@ -120,6 +120,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -340,6 +341,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 должен быть UUID 5 версии", expected: "UUID5 должен быть UUID 5 версии",
}, },
{
ns: "Test.ULID",
expected: "ULID должен быть ULID",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN должен быть ISBN номером", expected: "ISBN должен быть ISBN номером",

@ -1173,6 +1173,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} geçerli bir sürüm 5 UUID olmalıdır", translation: "{0} geçerli bir sürüm 5 UUID olmalıdır",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0} geçerli bir ULID olmalıdır",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0} yalnızca ascii karakterler içermelidir", translation: "{0} yalnızca ascii karakterler içermelidir",

@ -4,9 +4,9 @@ import (
"testing" "testing"
"time" "time"
. "github.com/go-playground/assert/v2"
turkish "github.com/go-playground/locales/tr" turkish "github.com/go-playground/locales/tr"
ut "github.com/go-playground/universal-translator" ut "github.com/go-playground/universal-translator"
. "github.com/go-playground/assert/v2"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
) )
@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -312,6 +313,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5 geçerli bir sürüm 5 UUID olmalıdır", expected: "UUID5 geçerli bir sürüm 5 UUID olmalıdır",
}, },
{
ns: "Test.ULID",
expected: "ULID geçerli bir ULID olmalıdır",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN geçerli bir ISBN numarası olmalıdır", expected: "ISBN geçerli bir ISBN numarası olmalıdır",

@ -29,6 +29,36 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}为必填字段", translation: "{0}为必填字段",
override: false, override: false,
}, },
{
tag: "required_if",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_unless",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_with",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_with_all",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_without",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_without_all",
translation: "{0}为必填字段",
override: false,
},
{ {
tag: "len", tag: "len",
customRegisFunc: func(ut ut.Translator) (err error) { customRegisFunc: func(ut ut.Translator) (err error) {
@ -1225,6 +1255,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}必须是一个有效的V5 UUID", translation: "{0}必须是一个有效的V5 UUID",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0}必须是一个有效的ULID",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0}必须只包含ascii字符", translation: "{0}必须只包含ascii字符",

@ -109,6 +109,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -327,6 +328,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5必须是一个有效的V5 UUID", expected: "UUID5必须是一个有效的V5 UUID",
}, },
{
ns: "Test.ULID",
expected: "ULID必须是一个有效的ULID",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN必须是一个有效的ISBN编号", expected: "ISBN必须是一个有效的ISBN编号",

@ -1166,6 +1166,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}必須是一個有效的V5 UUID", translation: "{0}必須是一個有效的V5 UUID",
override: false, override: false,
}, },
{
tag: "ulid",
translation: "{0}必須是一個有效的ULID",
override: false,
},
{ {
tag: "ascii", tag: "ascii",
translation: "{0}必須只包含ascii字元", translation: "{0}必須只包含ascii字元",

@ -104,6 +104,7 @@ func TestTranslations(t *testing.T) {
UUID3 string `validate:"uuid3"` UUID3 string `validate:"uuid3"`
UUID4 string `validate:"uuid4"` UUID4 string `validate:"uuid4"`
UUID5 string `validate:"uuid5"` UUID5 string `validate:"uuid5"`
ULID string `validate:"ulid"`
ASCII string `validate:"ascii"` ASCII string `validate:"ascii"`
PrintableASCII string `validate:"printascii"` PrintableASCII string `validate:"printascii"`
MultiByte string `validate:"multibyte"` MultiByte string `validate:"multibyte"`
@ -309,6 +310,10 @@ func TestTranslations(t *testing.T) {
ns: "Test.UUID5", ns: "Test.UUID5",
expected: "UUID5必須是一個有效的V5 UUID", expected: "UUID5必須是一個有效的V5 UUID",
}, },
{
ns: "Test.ULID",
expected: "ULID必須是一個有效的ULID",
},
{ {
ns: "Test.ISBN", ns: "Test.ISBN",
expected: "ISBN必須是一個有效的ISBN編號", expected: "ISBN必須是一個有效的ISBN編號",

@ -82,7 +82,7 @@ BEGIN:
fld := namespace fld := namespace
var ns string var ns string
if typ != timeType { if !typ.ConvertibleTo(timeType) {
idx := strings.Index(namespace, namespaceSeparator) idx := strings.Index(namespace, namespaceSeparator)

@ -164,7 +164,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
typ = current.Type() typ = current.Type()
if typ != timeType { if !typ.ConvertibleTo(timeType) {
if ct != nil { if ct != nil {
@ -355,6 +355,10 @@ OUTER:
v.ct = ct v.ct = ct
if ct.fn(ctx, v) { if ct.fn(ctx, v) {
if ct.isBlockEnd {
ct = ct.next
continue OUTER
}
// drain rest of the 'or' values, then continue or leave // drain rest of the 'or' values, then continue or leave
for { for {
@ -368,6 +372,11 @@ OUTER:
if ct.typeof != typeOr { if ct.typeof != typeOr {
continue OUTER continue OUTER
} }
if ct.isBlockEnd {
ct = ct.next
continue OUTER
}
} }
} }

@ -33,6 +33,8 @@ const (
excludedWithoutTag = "excluded_without" excludedWithoutTag = "excluded_without"
excludedWithTag = "excluded_with" excludedWithTag = "excluded_with"
excludedWithAllTag = "excluded_with_all" excludedWithAllTag = "excluded_with_all"
excludedIfTag = "excluded_if"
excludedUnlessTag = "excluded_unless"
skipValidationTag = "-" skipValidationTag = "-"
diveTag = "dive" diveTag = "dive"
keysTag = "keys" keysTag = "keys"
@ -120,7 +122,7 @@ func New() *Validate {
switch k { switch k {
// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag, case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag: excludedIfTag, excludedUnlessTag, excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag:
_ = v.registerValidation(k, wrapFunc(val), true, true) _ = v.registerValidation(k, wrapFunc(val), true, true)
default: default:
// no need to error check here, baked in will always be valid // no need to error check here, baked in will always be valid
@ -169,7 +171,7 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{
return errs return errs
} }
// ValidateMap validates map data form a map of tags // ValidateMap validates map data from a map of tags
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} { func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
return v.ValidateMapCtx(context.Background(), data, rules) return v.ValidateMapCtx(context.Background(), data, rules)
} }
@ -331,7 +333,7 @@ func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
val = val.Elem() val = val.Elem()
} }
if val.Kind() != reflect.Struct || val.Type() == timeType { if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)} return &InvalidValidationError{Type: reflect.TypeOf(s)}
} }
@ -376,7 +378,7 @@ func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn Filt
val = val.Elem() val = val.Elem()
} }
if val.Kind() != reflect.Struct || val.Type() == timeType { if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)} return &InvalidValidationError{Type: reflect.TypeOf(s)}
} }
@ -424,7 +426,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
val = val.Elem() val = val.Elem()
} }
if val.Kind() != reflect.Struct || val.Type() == timeType { if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)} return &InvalidValidationError{Type: reflect.TypeOf(s)}
} }
@ -514,7 +516,7 @@ func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ..
val = val.Elem() val = val.Elem()
} }
if val.Kind() != reflect.Struct || val.Type() == timeType { if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)} return &InvalidValidationError{Type: reflect.TypeOf(s)}
} }

@ -4269,6 +4269,46 @@ func TestUUIDRFC4122Validation(t *testing.T) {
} }
} }
func TestULIDValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"", false},
{"01BX5ZZKBKACT-V9WEVGEMMVRZ", false},
{"01bx5zzkbkactav9wevgemmvrz", false},
{"a987Fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false},
{"01BX5ZZKBKACTAV9WEVGEMMVRZABC", false},
{"01BX5ZZKBKACTAV9WEVGEMMVRZABC", false},
{"0IBX5ZZKBKACTAV9WEVGEMMVRZ", false},
{"O1BX5ZZKBKACTAV9WEVGEMMVRZ", false},
{"01BX5ZZKBKACTAVLWEVGEMMVRZ", false},
{"01BX5ZZKBKACTAV9WEVGEMMVRZ", true},
}
validate := New()
for i, test := range tests {
errs := validate.Var(test.param, "ulid")
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d ULID failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d ULID failed Error: %s", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "ulid" {
t.Fatalf("Index: %d ULID failed Error: %s", i, errs)
}
}
}
}
}
func TestISBNValidation(t *testing.T) { func TestISBNValidation(t *testing.T) {
tests := []struct { tests := []struct {
param string param string
@ -4981,6 +5021,28 @@ func TestIsEqFieldValidation(t *testing.T) {
Equal(t, errs, nil) Equal(t, errs, nil)
} }
func TestIsEqFieldValidationWithAliasTime(t *testing.T) {
var errs error
validate := New()
type CustomTime time.Time
type Test struct {
Start CustomTime `validate:"eqfield=End"`
End *time.Time
}
now := time.Now().UTC()
sv := &Test{
Start: CustomTime(now),
End: &now,
}
errs = validate.Struct(sv)
Equal(t, errs, nil)
}
func TestIsEqValidation(t *testing.T) { func TestIsEqValidation(t *testing.T) {
var errs error var errs error
validate := New() validate := New()
@ -9111,6 +9173,7 @@ func TestHostnameRFC1123Validation(t *testing.T) {
{"test.example24.com.", false}, {"test.example24.com.", false},
{"test24.example24.com.", false}, {"test24.example24.com.", false},
{"example.", false}, {"example.", false},
{"test_example", false},
{"192.168.0.1", true}, {"192.168.0.1", true},
{"email@example.com", false}, {"email@example.com", false},
{"2001:cdba:0000:0000:0000:0000:3257:9652", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652", false},
@ -9159,6 +9222,7 @@ func TestHostnameRFC1123AliasValidation(t *testing.T) {
{"test.example24.com.", false}, {"test.example24.com.", false},
{"test24.example24.com.", false}, {"test24.example24.com.", false},
{"example.", false}, {"example.", false},
{"test_example", false},
{"192.168.0.1", true}, {"192.168.0.1", true},
{"email@example.com", false}, {"email@example.com", false},
{"2001:cdba:0000:0000:0000:0000:3257:9652", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652", false},
@ -10635,6 +10699,145 @@ func TestRequiredWithoutAll(t *testing.T) {
AssertError(t, errs, "Field2", "Field2", "Field2", "Field2", "required_without_all") AssertError(t, errs, "Field2", "Field2", "Field2", "Field2", "required_without_all")
} }
func TestExcludedIf(t *testing.T) {
validate := New()
type Inner struct {
Field *string
}
test1 := struct {
FieldE string `validate:"omitempty" json:"field_e"`
FieldER *string `validate:"excluded_if=FieldE test" json:"field_er"`
}{
FieldE: "test",
}
errs := validate.Struct(test1)
Equal(t, errs, nil)
test2 := struct {
FieldE string `validate:"omitempty" json:"field_e"`
FieldER string `validate:"excluded_if=FieldE test" json:"field_er"`
}{
FieldE: "notest",
}
errs = validate.Struct(test2)
NotEqual(t, errs, nil)
ve := errs.(ValidationErrors)
Equal(t, len(ve), 1)
AssertError(t, errs, "FieldER", "FieldER", "FieldER", "FieldER", "excluded_if")
shouldError := "shouldError"
test3 := struct {
Inner *Inner
FieldE string `validate:"omitempty" json:"field_e"`
Field1 int `validate:"excluded_if=Inner.Field test" json:"field_1"`
}{
Inner: &Inner{Field: &shouldError},
}
errs = validate.Struct(test3)
NotEqual(t, errs, nil)
ve = errs.(ValidationErrors)
Equal(t, len(ve), 1)
AssertError(t, errs, "Field1", "Field1", "Field1", "Field1", "excluded_if")
shouldPass := "test"
test4 := struct {
Inner *Inner
FieldE string `validate:"omitempty" json:"field_e"`
Field1 int `validate:"excluded_if=Inner.Field test" json:"field_1"`
}{
Inner: &Inner{Field: &shouldPass},
}
errs = validate.Struct(test4)
Equal(t, errs, nil)
// Checks number of params in struct tag is correct
defer func() {
if r := recover(); r == nil {
t.Errorf("panicTest should have panicked!")
}
}()
fieldVal := "panicTest"
panicTest := struct {
Inner *Inner
Field1 string `validate:"excluded_if=Inner.Field" json:"field_1"`
}{
Inner: &Inner{Field: &fieldVal},
}
_ = validate.Struct(panicTest)
}
func TestExcludedUnless(t *testing.T) {
validate := New()
type Inner struct {
Field *string
}
fieldVal := "test"
test := struct {
FieldE string `validate:"omitempty" json:"field_e"`
FieldER string `validate:"excluded_unless=FieldE test" json:"field_er"`
}{
FieldE: "notest",
FieldER: "filled",
}
errs := validate.Struct(test)
Equal(t, errs, nil)
test2 := struct {
FieldE string `validate:"omitempty" json:"field_e"`
FieldER string `validate:"excluded_unless=FieldE test" json:"field_er"`
}{
FieldE: "test",
FieldER: "filled",
}
errs = validate.Struct(test2)
NotEqual(t, errs, nil)
ve := errs.(ValidationErrors)
Equal(t, len(ve), 1)
AssertError(t, errs, "FieldER", "FieldER", "FieldER", "FieldER", "excluded_unless")
shouldError := "test"
test3 := struct {
Inner *Inner
Field1 string `validate:"excluded_unless=Inner.Field test" json:"field_1"`
}{
Inner: &Inner{Field: &shouldError},
Field1: "filled",
}
errs = validate.Struct(test3)
NotEqual(t, errs, nil)
ve = errs.(ValidationErrors)
Equal(t, len(ve), 1)
AssertError(t, errs, "Field1", "Field1", "Field1", "Field1", "excluded_unless")
shouldPass := "shouldPass"
test4 := struct {
Inner *Inner
FieldE string `validate:"omitempty" json:"field_e"`
Field1 string `validate:"excluded_unless=Inner.Field test" json:"field_1"`
}{
Inner: &Inner{Field: &shouldPass},
Field1: "filled",
}
errs = validate.Struct(test4)
Equal(t, errs, nil)
// Checks number of params in struct tag is correct
defer func() {
if r := recover(); r == nil {
t.Errorf("panicTest should have panicked!")
}
}()
panicTest := struct {
Inner *Inner
Field1 string `validate:"excluded_unless=Inner.Field" json:"field_1"`
}{
Inner: &Inner{Field: &fieldVal},
}
_ = validate.Struct(panicTest)
}
func TestLookup(t *testing.T) { func TestLookup(t *testing.T) {
type Lookup struct { type Lookup struct {
FieldA *string `json:"fieldA,omitempty" validate:"required_without=FieldB"` FieldA *string `json:"fieldA,omitempty" validate:"required_without=FieldB"`
@ -11053,12 +11256,15 @@ func TestIsIso3166Alpha3Validation(t *testing.T) {
func TestIsIso3166AlphaNumericValidation(t *testing.T) { func TestIsIso3166AlphaNumericValidation(t *testing.T) {
tests := []struct { tests := []struct {
value int value interface{}
expected bool expected bool
}{ }{
{248, true}, {248, true},
{"248", true},
{0, false}, {0, false},
{1, false}, {1, false},
{"1", false},
{"invalid_int", false},
} }
validate := New() validate := New()
@ -11079,8 +11285,41 @@ func TestIsIso3166AlphaNumericValidation(t *testing.T) {
} }
PanicMatches(t, func() { PanicMatches(t, func() {
_ = validate.Var("1", "iso3166_1_alpha_numeric") _ = validate.Var([]string{"1"}, "iso3166_1_alpha_numeric")
}, "Bad field type string") }, "Bad field type []string")
}
func TestCountryCodeValidation(t *testing.T) {
tests := []struct {
value interface{}
expected bool
}{
{248, true},
{0, false},
{1, false},
{"POL", true},
{"NO", true},
{"248", true},
{"1", false},
{"0", false},
}
validate := New()
for i, test := range tests {
errs := validate.Var(test.value, "country_code")
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d country_code failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d country_code failed Error: %s", i, errs)
}
}
}
} }
func TestIsIso4217Validation(t *testing.T) { func TestIsIso4217Validation(t *testing.T) {
@ -11307,6 +11546,123 @@ func TestBicIsoFormatValidation(t *testing.T) {
} }
} }
func TestSemverFormatValidation(t *testing.T) {
tests := []struct {
value string `validate:"semver"`
tag string
expected bool
}{
{"1.2.3", "semver", true},
{"10.20.30", "semver", true},
{"1.1.2-prerelease+meta", "semver", true},
{"1.1.2+meta", "semver", true},
{"1.1.2+meta-valid", "semver", true},
{"1.0.0-alpha", "semver", true},
{"1.0.0-alpha.1", "semver", true},
{"1.0.0-alpha.beta", "semver", true},
{"1.0.0-alpha.beta.1", "semver", true},
{"1.0.0-alpha0.valid", "semver", true},
{"1.0.0-alpha.0valid", "semver", true},
{"1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay", "semver", true},
{"1.0.0-rc.1+build.1", "semver", true},
{"1.0.0-rc.1+build.123", "semver", true},
{"1.2.3-beta", "semver", true},
{"1.2.3-DEV-SNAPSHOT", "semver", true},
{"1.2.3-SNAPSHOT-123", "semver", true},
{"2.0.0+build.1848", "semver", true},
{"2.0.1-alpha.1227", "semver", true},
{"1.0.0-alpha+beta", "semver", true},
{"1.2.3----RC-SNAPSHOT.12.9.1--.12+788", "semver", true},
{"1.2.3----R-S.12.9.1--.12+meta", "semver", true},
{"1.2.3----RC-SNAPSHOT.12.9.1--.12", "semver", true},
{"1.0.0+0.build.1-rc.10000aaa-kk-0.1", "semver", true},
{"99999999999999999999999.999999999999999999.99999999999999999", "semver", true},
{"1.0.0-0A.is.legal", "semver", true},
{"1", "semver", false},
{"1.2", "semver", false},
{"1.2.3-0123", "semver", false},
{"1.2.3-0123.0123", "semver", false},
{"1.1.2+.123", "semver", false},
{"+invalid", "semver", false},
{"-invalid", "semver", false},
{"-invalid+invalid", "semver", false},
{"alpha", "semver", false},
{"alpha.beta.1", "semver", false},
{"alpha.1", "semver", false},
{"1.0.0-alpha_beta", "semver", false},
{"1.0.0-alpha_beta", "semver", false},
{"1.0.0-alpha...1", "semver", false},
{"01.1.1", "semver", false},
{"1.01.1", "semver", false},
{"1.1.01", "semver", false},
{"1.2", "semver", false},
{"1.2.Dev", "semver", false},
{"1.2.3.Dev", "semver", false},
{"1.2-SNAPSHOT", "semver", false},
}
validate := New()
for i, test := range tests {
errs := validate.Var(test.value, test.tag)
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d semver failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d semver failed Error: %s", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "semver" {
t.Fatalf("Index: %d semver failed Error: %s", i, errs)
}
}
}
}
}
func TestRFC1035LabelFormatValidation(t *testing.T) {
tests := []struct {
value string `validate:"dns_rfc1035_label"`
tag string
expected bool
}{
{"abc", "dns_rfc1035_label", true},
{"abc-", "dns_rfc1035_label", false},
{"abc-123", "dns_rfc1035_label", true},
{"ABC", "dns_rfc1035_label", false},
{"ABC-123", "dns_rfc1035_label", false},
{"abc-abc", "dns_rfc1035_label", true},
{"ABC-ABC", "dns_rfc1035_label", false},
{"123-abc", "dns_rfc1035_label", false},
{"", "dns_rfc1035_label", false},
}
validate := New()
for i, test := range tests {
errs := validate.Var(test.value, test.tag)
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d dns_rfc1035_label failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d dns_rfc1035_label failed Error: %s", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "dns_rfc1035_label" {
t.Fatalf("Index: %d dns_rfc1035_label failed Error: %s", i, errs)
}
}
}
}
}
func TestPostCodeByIso3166Alpha2(t *testing.T) { func TestPostCodeByIso3166Alpha2(t *testing.T) {
tests := map[string][]struct { tests := map[string][]struct {
value string value string
@ -11418,3 +11774,65 @@ func TestPostCodeByIso3166Alpha2Field_InvalidKind(t *testing.T) {
_ = New().Struct(test{"ABC", 123, false}) _ = New().Struct(test{"ABC", 123, false})
t.Errorf("Didn't panic as expected") t.Errorf("Didn't panic as expected")
} }
func TestCreditCardFormatValidation(t *testing.T) {
tests := []struct {
value string `validate:"credit_card"`
tag string
expected bool
}{
{"586824160825533338", "credit_card", true},
{"586824160825533328", "credit_card", false},
{"4624748233249780", "credit_card", true},
{"4624748233349780", "credit_card", false},
{"378282246310005", "credit_card", true},
{"378282146310005", "credit_card", false},
{"4624 7482 3324 9780", "credit_card", true},
{"4624 7482 3324 9780", "credit_card", false},
}
validate := New()
for i, test := range tests {
errs := validate.Var(test.value, test.tag)
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d credit_card failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d credit_card failed Error: %s", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "credit_card" {
t.Fatalf("Index: %d credit_card failed Error: %s", i, errs)
}
}
}
}
}
func TestMultiOrOperatorGroup(t *testing.T) {
tests := []struct {
Value int `validate:"eq=1|gte=5,eq=1|lt=7"`
expected bool
}{
{1, true}, {2, false}, {5, true}, {6, true}, {8, false},
}
validate := New()
for i, test := range tests {
errs := validate.Struct(test)
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d multi_group_of_OR_operators failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d multi_group_of_OR_operators should have errs", i)
}
}
}
}

Loading…
Cancel
Save