diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index acf873d..d1ca1e8 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -45,4 +45,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: v1.39 + version: v1.41.1 diff --git a/Makefile b/Makefile index 164e8bb..ec3455b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ GOCMD=GO111MODULE=on go linters-install: @golangci-lint --version >/dev/null 2>&1 || { \ echo "installing linting tools..."; \ - curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.39.0; \ + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.41.1; \ } lint: linters-install diff --git a/README.md b/README.md index 643cb90..f56cff1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Package validator ================= [![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.8.0-green.svg) +![Project status](https://img.shields.io/badge/version-10.9.0-green.svg) [![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) @@ -126,6 +126,7 @@ Baked-in Validations | alphanumunicode | Alphanumeric Unicode | | alphaunicode | Alpha Unicode | | ascii | ASCII | +| boolean | Boolean | | contains | Contains | | containsany | Contains Any | | containsrune | Contains Rune | @@ -169,6 +170,7 @@ Baked-in Validations | iso3166_1_alpha3 | Three-letter country code (ISO 3166-1 alpha-3) | | iso3166_1_alpha_numeric | Numeric country code (ISO 3166-1 numeric) | | iso3166_2 | Country subdivision code (ISO 3166-2) | +| iso4217 | Currency code (ISO 4217) | | json | JSON | | jwt | JSON Web Token (JWT) | | latitude | Latitude | diff --git a/baked_in.go b/baked_in.go index ce81457..f5fd239 100644 --- a/baked_in.go +++ b/baked_in.go @@ -107,6 +107,7 @@ var ( "alphanum": isAlphanum, "alphaunicode": isAlphaUnicode, "alphanumunicode": isAlphanumUnicode, + "boolean": isBoolean, "numeric": isNumeric, "number": isNumber, "hexadecimal": isHexadecimal, @@ -191,6 +192,8 @@ var ( "iso3166_1_alpha3": isIso3166Alpha3, "iso3166_1_alpha_numeric": isIso3166AlphaNumeric, "iso3166_2": isIso31662, + "iso4217": isIso4217, + "iso4217_numeric": isIso4217Numeric, "bcp47_language_tag": isBCP47LanguageTag, "postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2, "postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field, @@ -1438,6 +1441,12 @@ func isAlphaUnicode(fl FieldLevel) bool { return alphaUnicodeRegex.MatchString(fl.Field().String()) } +// isBoolean is the validation function for validating if the current field's value can be safely converted to a boolean. +func isBoolean(fl FieldLevel) bool { + _, err := strconv.ParseBool(fl.Field().String()) + return err == nil +} + // isDefault is the opposite of required aka hasValue func isDefault(fl FieldLevel) bool { return !hasValue(fl) @@ -2358,6 +2367,28 @@ func isIso31662(fl FieldLevel) bool { return iso3166_2[val] } +// isIso4217 is the validation function for validating if the current field's value is a valid iso4217 currency code. +func isIso4217(fl FieldLevel) bool { + val := fl.Field().String() + return iso4217[val] +} + +// isIso4217Numeric is the validation function for validating if the current field's value is a valid iso4217 numeric currency code. +func isIso4217Numeric(fl FieldLevel) bool { + field := fl.Field() + + var code int + switch field.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + code = int(field.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + code = int(field.Uint()) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + return iso4217_numeric[code] +} + // isBCP47LanguageTag is the validation function for validating if the current field's value is a valid BCP 47 language tag, as parsed by language.Parse func isBCP47LanguageTag(fl FieldLevel) bool { field := fl.Field() diff --git a/currency_codes.go b/currency_codes.go new file mode 100644 index 0000000..a5cd9b1 --- /dev/null +++ b/currency_codes.go @@ -0,0 +1,79 @@ +package validator + +var iso4217 = map[string]bool{ + "AFN": true, "EUR": true, "ALL": true, "DZD": true, "USD": true, + "AOA": true, "XCD": true, "ARS": true, "AMD": true, "AWG": true, + "AUD": true, "AZN": true, "BSD": true, "BHD": true, "BDT": true, + "BBD": true, "BYN": true, "BZD": true, "XOF": true, "BMD": true, + "INR": true, "BTN": true, "BOB": true, "BOV": true, "BAM": true, + "BWP": true, "NOK": true, "BRL": true, "BND": true, "BGN": true, + "BIF": true, "CVE": true, "KHR": true, "XAF": true, "CAD": true, + "KYD": true, "CLP": true, "CLF": true, "CNY": true, "COP": true, + "COU": true, "KMF": true, "CDF": true, "NZD": true, "CRC": true, + "HRK": true, "CUP": true, "CUC": true, "ANG": true, "CZK": true, + "DKK": true, "DJF": true, "DOP": true, "EGP": true, "SVC": true, + "ERN": true, "SZL": true, "ETB": true, "FKP": true, "FJD": true, + "XPF": true, "GMD": true, "GEL": true, "GHS": true, "GIP": true, + "GTQ": true, "GBP": true, "GNF": true, "GYD": true, "HTG": true, + "HNL": true, "HKD": true, "HUF": true, "ISK": true, "IDR": true, + "XDR": true, "IRR": true, "IQD": true, "ILS": true, "JMD": true, + "JPY": true, "JOD": true, "KZT": true, "KES": true, "KPW": true, + "KRW": true, "KWD": true, "KGS": true, "LAK": true, "LBP": true, + "LSL": true, "ZAR": true, "LRD": true, "LYD": true, "CHF": true, + "MOP": true, "MKD": true, "MGA": true, "MWK": true, "MYR": true, + "MVR": true, "MRU": true, "MUR": true, "XUA": true, "MXN": true, + "MXV": true, "MDL": true, "MNT": true, "MAD": true, "MZN": true, + "MMK": true, "NAD": true, "NPR": true, "NIO": true, "NGN": true, + "OMR": true, "PKR": true, "PAB": true, "PGK": true, "PYG": true, + "PEN": true, "PHP": true, "PLN": true, "QAR": true, "RON": true, + "RUB": true, "RWF": true, "SHP": true, "WST": true, "STN": true, + "SAR": true, "RSD": true, "SCR": true, "SLL": true, "SGD": true, + "XSU": true, "SBD": true, "SOS": true, "SSP": true, "LKR": true, + "SDG": true, "SRD": true, "SEK": true, "CHE": true, "CHW": true, + "SYP": true, "TWD": true, "TJS": true, "TZS": true, "THB": true, + "TOP": true, "TTD": true, "TND": true, "TRY": true, "TMT": true, + "UGX": true, "UAH": true, "AED": true, "USN": true, "UYU": true, + "UYI": true, "UYW": true, "UZS": true, "VUV": true, "VES": true, + "VND": true, "YER": true, "ZMW": true, "ZWL": true, "XBA": true, + "XBB": true, "XBC": true, "XBD": true, "XTS": true, "XXX": true, + "XAU": true, "XPD": true, "XPT": true, "XAG": true, +} + +var iso4217_numeric = map[int]bool{ + 8: true, 12: true, 32: true, 36: true, 44: true, + 48: true, 50: true, 51: true, 52: true, 60: true, + 64: true, 68: true, 72: true, 84: true, 90: true, + 96: true, 104: true, 108: true, 116: true, 124: true, + 132: true, 136: true, 144: true, 152: true, 156: true, + 170: true, 174: true, 188: true, 191: true, 192: true, + 203: true, 208: true, 214: true, 222: true, 230: true, + 232: true, 238: true, 242: true, 262: true, 270: true, + 292: true, 320: true, 324: true, 328: true, 332: true, + 340: true, 344: true, 348: true, 352: true, 356: true, + 360: true, 364: true, 368: true, 376: true, 388: true, + 392: true, 398: true, 400: true, 404: true, 408: true, + 410: true, 414: true, 417: true, 418: true, 422: true, + 426: true, 430: true, 434: true, 446: true, 454: true, + 458: true, 462: true, 480: true, 484: true, 496: true, + 498: true, 504: true, 512: true, 516: true, 524: true, + 532: true, 533: true, 548: true, 554: true, 558: true, + 566: true, 578: true, 586: true, 590: true, 598: true, + 600: true, 604: true, 608: true, 634: true, 643: true, + 646: true, 654: true, 682: true, 690: true, 694: true, + 702: true, 704: true, 706: true, 710: true, 728: true, + 748: true, 752: true, 756: true, 760: true, 764: true, + 776: true, 780: true, 784: true, 788: true, 800: true, + 807: true, 818: true, 826: true, 834: true, 840: true, + 858: true, 860: true, 882: true, 886: true, 901: true, + 927: true, 928: true, 929: true, 930: true, 931: true, + 932: true, 933: true, 934: true, 936: true, 938: true, + 940: true, 941: true, 943: true, 944: true, 946: true, + 947: true, 948: true, 949: true, 950: true, 951: true, + 952: true, 953: true, 955: true, 956: true, 957: true, + 958: true, 959: true, 960: true, 961: true, 962: true, + 963: true, 964: true, 965: true, 967: true, 968: true, + 969: true, 970: true, 971: true, 972: true, 973: true, + 975: true, 976: true, 977: true, 978: true, 979: true, + 980: true, 981: true, 984: true, 985: true, 986: true, + 990: true, 994: true, 997: true, 999: true, +} diff --git a/doc.go b/doc.go index cf1376b..8c25847 100644 --- a/doc.go +++ b/doc.go @@ -7,6 +7,14 @@ and has the ability to dive into arrays and maps of any type. see more examples https://github.com/go-playground/validator/tree/master/_examples +Singleton + +Validator is designed to be thread-safe and used as a singleton instance. +It caches information about your struct and validations, +in essence only parsing your validation tags once per struct type. +Using multiple instances neglects the benefit of caching. +The not thread-safe functions are explicitly marked as such in the documentation. + Validation Functions Return Type error Doing things this way is actually the way the standard library does, see the @@ -726,6 +734,12 @@ This validates that a string value contains unicode alphanumeric characters only Usage: alphanumunicode +Boolean + +This validates that a string value can successfully be parsed into a boolean with strconv.ParseBool + + Usage: boolean + Number This validates that a string value contains number values only. diff --git a/go.mod b/go.mod index 786b8ca..ddecff6 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,17 @@ module github.com/go-playground/validator/v10 go 1.13 require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-playground/assert/v2 v2.0.1 - github.com/go-playground/locales v0.13.0 - github.com/go-playground/universal-translator v0.17.0 + github.com/go-playground/locales v0.14.0 + github.com/go-playground/universal-translator v0.18.0 + github.com/kr/pretty v0.3.0 // indirect github.com/leodido/go-urn v1.2.1 + github.com/rogpeppe/go-internal v1.8.0 // indirect + github.com/stretchr/testify v1.7.0 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect golang.org/x/text v0.3.6 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index e0c6781..5750093 100644 --- a/go.sum +++ b/go.sum @@ -1,32 +1,50 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/translations/fr/fr_test.go b/translations/fr/fr_test.go index a3f864c..c2c88d0 100644 --- a/translations/fr/fr_test.go +++ b/translations/fr/fr_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" + . "github.com/go-playground/assert/v2" french "github.com/go-playground/locales/fr" ut "github.com/go-playground/universal-translator" - . "github.com/go-playground/assert/v2" "github.com/go-playground/validator/v10" ) @@ -536,7 +536,7 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.MaxNumber", - expected: "MaxNumber doit être égal à 1 113,00 ou moins", + expected: "MaxNumber doit être égal à 1 113,00 ou moins", }, { ns: "Test.MaxMultiple", @@ -548,7 +548,7 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.MinNumber", - expected: "MinNumber doit être égal à 1 113,00 ou plus", + expected: "MinNumber doit être égal à 1 113,00 ou plus", }, { ns: "Test.MinMultiple", @@ -560,7 +560,7 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.LenNumber", - expected: "LenNumber doit être égal à 1 113,00", + expected: "LenNumber doit être égal à 1 113,00", }, { ns: "Test.LenMultiple", diff --git a/translations/ru/ru.go b/translations/ru/ru.go index 6f9d41b..7a12bd8 100644 --- a/translations/ru/ru.go +++ b/translations/ru/ru.go @@ -41,6 +41,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("len-string-character", "{0} символа", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("len-string-character", "{0} символов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("len-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil { return } @@ -52,10 +60,19 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er if err = ut.Add("len-items", "{0} должен содержать {1}", false); err != nil { return } + if err = ut.AddCardinal("len-items-item", "{0} элемент", locales.PluralRuleOne, false); err != nil { return } + if err = ut.AddCardinal("len-items-item", "{0} элемента", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("len-items-item", "{0} элементов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("len-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil { return } @@ -132,6 +149,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("min-string-character", "{0} символа", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("min-string-character", "{0} символов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("min-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil { return } @@ -147,6 +172,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("min-items-item", "{0} элемента", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("min-items-item", "{0} элементов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("min-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil { return } @@ -223,6 +256,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("max-string-character", "{0} символа", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("max-string-character", "{0} символов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("max-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil { return } @@ -238,6 +279,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("max-items-item", "{0} элемента", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("max-items-item", "{0} элементов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("max-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil { return } @@ -344,6 +393,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("lt-string-character", "{0} символов", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("lt-string-character", "{0} символов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("lt-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil { return } @@ -360,6 +417,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("lt-items-item", "{0} элементов", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("lt-items-item", "{0} элементов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("lt-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil { return } @@ -465,6 +530,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("lte-string-character", "{0} символа", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("lte-string-character", "{0} символов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("lte-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil { return } @@ -481,6 +554,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("lte-items-item", "{0} элемента", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("lte-items-item", "{0} элементов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("lte-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil { return } @@ -585,6 +666,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("gt-string-character", "{0} символов", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("gt-string-character", "{0} символов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("gt-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil { return } @@ -601,6 +690,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("gt-items-item", "{0} элементов", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("gt-items-item", "{0} элементов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("gt-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil { return } @@ -705,6 +802,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("gte-string-character", "{0} символа", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("gte-string-character", "{0} символов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("gte-string-character", "{0} символы", locales.PluralRuleOther, false); err != nil { return } @@ -721,6 +826,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } + if err = ut.AddCardinal("gte-items-item", "{0} элемента", locales.PluralRuleFew, false); err != nil { + return + } + + if err = ut.AddCardinal("gte-items-item", "{0} элементов", locales.PluralRuleMany, false); err != nil { + return + } + if err = ut.AddCardinal("gte-items-item", "{0} элементы", locales.PluralRuleOther, false); err != nil { return } diff --git a/translations/ru/ru_test.go b/translations/ru/ru_test.go index aa5beb7..e1a1069 100644 --- a/translations/ru/ru_test.go +++ b/translations/ru/ru_test.go @@ -7,7 +7,7 @@ import ( "time" . "github.com/go-playground/assert/v2" - russian "github.com/go-playground/locales/en" + russian "github.com/go-playground/locales/ru" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" ) @@ -33,116 +33,136 @@ func TestTranslations(t *testing.T) { } 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"` - 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"` + 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"` + LenMultipleSecond []string `validate:"len=2"` + MinString string `validate:"min=1"` + MinStringMultiple string `validate:"min=2"` + MinStringMultipleSecond string `validate:"min=7"` + MinNumber float64 `validate:"min=1113.00"` + MinMultiple []string `validate:"min=7"` + MinMultipleSecond []string `validate:"min=2"` + MaxString string `validate:"max=3"` + MaxStringSecond string `validate:"max=7"` + MaxNumber float64 `validate:"max=1113.00"` + MaxMultiple []string `validate:"max=7"` + MaxMultipleSecond []string `validate:"max=2"` + 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"` + LtStringSecond string `validate:"lt=7"` + LtNumber float64 `validate:"lt=5.56"` + LtMultiple []string `validate:"lt=2"` + LtMultipleSecond []string `validate:"lt=7"` + LtTime time.Time `validate:"lt"` + LteString string `validate:"lte=3"` + LteStringSecond string `validate:"lte=7"` + LteNumber float64 `validate:"lte=5.56"` + LteMultiple []string `validate:"lte=2"` + LteMultipleSecond []string `validate:"lte=7"` + LteTime time.Time `validate:"lte"` + GtString string `validate:"gt=3"` + GtStringSecond string `validate:"gt=7"` + GtNumber float64 `validate:"gt=5.56"` + GtMultiple []string `validate:"gt=2"` + GtMultipleSecond []string `validate:"gt=7"` + GtTime time.Time `validate:"gt"` + GteString string `validate:"gte=3"` + GteStringSecond string `validate:"gte=7"` + GteNumber float64 `validate:"gte=5.56"` + GteMultiple []string `validate:"gte=2"` + GteMultipleSecond []string `validate:"gte=7"` + 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"` + 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"` + StrPtrMinLenSecond *string `validate:"min=2"` + StrPtrMaxLen *string `validate:"max=1"` + StrPtrLen *string `validate:"len=2"` + StrPtrLenSecond *string `validate:"len=7"` + StrPtrLt *string `validate:"lt=1"` + StrPtrLte *string `validate:"lte=1"` + StrPtrLteMultiple *string `validate:"lte=2"` + StrPtrLteMultipleSecond *string `validate:"lte=7"` + StrPtrGt *string `validate:"gt=10"` + StrPtrGte *string `validate:"gte=10"` + StrPtrGtSecond *string `validate:"gt=2"` + StrPtrGteSecond *string `validate:"gte=2"` + 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"` } var test Test @@ -152,17 +172,23 @@ func TestTranslations(t *testing.T) { test.Inner.GteCSFieldString = "1234" test.MaxString = "1234" + test.MaxStringSecond = "12345678" test.MaxNumber = 2000 test.MaxMultiple = make([]string, 9) + test.MaxMultipleSecond = make([]string, 3) test.LtString = "1234" + test.LtStringSecond = "12345678" test.LtNumber = 6 test.LtMultiple = make([]string, 3) + test.LtMultipleSecond = make([]string, 8) test.LtTime = time.Now().Add(time.Hour * 24) test.LteString = "1234" + test.LteStringSecond = "12345678" test.LteNumber = 6 test.LteMultiple = make([]string, 3) + test.LteMultipleSecond = make([]string, 8) test.LteTime = time.Now().Add(time.Hour * 24) test.LtFieldString = "12345" @@ -452,15 +478,23 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.GteString", - expected: "GteString должен содержать минимум 3 символы", + expected: "GteString должен содержать минимум 3 символа", + }, + { + ns: "Test.GteStringSecond", + expected: "GteStringSecond должен содержать минимум 7 символов", }, { ns: "Test.GteNumber", - expected: "GteNumber должен быть больше или равно 5.56", + expected: "GteNumber должен быть больше или равно 5,56", }, { ns: "Test.GteMultiple", - expected: "GteMultiple должен содержать минимум 2 элементы", + expected: "GteMultiple должен содержать минимум 2 элемента", + }, + { + ns: "Test.GteMultipleSecond", + expected: "GteMultipleSecond должен содержать минимум 7 элементов", }, { ns: "Test.GteTime", @@ -468,15 +502,23 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.GtString", - expected: "GtString должен быть длиннее 3 символы", + expected: "GtString должен быть длиннее 3 символов", + }, + { + ns: "Test.GtStringSecond", + expected: "GtStringSecond должен быть длиннее 7 символов", }, { ns: "Test.GtNumber", - expected: "GtNumber должен быть больше 5.56", + expected: "GtNumber должен быть больше 5,56", }, { ns: "Test.GtMultiple", - expected: "GtMultiple должен содержать более 2 элементы", + expected: "GtMultiple должен содержать более 2 элементов", + }, + { + ns: "Test.GtMultipleSecond", + expected: "GtMultipleSecond должен содержать более 7 элементов", }, { ns: "Test.GtTime", @@ -484,15 +526,23 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.LteString", - expected: "LteString должен содержать максимум 3 символы", + expected: "LteString должен содержать максимум 3 символа", + }, + { + ns: "Test.LteStringSecond", + expected: "LteStringSecond должен содержать максимум 7 символов", }, { ns: "Test.LteNumber", - expected: "LteNumber должен быть менее или равен 5.56", + expected: "LteNumber должен быть менее или равен 5,56", }, { ns: "Test.LteMultiple", - expected: "LteMultiple должен содержать максимум 2 элементы", + expected: "LteMultiple должен содержать максимум 2 элемента", + }, + { + ns: "Test.LteMultipleSecond", + expected: "LteMultipleSecond должен содержать максимум 7 элементов", }, { ns: "Test.LteTime", @@ -500,15 +550,23 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.LtString", - expected: "LtString должен иметь менее 3 символы", + expected: "LtString должен иметь менее 3 символов", + }, + { + ns: "Test.LtStringSecond", + expected: "LtStringSecond должен иметь менее 7 символов", }, { ns: "Test.LtNumber", - expected: "LtNumber должен быть менее 5.56", + expected: "LtNumber должен быть менее 5,56", }, { ns: "Test.LtMultiple", - expected: "LtMultiple должен содержать менее 2 элементы", + expected: "LtMultiple должен содержать менее 2 элементов", + }, + { + ns: "Test.LtMultipleSecond", + expected: "LtMultipleSecond должен содержать менее 7 элементов", }, { ns: "Test.LtTime", @@ -540,27 +598,47 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.MaxString", - expected: "MaxString должен содержать максимум 3 символы", + expected: "MaxString должен содержать максимум 3 символа", + }, + { + ns: "Test.MaxStringSecond", + expected: "MaxStringSecond должен содержать максимум 7 символов", }, { ns: "Test.MaxNumber", - expected: "MaxNumber должен быть меньше или равно 1,113.00", + expected: "MaxNumber должен быть меньше или равно 1 113,00", }, { ns: "Test.MaxMultiple", - expected: "MaxMultiple должен содержать максимум 7 элементы", + expected: "MaxMultiple должен содержать максимум 7 элементов", + }, + { + ns: "Test.MaxMultipleSecond", + expected: "MaxMultipleSecond должен содержать максимум 2 элемента", }, { ns: "Test.MinString", expected: "MinString должен содержать минимум 1 символ", }, + { + ns: "Test.MinStringMultiple", + expected: "MinStringMultiple должен содержать минимум 2 символа", + }, + { + ns: "Test.MinStringMultipleSecond", + expected: "MinStringMultipleSecond должен содержать минимум 7 символов", + }, { ns: "Test.MinNumber", - expected: "MinNumber должен быть больше или равно 1,113.00", + expected: "MinNumber должен быть больше или равно 1 113,00", }, { ns: "Test.MinMultiple", - expected: "MinMultiple должен содержать минимум 7 элементы", + expected: "MinMultiple должен содержать минимум 7 элементов", + }, + { + ns: "Test.MinMultipleSecond", + expected: "MinMultipleSecond должен содержать минимум 2 элемента", }, { ns: "Test.LenString", @@ -568,11 +646,15 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.LenNumber", - expected: "LenNumber должен быть равен 1,113.00", + expected: "LenNumber должен быть равен 1 113,00", }, { ns: "Test.LenMultiple", - expected: "LenMultiple должен содержать 7 элементы", + expected: "LenMultiple должен содержать 7 элементов", + }, + { + ns: "Test.LenMultipleSecond", + expected: "LenMultipleSecond должен содержать 2 элемента", }, { ns: "Test.RequiredString", @@ -588,7 +670,11 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.StrPtrMinLen", - expected: "StrPtrMinLen должен содержать минимум 10 символы", + expected: "StrPtrMinLen должен содержать минимум 10 символов", + }, + { + ns: "Test.StrPtrMinLenSecond", + expected: "StrPtrMinLenSecond должен содержать минимум 2 символа", }, { ns: "Test.StrPtrMaxLen", @@ -596,7 +682,11 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.StrPtrLen", - expected: "StrPtrLen должен быть длиной в 2 символы", + expected: "StrPtrLen должен быть длиной в 2 символа", + }, + { + ns: "Test.StrPtrLenSecond", + expected: "StrPtrLenSecond должен быть длиной в 7 символов", }, { ns: "Test.StrPtrLt", @@ -606,13 +696,29 @@ func TestTranslations(t *testing.T) { ns: "Test.StrPtrLte", expected: "StrPtrLte должен содержать максимум 1 символ", }, + { + ns: "Test.StrPtrLteMultiple", + expected: "StrPtrLteMultiple должен содержать максимум 2 символа", + }, + { + ns: "Test.StrPtrLteMultipleSecond", + expected: "StrPtrLteMultipleSecond должен содержать максимум 7 символов", + }, { ns: "Test.StrPtrGt", - expected: "StrPtrGt должен быть длиннее 10 символы", + expected: "StrPtrGt должен быть длиннее 10 символов", + }, + { + ns: "Test.StrPtrGtSecond", + expected: "StrPtrGtSecond должен быть длиннее 2 символов", }, { ns: "Test.StrPtrGte", - expected: "StrPtrGte должен содержать минимум 10 символы", + expected: "StrPtrGte должен содержать минимум 10 символов", + }, + { + ns: "Test.StrPtrGteSecond", + expected: "StrPtrGteSecond должен содержать минимум 2 символа", }, { ns: "Test.OneOfString", diff --git a/validator_instance.go b/validator_instance.go index 96e777f..973964f 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -89,6 +89,10 @@ type Validate struct { } // New returns a new instance of 'validate' with sane defaults. +// Validate is designed to be thread-safe and used as a singleton instance. +// It caches information about your struct and validations, +// in essence only parsing your validation tags once per struct type. +// Using multiple instances neglects the benefit of caching. func New() *Validate { tc := new(tagCache) diff --git a/validator_test.go b/validator_test.go index 39ef250..5f146ea 100644 --- a/validator_test.go +++ b/validator_test.go @@ -71,6 +71,7 @@ type TestString struct { Gt string `validate:"gt=10"` Gte string `validate:"gte=10"` OmitEmpty string `validate:"omitempty,min=1,max=10"` + Boolean string `validate:"boolean"` Sub *SubTest SubIgnore *SubTest `validate:"-"` Anonymous struct { @@ -7943,6 +7944,7 @@ func TestStructStringValidation(t *testing.T) { Lte: "0123456789", Gt: "01234567890", Gte: "0123456789", + Boolean: "true", OmitEmpty: "", Sub: &SubTest{ Test: "1", @@ -7974,6 +7976,7 @@ func TestStructStringValidation(t *testing.T) { Gt: "1", Gte: "1", OmitEmpty: "12345678901", + Boolean: "nope", Sub: &SubTest{ Test: "", }, @@ -7991,7 +7994,7 @@ func TestStructStringValidation(t *testing.T) { // Assert Top Level NotEqual(t, errs, nil) - Equal(t, len(errs.(ValidationErrors)), 13) + Equal(t, len(errs.(ValidationErrors)), 14) // Assert Fields AssertError(t, errs, "TestString.Required", "TestString.Required", "Required", "Required", "required") @@ -8004,6 +8007,7 @@ func TestStructStringValidation(t *testing.T) { AssertError(t, errs, "TestString.Gt", "TestString.Gt", "Gt", "Gt", "gt") AssertError(t, errs, "TestString.Gte", "TestString.Gte", "Gte", "Gte", "gte") AssertError(t, errs, "TestString.OmitEmpty", "TestString.OmitEmpty", "OmitEmpty", "OmitEmpty", "max") + AssertError(t, errs, "TestString.Boolean", "TestString.Boolean", "Boolean", "Boolean", "boolean") // Nested Struct Field Errs AssertError(t, errs, "TestString.Anonymous.A", "TestString.Anonymous.A", "A", "A", "required") @@ -11079,6 +11083,62 @@ func TestIsIso3166AlphaNumericValidation(t *testing.T) { }, "Bad field type string") } +func TestIsIso4217Validation(t *testing.T) { + tests := []struct { + value string `validate:"iso4217"` + expected bool + }{ + {"TRY", true}, + {"EUR", true}, + {"USA", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.value, "iso4217") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d iso4217 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d iso4217 failed Error: %s", i, errs) + } + } + } +} + +func TestIsIso4217NumericValidation(t *testing.T) { + tests := []struct { + value int `validate:"iso4217_numeric"` + expected bool + }{ + {8, true}, + {12, true}, + {13, false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.value, "iso4217_numeric") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d iso4217 failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d iso4217 failed Error: %s", i, errs) + } + } + } +} + func TestTimeZoneValidation(t *testing.T) { tests := []struct { value string `validate:"timezone"`