From 1e8c614c2a5449c8537d78a155c214d1dd50b030 Mon Sep 17 00:00:00 2001 From: Vaibhav Dighe <86607407+V-R-Dighe@users.noreply.github.com> Date: Fri, 16 Sep 2022 21:29:49 +0530 Subject: [PATCH 01/37] Fixed boolean validation to handle bool kind (#988) --- baked_in.go | 11 +++++-- validator_test.go | 81 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/baked_in.go b/baked_in.go index f2f0939..c9b1db4 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1484,10 +1484,15 @@ 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. +// isBoolean is the validation function for validating if the current field's value is a valid boolean value or can be safely converted to a boolean value. func isBoolean(fl FieldLevel) bool { - _, err := strconv.ParseBool(fl.Field().String()) - return err == nil + switch fl.Field().Kind() { + case reflect.Bool: + return true + default: + _, err := strconv.ParseBool(fl.Field().String()) + return err == nil + } } // isDefault is the opposite of required aka hasValue diff --git a/validator_test.go b/validator_test.go index 1ebdea5..7e314d6 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8302,6 +8302,43 @@ func TestNumeric(t *testing.T) { errs = validate.Var(i, "numeric") Equal(t, errs, nil) } +func TestBoolean(t *testing.T) { + validate := New() + + b := true + errs := validate.Var(b, "boolean") + Equal(t, errs, nil) + + b = false + errs = validate.Var(b, "boolean") + Equal(t, errs, nil) + + s := "true" + errs = validate.Var(s, "boolean") + Equal(t, errs, nil) + + s = "false" + errs = validate.Var(s, "boolean") + Equal(t, errs, nil) + + s = "0" + errs = validate.Var(s, "boolean") + Equal(t, errs, nil) + + s = "1" + errs = validate.Var(s, "boolean") + Equal(t, errs, nil) + + s = "xyz" + errs = validate.Var(s, "boolean") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "", "", "boolean") + + s = "1." + errs = validate.Var(s, "boolean") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "", "", "boolean") +} func TestAlphaNumeric(t *testing.T) { validate := New() @@ -12264,25 +12301,25 @@ func TestCreditCardFormatValidation(t *testing.T) { } 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) - } - } - } - } + 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) + } + } + } +} From c7e0172e0fd176bdc521afb5186818a7db6b77ac Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Fri, 16 Sep 2022 09:01:33 -0700 Subject: [PATCH 02/37] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b730b6..9d0a79e 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.11.0-green.svg) +![Project status](https://img.shields.io/badge/version-10.11.1-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) From f036dd475d11b2a5c32ce677c9a407dd7e77553a Mon Sep 17 00:00:00 2001 From: tomaisthorpe Date: Mon, 30 Jan 2023 04:32:10 +0000 Subject: [PATCH 03/37] update golang.org/x/text to resolve CVE-2022-32149 (#1015) --- go.mod | 3 +-- go.sum | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index e281ec0..c535cf8 100644 --- a/go.mod +++ b/go.mod @@ -12,8 +12,7 @@ require ( github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/stretchr/testify v1.7.0 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 - golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect - golang.org/x/text v0.3.7 + golang.org/x/text v0.3.8 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 a1c43c3..b56e67e 100644 --- a/go.sum +++ b/go.sum @@ -28,20 +28,40 @@ 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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 5aa5f93593151c525b9e618d87721040056f077a Mon Sep 17 00:00:00 2001 From: wjl110 <53851034+wjl110@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:32:48 +0800 Subject: [PATCH 04/37] update gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b to 3.0.0 (#1057) --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c535cf8..35bbac2 100644 --- a/go.mod +++ b/go.mod @@ -14,5 +14,5 @@ require ( golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/text v0.3.8 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 // indirect ) diff --git a/go.sum b/go.sum index b56e67e..1c1fb72 100644 --- a/go.sum +++ b/go.sum @@ -70,3 +70,5 @@ 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= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 3201fe42fe04a0f4ff45a1e8911c9e84daa5fb93 Mon Sep 17 00:00:00 2001 From: Marc Brugger Date: Mon, 30 Jan 2023 05:33:22 +0100 Subject: [PATCH 05/37] Add FQDN en translation (#1058) --- .gitignore | 1 + translations/en/en.go | 5 +++++ translations/en/en_test.go | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index 6e43fac..2410a91 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ _testmain.go *.txt cover.html README.html +.idea diff --git a/translations/en/en.go b/translations/en/en.go index ee05f91..f007d34 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1266,6 +1266,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} must contain a valid MAC address", override: false, }, + { + tag: "fqdn", + translation: "{0} must be a valid FQDN", + override: false, + }, { tag: "unique", translation: "{0} must contain unique values", diff --git a/translations/en/en_test.go b/translations/en/en_test.go index 9cb6deb..69f4ee0 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -130,6 +130,7 @@ func TestTranslations(t *testing.T) { 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"` + FQDN string `validate:"fqdn"` IsColor string `validate:"iscolor"` StrPtrMinLen *string `validate:"min=10"` StrPtrMaxLen *string `validate:"max=1"` @@ -226,6 +227,10 @@ func TestTranslations(t *testing.T) { ns: "Test.MAC", expected: "MAC must contain a valid MAC address", }, + { + ns: "Test.FQDN", + expected: "FQDN must be a valid FQDN", + }, { ns: "Test.IPAddr", expected: "IPAddr must be a resolvable IP address", From 7edc63a0be364f8a8796cc9c81cf455dcc8f2b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Ram=C3=ADrez?= Date: Sun, 29 Jan 2023 22:33:51 -0600 Subject: [PATCH 06/37] #updated Added required_id spanish translation (#1026) --- translations/es/es.go | 5 +++++ translations/es/es_test.go | 1 + 2 files changed, 6 insertions(+) diff --git a/translations/es/es.go b/translations/es/es.go index 2635408..73b06cf 100644 --- a/translations/es/es.go +++ b/translations/es/es.go @@ -29,6 +29,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} es un campo requerido", override: false, }, + { + tag: "required_if", + translation: "{0} es un campo requerido", + override: false, + }, { tag: "len", customRegisFunc: func(ut ut.Translator) (err error) { diff --git a/translations/es/es_test.go b/translations/es/es_test.go index 814f2ab..c2c4c81 100644 --- a/translations/es/es_test.go +++ b/translations/es/es_test.go @@ -35,6 +35,7 @@ func TestTranslations(t *testing.T) { RequiredString string `validate:"required"` RequiredNumber int `validate:"required"` RequiredMultiple []string `validate:"required"` + RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"` LenString string `validate:"len=1"` LenNumber float64 `validate:"len=1113.00"` LenMultiple []string `validate:"len=7"` From 8f07b0368280d9fc70b4ffd3708b31427bbc286d Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 29 Jan 2023 20:43:00 -0800 Subject: [PATCH 07/37] update deps (#1061) --- .github/workflows/workflow.yml | 8 ++++---- README.md | 2 +- go.mod | 14 ++++++------- go.sum | 36 +++++++++++++++------------------- validator_test.go | 4 ++-- 5 files changed, 30 insertions(+), 34 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 8c07a64..041b304 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -8,7 +8,7 @@ jobs: test: strategy: matrix: - go-version: [1.17.x, 1.18.x] + go-version: [1.19.x] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -32,7 +32,7 @@ jobs: run: go test -race -covermode=atomic -coverprofile="profile.cov" ./... - name: Send Coverage - if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.18.x' + if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.19.x' uses: shogo82148/actions-goveralls@v1 with: path-to-profile: profile.cov @@ -43,9 +43,9 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.18.x + go-version: 1.19.x - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.45.2 + version: v1.50.1 diff --git a/README.md b/README.md index 9d0a79e..5f8878d 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.11.1-green.svg) +![Project status](https://img.shields.io/badge/version-10.11.2-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) diff --git a/go.mod b/go.mod index 35bbac2..b0efab7 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,18 @@ module github.com/go-playground/validator/v10 -go 1.13 +go 1.18 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.14.0 - github.com/go-playground/universal-translator v0.18.0 + github.com/go-playground/assert/v2 v2.2.0 + github.com/go-playground/locales v0.14.1 + github.com/go-playground/universal-translator v0.18.1 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-20211215153901-e495a2d5b3d3 - golang.org/x/text v0.3.8 + golang.org/x/crypto v0.5.0 + golang.org/x/text v0.6.0 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1c1fb72..940ed5d 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,12 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 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.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/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 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= @@ -31,33 +31,31 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -68,7 +66,5 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN 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= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/validator_test.go b/validator_test.go index 7e314d6..62be26a 100644 --- a/validator_test.go +++ b/validator_test.go @@ -2038,10 +2038,10 @@ func TestCrossNamespaceFieldValidation(t *testing.T) { Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") - current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField") + _, _, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField") Equal(t, ok, false) - current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]") + _, _, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]") Equal(t, ok, false) current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]") From b95730fb115eff942b0657808ff9a2ecf692a223 Mon Sep 17 00:00:00 2001 From: Mohamed Sohail Date: Sun, 19 Mar 2023 21:35:26 +0300 Subject: [PATCH 08/37] feat: add eth_addr_checksum validation, update eth_addr (#1080) rationale: - ethereum addresses are derived from a public key (the last 20 bytes). The network accepts any case insensitive address as long as it is a 40 char alphanumeric (When the last 20 bytes are converted to hex). It is 0x prefixed so that it becomes 42 in length, hence the current eth_addr implementation is modified to reflect this. - Checksum-ing is optional on ethereum and is a way of typo checking the address hence this is introduced as a new validation i.e. eth_addr_checksum. refs: - https://github.com/ethereum/go-ethereum/blob/master/crypto/crypto.go#L275 - https://goethereumbook.org/en/wallet-generate/ - https://goethereumbook.org/en/address-check/ --- baked_in.go | 13 +++++++----- regexes.go | 2 -- validator_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/baked_in.go b/baked_in.go index c9b1db4..191cc54 100644 --- a/baked_in.go +++ b/baked_in.go @@ -140,6 +140,7 @@ var ( "isbn10": isISBN10, "isbn13": isISBN13, "eth_addr": isEthereumAddress, + "eth_addr_checksum": isEthereumAddressChecksum, "btc_addr": isBitcoinAddress, "btc_addr_bech32": isBitcoinBech32Address, "uuid": isUUID, @@ -613,14 +614,16 @@ func isISBN10(fl FieldLevel) bool { func isEthereumAddress(fl FieldLevel) bool { address := fl.Field().String() + return ethAddressRegex.MatchString(address) +} + +// isEthereumAddressChecksum is the validation function for validating if the field's value is a valid checksumed Ethereum address. +func isEthereumAddressChecksum(fl FieldLevel) bool { + address := fl.Field().String() + if !ethAddressRegex.MatchString(address) { return false } - - if ethAddressRegexUpper.MatchString(address) || ethAddressRegexLower.MatchString(address) { - return true - } - // Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md address = address[2:] // Skip "0x" prefix. h := sha3.NewLegacyKeccak256() diff --git a/regexes.go b/regexes.go index 9c1c634..fb2ea18 100644 --- a/regexes.go +++ b/regexes.go @@ -118,8 +118,6 @@ var ( btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32) btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32) ethAddressRegex = regexp.MustCompile(ethAddressRegexString) - ethAddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString) - ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString) uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString) hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString) hTMLRegex = regexp.MustCompile(hTMLRegexString) diff --git a/validator_test.go b/validator_test.go index 62be26a..33abcb0 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5672,7 +5672,7 @@ func TestEthereumAddressValidation(t *testing.T) { {"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true}, {"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true}, {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true}, - {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", false}, // Invalid checksum. + {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", true}, // Invalid checksum, but valid address. // Other. {"", false}, @@ -5703,6 +5703,56 @@ func TestEthereumAddressValidation(t *testing.T) { } } +func TestEthereumAddressChecksumValidation(t *testing.T) { + validate := New() + + tests := []struct { + param string + expected bool + }{ + // All caps. + {"0x52908400098527886E0F7030069857D2E4169EE7", true}, + {"0x8617E340B3D01FA5F11F306F4090FD50E238070D", true}, + + // All lower. + {"0x27b1fdb04752bbc536007a920d24acb045561c26", true}, + {"0x123f681646d4a755815f9cb19e1acc8565a0c2ac", false}, + + // Mixed case: runs checksum validation. + {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true}, + {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", false}, // Invalid checksum. + {"0x000000000000000000000000000000000000dead", false}, // Invalid checksum. + {"0x000000000000000000000000000000000000dEaD", true}, // Valid checksum. + + // Other. + {"", false}, + {"D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", false}, // Missing "0x" prefix. + {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDbc", false}, // More than 40 hex digits. + {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aD", false}, // Less than 40 hex digits. + {"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDw", false}, // Invalid hex digit "w". + } + + for i, test := range tests { + + errs := validate.Var(test.param, "eth_addr_checksum") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d eth_addr_checksum failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d eth_addr_checksum failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "eth_addr_checksum" { + t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) + } + } + } + } +} + func TestBitcoinAddressValidation(t *testing.T) { validate := New() From c0b3430b1f90efd08efe1b23bb2b3a766e9b9a74 Mon Sep 17 00:00:00 2001 From: Phil Porada Date: Sun, 19 Mar 2023 14:36:20 -0400 Subject: [PATCH 09/37] Fix spelling error in doc (#1078) --- doc.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc.go b/doc.go index 7341c67..7f0a853 100644 --- a/doc.go +++ b/doc.go @@ -232,16 +232,16 @@ require another 'keys' and 'endkeys' tag. These tags are only valid for maps. Example #1 - map[string]string with validation tag "gt=0,dive,keys,eg=1|eq=2,endkeys,required" + map[string]string with validation tag "gt=0,dive,keys,eq=1|eq=2,endkeys,required" // gt=0 will be applied to the map itself - // eg=1|eq=2 will be applied to the map keys + // eq=1|eq=2 will be applied to the map keys // required will be applied to map values Example #2 map[[2]string]string with validation tag "gt=0,dive,keys,dive,eq=1|eq=2,endkeys,required" // gt=0 will be applied to the map itself - // eg=1|eq=2 will be applied to each array element in the the map keys + // eq=1|eq=2 will be applied to each array element in the the map keys // required will be applied to map values Required From 72a3e754603c7341e93ccf48e3e7db7e66767a40 Mon Sep 17 00:00:00 2001 From: sgallizia Date: Sun, 19 Mar 2023 19:40:08 +0100 Subject: [PATCH 10/37] added IsEqIgnoreCase and IsNeIgnoreCase (#1076) --- README.md | 2 ++ baked_in.go | 32 ++++++++++++++++++++++++++++++-- validator_test.go | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5f8878d..2e5fdcd 100644 --- a/README.md +++ b/README.md @@ -207,11 +207,13 @@ Baked-in Validations | Tag | Description | | - | - | | eq | Equals | +| eqIgnoreCase | Equals ignoring case | | gt | Greater than| | gte | Greater than or equal | | lt | Less Than | | lte | Less Than or Equal | | ne | Not Equal | +| neIgnoreCase | Not Equal ignoring case | ### Other: | Tag | Description | diff --git a/baked_in.go b/baked_in.go index 191cc54..b9c3fdd 100644 --- a/baked_in.go +++ b/baked_in.go @@ -86,7 +86,9 @@ var ( "min": hasMinOf, "max": hasMaxOf, "eq": isEq, + "eqIgnoreCase": isEqIgnoreCase, "ne": isNe, + "neIgnoreCase": isNeIgnoreCase, "lt": isLt, "lte": isLte, "gt": isGt, @@ -892,6 +894,12 @@ func isNe(fl FieldLevel) bool { return !isEq(fl) } +// isNe is the validation function for validating that the field's string value does not equal the +// provided param value. The comparison is case-insensitive +func isNeIgnoreCase(fl FieldLevel) bool { + return !isEqIgnoreCase(fl) +} + // isLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value. func isLteCrossStructField(fl FieldLevel) bool { field := fl.Field() @@ -1263,6 +1271,22 @@ func isEq(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isEqIgnoreCase is the validation function for validating if the current field's string value is +//equal to the param's value. +// The comparison is case-insensitive. +func isEqIgnoreCase(fl FieldLevel) bool { + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + return strings.EqualFold(field.String(), param) + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // isPostcodeByIso3166Alpha2 validates by value which is country code in iso 3166 alpha 2 // example: `postcode_iso3166_alpha2=US` func isPostcodeByIso3166Alpha2(fl FieldLevel) bool { @@ -1542,7 +1566,9 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo } // requireCheckFieldValue is a func for check field value -func requireCheckFieldValue(fl FieldLevel, param string, value string, defaultNotFoundValue bool) bool { +func requireCheckFieldValue( + fl FieldLevel, param string, value string, defaultNotFoundValue bool, +) bool { field, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), param) if !found { return defaultNotFoundValue @@ -2319,7 +2345,9 @@ func isHostnamePort(fl FieldLevel) bool { return false } // Port must be a iny <= 65535. - if portNum, err := strconv.ParseInt(port, 10, 32); err != nil || portNum > 65535 || portNum < 1 { + if portNum, err := strconv.ParseInt( + port, 10, 32, + ); err != nil || portNum > 65535 || portNum < 1 { return false } diff --git a/validator_test.go b/validator_test.go index 33abcb0..8c9a4dd 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5207,6 +5207,24 @@ func TestIsNeValidation(t *testing.T) { Equal(t, errs, nil) } +func TestIsNeIgnoreCaseValidation(t *testing.T) { + var errs error + validate := New() + s := "abcd" + now := time.Now() + + errs = validate.Var(s, "neIgnoreCase=efgh") + Equal(t, errs, nil) + + errs = validate.Var(s, "neIgnoreCase=AbCd") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "", "", "neIgnoreCase") + + PanicMatches( + t, func() { _ = validate.Var(now, "eqIgnoreCase=abcd") }, "Bad field type time.Time", + ) +} + func TestIsEqFieldValidation(t *testing.T) { var errs error validate := New() @@ -5484,6 +5502,23 @@ func TestIsEqValidation(t *testing.T) { Equal(t, errs, nil) } +func TestIsEqIgnoreCaseValidation(t *testing.T) { + var errs error + validate := New() + s := "abcd" + now := time.Now() + + errs = validate.Var(s, "eqIgnoreCase=abcd") + Equal(t, errs, nil) + + errs = validate.Var(s, "eqIgnoreCase=AbCd") + Equal(t, errs, nil) + + PanicMatches( + t, func() { _ = validate.Var(now, "eqIgnoreCase=abcd") }, "Bad field type time.Time", + ) +} + func TestOneOfValidation(t *testing.T) { validate := New() From d1178535f7442b82ac8b63929af35c279066473f Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 19 Mar 2023 11:44:44 -0700 Subject: [PATCH 11/37] change eq and ne ignore case tag names --- baked_in.go | 6 +++--- validator_test.go | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/baked_in.go b/baked_in.go index b9c3fdd..e08442b 100644 --- a/baked_in.go +++ b/baked_in.go @@ -86,9 +86,9 @@ var ( "min": hasMinOf, "max": hasMaxOf, "eq": isEq, - "eqIgnoreCase": isEqIgnoreCase, + "eq_ignore_case": isEqIgnoreCase, "ne": isNe, - "neIgnoreCase": isNeIgnoreCase, + "ne_ignore_case": isNeIgnoreCase, "lt": isLt, "lte": isLte, "gt": isGt, @@ -1272,7 +1272,7 @@ func isEq(fl FieldLevel) bool { } // isEqIgnoreCase is the validation function for validating if the current field's string value is -//equal to the param's value. +// equal to the param's value. // The comparison is case-insensitive. func isEqIgnoreCase(fl FieldLevel) bool { field := fl.Field() diff --git a/validator_test.go b/validator_test.go index 8c9a4dd..9ffcd9c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5213,15 +5213,15 @@ func TestIsNeIgnoreCaseValidation(t *testing.T) { s := "abcd" now := time.Now() - errs = validate.Var(s, "neIgnoreCase=efgh") + errs = validate.Var(s, "ne_ignore_case=efgh") Equal(t, errs, nil) - errs = validate.Var(s, "neIgnoreCase=AbCd") + errs = validate.Var(s, "ne_ignore_case=AbCd") NotEqual(t, errs, nil) - AssertError(t, errs, "", "", "", "", "neIgnoreCase") + AssertError(t, errs, "", "", "", "", "ne_ignore_case") PanicMatches( - t, func() { _ = validate.Var(now, "eqIgnoreCase=abcd") }, "Bad field type time.Time", + t, func() { _ = validate.Var(now, "ne_ignore_case=abcd") }, "Bad field type time.Time", ) } @@ -5508,14 +5508,14 @@ func TestIsEqIgnoreCaseValidation(t *testing.T) { s := "abcd" now := time.Now() - errs = validate.Var(s, "eqIgnoreCase=abcd") + errs = validate.Var(s, "eq_ignore_case=abcd") Equal(t, errs, nil) - errs = validate.Var(s, "eqIgnoreCase=AbCd") + errs = validate.Var(s, "eq_ignore_case=AbCd") Equal(t, errs, nil) PanicMatches( - t, func() { _ = validate.Var(now, "eqIgnoreCase=abcd") }, "Bad field type time.Time", + t, func() { _ = validate.Var(now, "eq_ignore_case=abcd") }, "Bad field type time.Time", ) } From f17b3458fadd336accebedfec00b936d49e93a86 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 19 Mar 2023 11:46:15 -0700 Subject: [PATCH 12/37] fix README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e5fdcd..4203296 100644 --- a/README.md +++ b/README.md @@ -207,13 +207,13 @@ Baked-in Validations | Tag | Description | | - | - | | eq | Equals | -| eqIgnoreCase | Equals ignoring case | +| eq_ignore_case | Equals ignoring case | | gt | Greater than| | gte | Greater than or equal | | lt | Less Than | | lte | Less Than or Equal | | ne | Not Equal | -| neIgnoreCase | Not Equal ignoring case | +| ne_ignore_case | Not Equal ignoring case | ### Other: | Tag | Description | From c242c49c02f7393de22355d5eb26b4eb78b7c0e9 Mon Sep 17 00:00:00 2001 From: Simon Tremblay Date: Sun, 19 Mar 2023 15:02:45 -0400 Subject: [PATCH 13/37] Update go.mod/go.sum using go mod tidy (#1071) --- go.mod | 11 ++++++----- go.sum | 43 ------------------------------------------- 2 files changed, 6 insertions(+), 48 deletions(-) diff --git a/go.mod b/go.mod index b0efab7..d4a9d9c 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,17 @@ module github.com/go-playground/validator/v10 go 1.18 require ( - github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-playground/assert/v2 v2.2.0 github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 - 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.5.0 golang.org/x/text v0.6.0 - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/stretchr/testify v1.7.0 // indirect + golang.org/x/sys v0.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 940ed5d..4203ddc 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -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= @@ -8,63 +7,21 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -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/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= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 13ad6f5b30d18528169bcf3fc15fe42ca9fb051a Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 19 Mar 2023 12:06:56 -0700 Subject: [PATCH 14/37] update deps --- go.mod | 13 ++++--------- go.sum | 26 +++++++++++++++----------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index d4a9d9c..aa7527d 100644 --- a/go.mod +++ b/go.mod @@ -6,14 +6,9 @@ require ( github.com/go-playground/assert/v2 v2.2.0 github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 - github.com/leodido/go-urn v1.2.1 - golang.org/x/crypto v0.5.0 - golang.org/x/text v0.6.0 + github.com/leodido/go-urn v1.2.2 + golang.org/x/crypto v0.7.0 + golang.org/x/text v0.8.0 ) -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/stretchr/testify v1.7.0 // indirect - golang.org/x/sys v0.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require golang.org/x/sys v0.6.0 // indirect diff --git a/go.sum b/go.sum index 4203ddc..cce9416 100644 --- a/go.sum +++ b/go.sum @@ -7,20 +7,24 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -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/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4= +github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ= 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/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -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.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 75ba9f6a84164260b0eef7689fbaedd208116020 Mon Sep 17 00:00:00 2001 From: Vitor Hugo Date: Sun, 19 Mar 2023 16:09:27 -0300 Subject: [PATCH 15/37] Update README.md (#1066) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4203296..1334311 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,8 @@ Baked-in Validations | - | - | | eqcsfield | Field Equals Another Field (relative)| | eqfield | Field Equals Another Field | -| fieldcontains | NOT DOCUMENTED IN doc.go | -| fieldexcludes | NOT DOCUMENTED IN doc.go | +| fieldcontains | Check the indicated characters are present in the Field | +| fieldexcludes | Check the indicated characters are not present in the field | | gtcsfield | Field Greater Than Another Relative Field | | gtecsfield | Field Greater Than or Equal To Another Relative Field | | gtefield | Field Greater Than or Equal To Another Field | @@ -130,14 +130,14 @@ Baked-in Validations | contains | Contains | | containsany | Contains Any | | containsrune | Contains Rune | -| endsnotwith | Ends Not With | +| ends\with | Ends Not With | | endswith | Ends With | | excludes | Excludes | | excludesall | Excludes All | | excludesrune | Excludes Rune | | lowercase | Lowercase | | multibyte | Multi-Byte Characters | -| number | NOT DOCUMENTED IN doc.go | +| number | Number | | numeric | Numeric | | printascii | Printable ASCII | | startsnotwith | Starts Not With | From f3086daf632deaff7a4321dbd932a7e98545eb28 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 19 Mar 2023 12:10:07 -0700 Subject: [PATCH 16/37] fix README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1334311..f2b6772 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ Baked-in Validations | contains | Contains | | containsany | Contains Any | | containsrune | Contains Rune | -| ends\with | Ends Not With | +| endsnotwith | Ends Not With | | endswith | Ends With | | excludes | Excludes | | excludesall | Excludes All | From ef342b6f7ced1f4cc36fcb9019149138b91a245d Mon Sep 17 00:00:00 2001 From: Arthur Jenoudet <23088925+jenoudet@users.noreply.github.com> Date: Sun, 19 Mar 2023 12:12:53 -0700 Subject: [PATCH 17/37] fix validator excluded_unless (#1064) --- baked_in.go | 4 ++-- validator_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/baked_in.go b/baked_in.go index e08442b..eda5814 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1652,10 +1652,10 @@ func excludedUnless(fl FieldLevel) bool { } for i := 0; i < len(params); i += 2 { if !requireCheckFieldValue(fl, params[i], params[i+1], false) { - return true + return !hasValue(fl) } } - return !hasValue(fl) + return true } // excludedWith is the validation function diff --git a/validator_test.go b/validator_test.go index 9ffcd9c..0f43db8 100644 --- a/validator_test.go +++ b/validator_test.go @@ -11259,7 +11259,7 @@ func TestExcludedUnless(t *testing.T) { FieldE string `validate:"omitempty" json:"field_e"` FieldER string `validate:"excluded_unless=FieldE test" json:"field_er"` }{ - FieldE: "notest", + FieldE: "test", FieldER: "filled", } errs := validate.Struct(test) @@ -11269,7 +11269,7 @@ func TestExcludedUnless(t *testing.T) { FieldE string `validate:"omitempty" json:"field_e"` FieldER string `validate:"excluded_unless=FieldE test" json:"field_er"` }{ - FieldE: "test", + FieldE: "notest", FieldER: "filled", } errs = validate.Struct(test2) @@ -11278,7 +11278,26 @@ func TestExcludedUnless(t *testing.T) { Equal(t, len(ve), 1) AssertError(t, errs, "FieldER", "FieldER", "FieldER", "FieldER", "excluded_unless") - shouldError := "test" + // test5 and test6: excluded_unless has no effect if FieldER is left blank + test5 := struct { + FieldE string `validate:"omitempty" json:"field_e"` + FieldER string `validate:"excluded_unless=FieldE test" json:"field_er"` + }{ + FieldE: "test", + } + errs = validate.Struct(test5) + Equal(t, errs, nil) + + test6 := struct { + FieldE string `validate:"omitempty" json:"field_e"` + FieldER string `validate:"excluded_unless=FieldE test" json:"field_er"` + }{ + FieldE: "notest", + } + errs = validate.Struct(test6) + Equal(t, errs, nil) + + shouldError := "notest" test3 := struct { Inner *Inner Field1 string `validate:"excluded_unless=Inner.Field test" json:"field_1"` @@ -11292,7 +11311,7 @@ func TestExcludedUnless(t *testing.T) { Equal(t, len(ve), 1) AssertError(t, errs, "Field1", "Field1", "Field1", "Field1", "excluded_unless") - shouldPass := "shouldPass" + shouldPass := "test" test4 := struct { Inner *Inner FieldE string `validate:"omitempty" json:"field_e"` @@ -11304,6 +11323,26 @@ func TestExcludedUnless(t *testing.T) { errs = validate.Struct(test4) Equal(t, errs, nil) + // test7 and test8: excluded_unless has no effect if FieldER is left blank + test7 := struct { + Inner *Inner + FieldE string `validate:"omitempty" json:"field_e"` + FieldER string `validate:"excluded_unless=Inner.Field test" json:"field_er"` + }{ + FieldE: "test", + } + errs = validate.Struct(test7) + Equal(t, errs, nil) + + test8 := struct { + FieldE string `validate:"omitempty" json:"field_e"` + FieldER string `validate:"excluded_unless=Inner.Field test" json:"field_er"` + }{ + FieldE: "test", + } + errs = validate.Struct(test8) + Equal(t, errs, nil) + // Checks number of params in struct tag is correct defer func() { if r := recover(); r == nil { From f2078f769615f1774abe2047eff6b770591e4275 Mon Sep 17 00:00:00 2001 From: Vijay Nallagatla Date: Sun, 19 Mar 2023 21:14:38 +0200 Subject: [PATCH 18/37] Fix unique validator for map with Pointer value (#1062) --- baked_in.go | 10 ++++++++-- validator_test.go | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/baked_in.go b/baked_in.go index eda5814..ef9e055 100644 --- a/baked_in.go +++ b/baked_in.go @@ -315,11 +315,17 @@ func isUnique(fl FieldLevel) bool { } return field.Len() == m.Len() case reflect.Map: - m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + var m reflect.Value + if field.Type().Elem().Kind() == reflect.Ptr { + m = reflect.MakeMap(reflect.MapOf(field.Type().Elem().Elem(), v.Type())) + } else { + m = reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + } for _, k := range field.MapKeys() { - m.SetMapIndex(field.MapIndex(k), v) + m.SetMapIndex(reflect.Indirect(field.MapIndex(k)), v) } + return field.Len() == m.Len() default: panic(fmt.Sprintf("Bad field type %T", field.Interface())) diff --git a/validator_test.go b/validator_test.go index 0f43db8..e2a608f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -9890,6 +9890,12 @@ func TestUniqueValidation(t *testing.T) { {map[string]string{"one": "a", "two": "a"}, false}, {map[string]interface{}{"one": "a", "two": "a"}, false}, {map[string]interface{}{"one": "a", "two": 1, "three": "b", "four": 1}, false}, + {map[string]*string{"one": stringPtr("a"), "two": stringPtr("a")}, false}, + {map[string]*string{"one": stringPtr("a"), "two": stringPtr("b")}, true}, + {map[string]*int{"one": intPtr(1), "two": intPtr(1)}, false}, + {map[string]*int{"one": intPtr(1), "two": intPtr(2)}, true}, + {map[string]*float64{"one": float64Ptr(1.1), "two": float64Ptr(1.1)}, false}, + {map[string]*float64{"one": float64Ptr(1.1), "two": float64Ptr(1.2)}, true}, } validate := New() From 89b91cea9901d8a92adfdbb5d0e8491344253132 Mon Sep 17 00:00:00 2001 From: Owen Gong Date: Mon, 20 Mar 2023 03:17:08 +0800 Subject: [PATCH 19/37] Added "http_url" tag to validate if the variable is a HTTP(s) URL (#1047) --- README.md | 1 + baked_in.go | 18 ++++++++++++ validator_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/README.md b/README.md index f2b6772..84b4cb6 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Baked-in Validations | unix_addr | Unix domain socket end point Address | | uri | URI String | | url | URL String | +| http_url | HTTP URL String | | url_encoded | URL Encoded | | urn_rfc2141 | Urn RFC 2141 String | diff --git a/baked_in.go b/baked_in.go index ef9e055..eafc2a0 100644 --- a/baked_in.go +++ b/baked_in.go @@ -123,6 +123,7 @@ var ( "e164": isE164, "email": isEmail, "url": isURL, + "http_url": isHttpURL, "uri": isURI, "urn_rfc2141": isUrnRFC2141, // RFC 2141 "file": isFile, @@ -1403,6 +1404,23 @@ func isURL(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isHttpURL is the validation function for validating if the current field's value is a valid HTTP(s) URL. +func isHttpURL(fl FieldLevel) bool { + if !isURL(fl) { + return false + } + + field := fl.Field() + switch field.Kind() { + case reflect.String: + + s := strings.ToLower(field.String()) + return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141. func isUrnRFC2141(fl FieldLevel) bool { field := fl.Field() diff --git a/validator_test.go b/validator_test.go index e2a608f..79cfb55 100644 --- a/validator_test.go +++ b/validator_test.go @@ -7878,6 +7878,77 @@ func TestUrl(t *testing.T) { PanicMatches(t, func() { _ = validate.Var(i, "url") }, "Bad field type int") } +func TestHttpUrl(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"http://foo.bar#com", true}, + {"http://foobar.com", true}, + {"HTTP://foobar.com", true}, + {"https://foobar.com", true}, + {"foobar.com", false}, + {"http://foobar.coffee/", true}, + {"http://foobar.中文网/", true}, + {"http://foobar.org/", true}, + {"http://foobar.org:8080/", true}, + {"ftp://foobar.ru/", false}, + {"file:///etc/passwd", false}, + {"file://C:/windows/win.ini", false}, + {"http://user:pass@www.foobar.com/", true}, + {"http://127.0.0.1/", true}, + {"http://duckduckgo.com/?q=%2F", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com/?foo=bar#baz=qux", true}, + {"http://foobar.com?foo=bar", true}, + {"http://www.xn--froschgrn-x9a.net/", true}, + {"", false}, + {"a://b", false}, + {"xyz://foobar.com", false}, + {"invalid.", false}, + {".com", false}, + {"rtmp://foobar.com", false}, + {"http://www.foo_bar.com/", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com/#baz", true}, + {"http://foobar.com#baz=qux", true}, + {"http://foobar.com/t$-_.+!*\\'(),", true}, + {"http://www.foobar.com/~foobar", true}, + {"http://www.-foobar.com/", true}, + {"http://www.foo---bar.com/", true}, + {"mailto:someone@example.com", false}, + {"irc://irc.server.org/channel", false}, + {"irc://#channel@network", false}, + {"/abs/test/dir", false}, + {"./rel/test/dir", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "http_url") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d HTTP URL failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d HTTP URL failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "http_url" { + t.Fatalf("Index: %d HTTP URL failed Error: %s", i, errs) + } + } + } + } + + i := 1 + PanicMatches(t, func() { _ = validate.Var(i, "http_url") }, "Bad field type int") +} + func TestUri(t *testing.T) { tests := []struct { param string From 3ee65f8c59a1f10e43bee5f5ebaf49a437f65d1b Mon Sep 17 00:00:00 2001 From: Alberto Forcato <45015176+albertoforcato@users.noreply.github.com> Date: Sun, 19 Mar 2023 20:20:46 +0100 Subject: [PATCH 20/37] Add cron support (#1045) --- README.md | 1 + baked_in.go | 7 +++++++ doc.go | 6 ++++++ regexes.go | 2 ++ translations/en/en.go | 5 +++++ translations/it/it.go | 9 +++++++-- validator_test.go | 34 ++++++++++++++++++++++++++++++++++ 7 files changed, 62 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 84b4cb6..284092b 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ Baked-in Validations | btc_addr | Bitcoin Address | | btc_addr_bech32 | Bitcoin Bech32 Address (segwit) | | credit_card | Credit Card Number | +| cron | Cron | | datetime | Datetime | | e164 | e164 formatted phone number | | email | E-mail String diff --git a/baked_in.go b/baked_in.go index eafc2a0..e8d17a7 100644 --- a/baked_in.go +++ b/baked_in.go @@ -218,6 +218,7 @@ var ( "semver": isSemverFormat, "dns_rfc1035_label": isDnsRFC1035LabelFormat, "credit_card": isCreditCard, + "cron": isCron, } ) @@ -2579,3 +2580,9 @@ func isCreditCard(fl FieldLevel) bool { } return (sum % 10) == 0 } + +// isCron is the validation function for validating if the current field's value is a valid cron expression +func isCron(fl FieldLevel) bool { + cronString := fl.Field().String() + return cronRegex.MatchString(cronString) +} diff --git a/doc.go b/doc.go index 7f0a853..ba4639c 100644 --- a/doc.go +++ b/doc.go @@ -1323,6 +1323,12 @@ This validates that a string value contains a valid credit card number using Luh Usage: credit_card +Cron + +This validates that a string value contains a valid cron expression. + + Usage: cron + Alias Validators and Tags NOTE: When returning an error, the tag returned in "FieldError" will be diff --git a/regexes.go b/regexes.go index fb2ea18..5436e3f 100644 --- a/regexes.go +++ b/regexes.go @@ -64,6 +64,7 @@ const ( 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}$" + cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})` ) var ( @@ -126,4 +127,5 @@ var ( bicRegex = regexp.MustCompile(bicRegexString) semverRegex = regexp.MustCompile(semverRegexString) dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label) + cronRegex = regexp.MustCompile(cronRegexString) ) diff --git a/translations/en/en.go b/translations/en/en.go index f007d34..0668bb2 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1281,6 +1281,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} must be a valid color", override: false, }, + { + tag: "cron", + translation: "{0} must be a valid cron expression", + override: false, + }, { tag: "oneof", translation: "{0} must be one of [{1}]", diff --git a/translations/it/it.go b/translations/it/it.go index 0b46fc4..8f999a4 100644 --- a/translations/it/it.go +++ b/translations/it/it.go @@ -124,7 +124,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { tag: "min", customRegisFunc: func(ut ut.Translator) (err error) { - + if err = ut.Add("min-string", "{0} deve essere lungo almeno {1}", false); err != nil { return } @@ -432,7 +432,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { tag: "lte", customRegisFunc: func(ut ut.Translator) (err error) { - + if err = ut.Add("lte-string", "{0} deve essere lungo al massimo {1}", false); err != nil { return } @@ -1132,6 +1132,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} deve essere un colore valido", override: false, }, + { + tag: "cron", + translation: "{0} deve essere una stringa cron valida", + override: false, + }, { tag: "oneof", translation: "{0} deve essere uno di [{1}]", diff --git a/validator_test.go b/validator_test.go index 79cfb55..b8ebe64 100644 --- a/validator_test.go +++ b/validator_test.go @@ -12524,3 +12524,37 @@ func TestMultiOrOperatorGroup(t *testing.T) { } } } + +func TestCronExpressionValidation(t *testing.T) { + tests := []struct { + value string `validate:"cron"` + tag string + expected bool + }{ + {"0 0 12 * * ?", "cron", true}, + {"0 15 10 ? * *", "cron", true}, + {"0 15 10 * * ?", "cron", true}, + {"0 15 10 * * ? 2005", "cron", true}, + {"0 15 10 ? * 6L", "cron", true}, + {"0 15 10 ? * 6L 2002-2005", "cron", true}, + {"*/20 * * * *", "cron", true}, + {"0 15 10 ? * MON-FRI", "cron", true}, + {"0 15 10 ? * 6#3", "cron", true}, + {"wrong", "cron", 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 cron "%s" failed Error: %s`, i, test.value, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf(`Index: %d cron "%s" should have errs`, i, test.value) + } + } + } +} From cc768b176db37d4bd0efee2572a0402ec5b8da6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Sala=C3=BCn?= <1910607+yansal@users.noreply.github.com> Date: Sun, 19 Mar 2023 20:22:03 +0100 Subject: [PATCH 21/37] fix unique= when struct field is a nil pointer (#1041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix https://github.com/go-playground/validator/issues/749 Co-authored-by: Yann Salaün --- baked_in.go | 9 +++++++-- validator_test.go | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/baked_in.go b/baked_in.go index e8d17a7..dd4b331 100644 --- a/baked_in.go +++ b/baked_in.go @@ -312,10 +312,15 @@ func isUnique(fl FieldLevel) bool { } m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type())) + var fieldlen int for i := 0; i < field.Len(); i++ { - m.SetMapIndex(reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param)), v) + key := reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param)) + if key.IsValid() { + fieldlen++ + m.SetMapIndex(key, v) + } } - return field.Len() == m.Len() + return fieldlen == m.Len() case reflect.Map: var m reflect.Value if field.Type().Elem().Kind() == reflect.Ptr { diff --git a/validator_test.go b/validator_test.go index b8ebe64..07bc889 100644 --- a/validator_test.go +++ b/validator_test.go @@ -10043,6 +10043,7 @@ func TestUniqueValidationStructPtrSlice(t *testing.T) { }{ {A: stringPtr("one"), B: stringPtr("two")}, {A: stringPtr("one"), B: stringPtr("three")}, + {}, } tests := []struct { From 1c1f70d35b58e3ecf9416b30f7b3804e8e68dee3 Mon Sep 17 00:00:00 2001 From: zemzale <14844365+zemzale@users.noreply.github.com> Date: Sun, 19 Mar 2023 21:28:12 +0200 Subject: [PATCH 22/37] feat(unique): Add support for struct memember validation (#1048) This allows validating that two struct memebers are unique. This has been documented as a feature, but has never been actually implemented. --- baked_in.go | 13 +++++++++++++ validator_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/baked_in.go b/baked_in.go index dd4b331..1e6e00d 100644 --- a/baked_in.go +++ b/baked_in.go @@ -335,6 +335,19 @@ func isUnique(fl FieldLevel) bool { return field.Len() == m.Len() default: + if parent := fl.Parent(); parent.Kind() == reflect.Struct { + uniqueField := parent.FieldByName(param) + if uniqueField == reflect.ValueOf(nil) { + panic(fmt.Sprintf("Bad field name provided %s", param)) + } + + if uniqueField.Kind() != field.Kind() { + panic(fmt.Sprintf("Bad field type %T:%T", field.Interface(), uniqueField.Interface())) + } + + return field.Interface() != uniqueField.Interface() + } + panic(fmt.Sprintf("Bad field type %T", field.Interface())) } } diff --git a/validator_test.go b/validator_test.go index 07bc889..fdf8a54 100644 --- a/validator_test.go +++ b/validator_test.go @@ -9991,6 +9991,41 @@ func TestUniqueValidation(t *testing.T) { } } PanicMatches(t, func() { _ = validate.Var(1.0, "unique") }, "Bad field type float64") + + t.Run("struct", func(t *testing.T) { + tests := []struct { + param interface{} + expected bool + }{ + {struct { + A string `validate:"unique=B"` + B string + }{A: "abc", B: "bcd"}, true}, + {struct { + A string `validate:"unique=B"` + B string + }{A: "abc", B: "abc"}, false}, + } + validate := New() + + for i, test := range tests { + errs := validate.Struct(test.param) + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d unique failed Error: %v", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d unique failed Error: %v", i, errs) + } else { + val := getError(errs, "A", "A") + if val.Tag() != "unique" { + t.Fatalf("Index: %d unique failed Error: %v", i, errs) + } + } + } + } + }) } func TestUniqueValidationStructSlice(t *testing.T) { From 29d50ba9634f850e9535ae850eb763c010175cd3 Mon Sep 17 00:00:00 2001 From: Yasoob Haider Date: Mon, 20 Mar 2023 01:05:00 +0530 Subject: [PATCH 23/37] add validation for base64 url without = padding (#1024) Co-authored-by: Yasoob Haider Co-authored-by: Dean Karn --- README.md | 1 + baked_in.go | 6 + cache.go | 2 +- doc.go | 315 ++++++++++++++++++++++-------------------- regexes.go | 2 + validator.go | 1 - validator_instance.go | 19 +-- validator_test.go | 47 +++++++ 8 files changed, 229 insertions(+), 164 deletions(-) diff --git a/README.md b/README.md index 284092b..3d52c73 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ Baked-in Validations | - | - | | base64 | Base64 String | | base64url | Base64URL String | +| base64rawurl | Base64RawURL String | | bic | Business Identifier Code (ISO 9362) | | bcp47_language_tag | Language tag (BCP 47) | | btc_addr | Bitcoin Address | diff --git a/baked_in.go b/baked_in.go index 1e6e00d..9ce2282 100644 --- a/baked_in.go +++ b/baked_in.go @@ -129,6 +129,7 @@ var ( "file": isFile, "base64": isBase64, "base64url": isBase64URL, + "base64rawurl": isBase64RawURL, "contains": contains, "containsany": containsAny, "containsrune": containsRune, @@ -1364,6 +1365,11 @@ func isBase64URL(fl FieldLevel) bool { return base64URLRegex.MatchString(fl.Field().String()) } +// isBase64RawURL is the validation function for validating if the current field's value is a valid base64 URL safe string without '=' padding. +func isBase64RawURL(fl FieldLevel) bool { + return base64RawURLRegex.MatchString(fl.Field().String()) +} + // isURI is the validation function for validating if the current field's value is a valid URI. func isURI(fl FieldLevel) bool { field := fl.Field() diff --git a/cache.go b/cache.go index 7b84c91..bbfd2a4 100644 --- a/cache.go +++ b/cache.go @@ -120,7 +120,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr var fld reflect.StructField var tag string var customName string - + for i := 0; i < numFields; i++ { fld = typ.Field(i) diff --git a/doc.go b/doc.go index ba4639c..2b44b53 100644 --- a/doc.go +++ b/doc.go @@ -7,7 +7,7 @@ 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 +# Singleton Validator is designed to be thread-safe and used as a singleton instance. It caches information about your struct and validations, @@ -15,7 +15,7 @@ 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 +# Validation Functions Return Type error Doing things this way is actually the way the standard library does, see the file.Open method here: @@ -34,7 +34,7 @@ if the error returned is not nil, and if it's not check if error is InvalidValidationError ( if necessary, most of the time it isn't ) type cast it to type ValidationErrors like so err.(validator.ValidationErrors). -Custom Validation Functions +# Custom Validation Functions Custom Validation functions can be added. Example: @@ -52,21 +52,21 @@ Custom Validation functions can be added. Example: // NOTES: using the same tag name as an existing function // will overwrite the existing one -Cross-Field Validation +# Cross-Field Validation Cross-Field Validation can be done via the following tags: - - eqfield - - nefield - - gtfield - - gtefield - - ltfield - - ltefield - - eqcsfield - - necsfield - - gtcsfield - - gtecsfield - - ltcsfield - - ltecsfield + - eqfield + - nefield + - gtfield + - gtefield + - ltfield + - ltefield + - eqcsfield + - necsfield + - gtcsfield + - gtecsfield + - ltcsfield + - ltecsfield If, however, some custom cross-field validation is required, it can be done using a custom validation. @@ -106,7 +106,7 @@ used "eqcsfield" it could be multiple levels down. Example: // whatever you pass, struct, field... // when calling validate.Field(field, tag) val will be nil -Multiple Validators +# Multiple Validators Multiple validators on a field will process in the order defined. Example: @@ -124,7 +124,7 @@ Bad Validator definitions are not handled by the library. Example: // this definition of min max will never succeed -Using Validator Tags +# Using Validator Tags Baked In Cross-Field validation only compares fields on the same struct. If Cross-Field + Cross-Struct validation is needed you should implement your @@ -150,20 +150,18 @@ so the above will become excludesall=0x7C Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation. } - -Baked In Validators and Tags +# Baked In Validators and Tags Here is a list of the current built in validators: - -Skip Field +# Skip Field Tells the validation to skip this struct field; this is particularly handy in ignoring embedded structs from being validated. (Usage: -) - Usage: - + Usage: - -Or Operator +# Or Operator This is the 'or' operator allowing multiple validators to be used and accepted. (Usage: rgb|rgba) <-- this would allow either rgb or rgba @@ -172,7 +170,7 @@ colors to be accepted. This can also be combined with 'and' for example Usage: | -StructOnly +# StructOnly When a field that is a nested struct is encountered, and contains this flag any validation on the nested struct will be run, but none of the nested @@ -182,13 +180,13 @@ NOTE: only "required" and "omitempty" can be used on a struct itself. Usage: structonly -NoStructLevel +# NoStructLevel Same as structonly tag except that any struct level validations will not run. Usage: nostructlevel -Omit Empty +# Omit Empty Allows conditional validation, for example if a field is not set with a value (Determined by the "required" validator) then other validation @@ -196,7 +194,7 @@ such as min or max won't run, but if a value is set validation will run. Usage: omitempty -Dive +# Dive This tells the validator to dive into a slice, array or map and validate that level of the slice, array or map with the validation tags that follow. @@ -244,7 +242,7 @@ Example #2 // eq=1|eq=2 will be applied to each array element in the the map keys // required will be applied to map values -Required +# Required This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is @@ -253,7 +251,7 @@ ensures the value is not nil. Usage: required -Required If +# Required If The field under validation must be present and not empty only if all the other specified fields are equal to the value following the specified @@ -270,7 +268,7 @@ Examples: // require the field if the Field1 and Field2 is equal to the value respectively: Usage: required_if=Field1 foo Field2 bar -Required Unless +# Required Unless The field under validation must be present and not empty unless all the other specified fields are equal to the value following the specified @@ -287,7 +285,7 @@ Examples: // require the field unless the Field1 and Field2 is equal to the value respectively: Usage: required_unless=Field1 foo Field2 bar -Required With +# Required With The field under validation must be present and not empty only if any of the other specified fields are present. For strings ensures value is @@ -304,7 +302,7 @@ Examples: // require the field if the Field1 or Field2 is present: Usage: required_with=Field1 Field2 -Required With All +# Required With All The field under validation must be present and not empty only if all of the other specified fields are present. For strings ensures value is @@ -318,7 +316,7 @@ Example: // require the field if the Field1 and Field2 is present: Usage: required_with_all=Field1 Field2 -Required Without +# Required Without The field under validation must be present and not empty only when any of the other specified fields are not present. For strings ensures value is @@ -335,7 +333,7 @@ Examples: // require the field if the Field1 or Field2 is not present: Usage: required_without=Field1 Field2 -Required Without All +# Required Without All The field under validation must be present and not empty only when all of the other specified fields are not present. For strings ensures value is @@ -349,7 +347,7 @@ Example: // require the field if the Field1 and Field2 is not present: Usage: required_without_all=Field1 Field2 -Excluded If +# 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 @@ -366,7 +364,7 @@ Examples: // exclude the field if the Field1 and Field2 is equal to the value respectively: Usage: excluded_if=Field1 foo Field2 bar -Excluded Unless +# 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 @@ -383,14 +381,14 @@ Examples: // 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 opposite of required. Usage: isdefault -Length +# Length For numbers, length will ensure that the value is equal to the parameter given. For strings, it checks that @@ -408,7 +406,7 @@ in the parameter. Usage: len=1h30m -Maximum +# Maximum For numbers, max will ensure that the value is less than or equal to the parameter given. For strings, it checks @@ -426,7 +424,7 @@ duration given in the parameter. Usage: max=1h30m -Minimum +# Minimum For numbers, min will ensure that the value is greater or equal to the parameter given. For strings, it checks that @@ -444,7 +442,7 @@ the duration given in the parameter. Usage: min=1h30m -Equals +# Equals For strings & numbers, eq will ensure that the value is equal to the parameter given. For slices, arrays, and maps, @@ -461,7 +459,7 @@ in the parameter. Usage: eq=1h30m -Not Equal +# Not Equal For strings & numbers, ne will ensure that the value is not equal to the parameter given. For slices, arrays, and maps, @@ -478,7 +476,7 @@ given in the parameter. Usage: ne=1h30m -One Of +# One Of For strings, ints, and uints, oneof will ensure that the value is one of the values in the parameter. The parameter should be @@ -486,11 +484,11 @@ a list of values separated by whitespace. Values may be strings or numbers. To match strings with spaces in them, include the target string between single quotes. - Usage: oneof=red green - oneof='red green' 'blue yellow' - oneof=5 7 9 + Usage: oneof=red green + oneof='red green' 'blue yellow' + oneof=5 7 9 -Greater Than +# Greater Than For numbers, this will ensure that the value is greater than the parameter given. For strings, it checks that the string length @@ -514,7 +512,7 @@ given in the parameter. Usage: gt=1h30m -Greater Than or Equal +# Greater Than or Equal Same as 'min' above. Kept both to make terminology with 'len' easier. @@ -535,7 +533,7 @@ the duration given in the parameter. Usage: gte=1h30m -Less Than +# Less Than For numbers, this will ensure that the value is less than the parameter given. For strings, it checks that the string length is less than that number of @@ -558,7 +556,7 @@ in the parameter. Usage: lt=1h30m -Less Than or Equal +# Less Than or Equal Same as 'max' above. Kept both to make terminology with 'len' easier. @@ -579,7 +577,7 @@ duration given in the parameter. Usage: lte=1h30m -Field Equals Another Field +# Field Equals Another Field This will validate the field value against another fields value either within a struct or passed in field. @@ -601,7 +599,7 @@ to the top level struct. Usage: eqcsfield=InnerStructField.Field) -Field Does Not Equal Another Field +# Field Does Not Equal Another Field This will validate the field value against another fields value either within a struct or passed in field. @@ -623,7 +621,7 @@ relative to the top level struct. Usage: necsfield=InnerStructField.Field -Field Greater Than Another Field +# Field Greater Than Another Field Only valid for Numbers, time.Duration and time.Time types, this will validate the field value against another fields value either within a struct or passed in @@ -639,14 +637,14 @@ Example #2: // Validating by field: validate.VarWithValue(start, end, "gtfield") -Field Greater Than Another Relative Field +# Field Greater Than Another Relative Field This does the same as gtfield except that it validates the field provided relative to the top level struct. Usage: gtcsfield=InnerStructField.Field -Field Greater Than or Equal To Another Field +# Field Greater Than or Equal To Another Field Only valid for Numbers, time.Duration and time.Time types, this will validate the field value against another fields value either within a struct or passed in @@ -662,14 +660,14 @@ Example #2: // Validating by field: validate.VarWithValue(start, end, "gtefield") -Field Greater Than or Equal To Another Relative Field +# Field Greater Than or Equal To Another Relative Field This does the same as gtefield except that it validates the field provided relative to the top level struct. Usage: gtecsfield=InnerStructField.Field -Less Than Another Field +# Less Than Another Field Only valid for Numbers, time.Duration and time.Time types, this will validate the field value against another fields value either within a struct or passed in @@ -685,14 +683,14 @@ Example #2: // Validating by field: validate.VarWithValue(start, end, "ltfield") -Less Than Another Relative Field +# Less Than Another Relative Field This does the same as ltfield except that it validates the field provided relative to the top level struct. Usage: ltcsfield=InnerStructField.Field -Less Than or Equal To Another Field +# Less Than or Equal To Another Field Only valid for Numbers, time.Duration and time.Time types, this will validate the field value against another fields value either within a struct or passed in @@ -708,14 +706,14 @@ Example #2: // Validating by field: validate.VarWithValue(start, end, "ltefield") -Less Than or Equal To Another Relative Field +# Less Than or Equal To Another Relative Field This does the same as ltefield except that it validates the field provided relative to the top level struct. Usage: ltecsfield=InnerStructField.Field -Field Contains Another Field +# Field Contains Another Field This does the same as contains except for struct fields. It should only be used with string types. See the behavior of reflect.Value.String() for behavior on @@ -723,7 +721,7 @@ other types. Usage: containsfield=InnerStructField.Field -Field Excludes Another Field +# Field Excludes Another Field This does the same as excludes except for struct fields. It should only be used with string types. See the behavior of reflect.Value.String() for behavior on @@ -731,7 +729,7 @@ other types. Usage: excludesfield=InnerStructField.Field -Unique +# Unique For arrays & slices, unique will ensure that there are no duplicates. For maps, unique will ensure that there are no duplicate values. @@ -744,44 +742,44 @@ in a field of the struct specified via a parameter. // For slices of struct: Usage: unique=field -Alpha Only +# Alpha Only This validates that a string value contains ASCII alpha characters only Usage: alpha -Alphanumeric +# Alphanumeric This validates that a string value contains ASCII alphanumeric characters only Usage: alphanum -Alpha Unicode +# Alpha Unicode This validates that a string value contains unicode alpha characters only Usage: alphaunicode -Alphanumeric Unicode +# Alphanumeric Unicode This validates that a string value contains unicode alphanumeric characters only Usage: alphanumunicode -Boolean +# Boolean This validates that a string value can successfully be parsed into a boolean with strconv.ParseBool Usage: boolean -Number +# Number This validates that a string value contains number values only. For integers or float it returns true. Usage: number -Numeric +# Numeric This validates that a string value contains a basic numeric value. basic excludes exponents etc... @@ -789,63 +787,63 @@ for integers or float it returns true. Usage: numeric -Hexadecimal String +# Hexadecimal String This validates that a string value contains a valid hexadecimal. Usage: hexadecimal -Hexcolor String +# Hexcolor String This validates that a string value contains a valid hex color including hashtag (#) - Usage: hexcolor + Usage: hexcolor -Lowercase String +# Lowercase String This validates that a string value contains only lowercase characters. An empty string is not a valid lowercase string. Usage: lowercase -Uppercase String +# Uppercase String This validates that a string value contains only uppercase characters. An empty string is not a valid uppercase string. Usage: uppercase -RGB String +# RGB String This validates that a string value contains a valid rgb color Usage: rgb -RGBA String +# RGBA String This validates that a string value contains a valid rgba color Usage: rgba -HSL String +# HSL String This validates that a string value contains a valid hsl color Usage: hsl -HSLA String +# HSLA String This validates that a string value contains a valid hsla color Usage: hsla -E.164 Phone Number String +# E.164 Phone Number String This validates that a string value contains a valid E.164 Phone number https://en.wikipedia.org/wiki/E.164 (ex. +1123456789) Usage: e164 -E-mail String +# E-mail String This validates that a string value contains a valid email This may not conform to all possibilities of any rfc standard, but neither @@ -853,19 +851,19 @@ does any email provider accept all possibilities. Usage: email -JSON String +# JSON String This validates that a string value is valid JSON Usage: json -JWT String +# JWT String This validates that a string value is a valid JWT Usage: jwt -File path +# File path This validates that a string value contains a valid file path and that the file exists on the machine. @@ -873,7 +871,7 @@ This is done using os.Stat, which is a platform independent function. Usage: file -URL String +# URL String This validates that a string value contains a valid url This will accept any url the golang request uri accepts but must contain @@ -881,21 +879,21 @@ a schema for example http:// or rtmp:// Usage: url -URI String +# URI String This validates that a string value contains a valid uri This will accept any uri the golang request uri accepts Usage: uri -Urn RFC 2141 String +# Urn RFC 2141 String This validataes that a string value contains a valid URN according to the RFC 2141 spec. Usage: urn_rfc2141 -Base64 String +# Base64 String This validates that a string value contains a valid base64 value. Although an empty string is valid base64 this will report an empty string @@ -904,7 +902,7 @@ this with the omitempty tag. Usage: base64 -Base64URL String +# Base64URL String This validates that a string value contains a valid base64 URL safe value according the the RFC4648 spec. @@ -914,7 +912,17 @@ you can use this with the omitempty tag. Usage: base64url -Bitcoin Address +# Base64RawURL String + +This validates that a string value contains a valid base64 URL safe value, +but without = padding, according the the RFC4648 spec, section 3.2. +Although an empty string is a valid base64 URL safe value, this will report +an empty string as an error, if you wish to accept an empty string as valid +you can use this with the omitempty tag. + + Usage: base64url + +# Bitcoin Address This validates that a string value contains a valid bitcoin address. The format of the string is checked to ensure it matches one of the three formats @@ -930,266 +938,266 @@ Special thanks to Pieter Wuille for providng reference implementations. Usage: btc_addr_bech32 -Ethereum Address +# Ethereum Address This validates that a string value contains a valid ethereum address. The format of the string is checked to ensure it matches the standard Ethereum address format. Usage: eth_addr -Contains +# Contains This validates that a string value contains the substring value. Usage: contains=@ -Contains Any +# Contains Any This validates that a string value contains any Unicode code points in the substring value. Usage: containsany=!@#? -Contains Rune +# Contains Rune This validates that a string value contains the supplied rune value. Usage: containsrune=@ -Excludes +# Excludes This validates that a string value does not contain the substring value. Usage: excludes=@ -Excludes All +# Excludes All This validates that a string value does not contain any Unicode code points in the substring value. Usage: excludesall=!@#? -Excludes Rune +# Excludes Rune This validates that a string value does not contain the supplied rune value. Usage: excludesrune=@ -Starts With +# Starts With This validates that a string value starts with the supplied string value Usage: startswith=hello -Ends With +# Ends With This validates that a string value ends with the supplied string value Usage: endswith=goodbye -Does Not Start With +# Does Not Start With This validates that a string value does not start with the supplied string value Usage: startsnotwith=hello -Does Not End With +# Does Not End With This validates that a string value does not end with the supplied string value Usage: endsnotwith=goodbye -International Standard Book Number +# International Standard Book Number This validates that a string value contains a valid isbn10 or isbn13 value. Usage: isbn -International Standard Book Number 10 +# International Standard Book Number 10 This validates that a string value contains a valid isbn10 value. Usage: isbn10 -International Standard Book Number 13 +# International Standard Book Number 13 This validates that a string value contains a valid isbn13 value. Usage: isbn13 -Universally Unique Identifier UUID +# Universally Unique Identifier UUID This validates that a string value contains a valid UUID. Uppercase UUID values will not pass - use `uuid_rfc4122` instead. Usage: uuid -Universally Unique Identifier UUID v3 +# Universally Unique Identifier UUID v3 This validates that a string value contains a valid version 3 UUID. Uppercase UUID values will not pass - use `uuid3_rfc4122` instead. Usage: uuid3 -Universally Unique Identifier UUID v4 +# Universally Unique Identifier UUID v4 This validates that a string value contains a valid version 4 UUID. Uppercase UUID values will not pass - use `uuid4_rfc4122` instead. Usage: uuid4 -Universally Unique Identifier UUID v5 +# Universally Unique Identifier UUID v5 This validates that a string value contains a valid version 5 UUID. Uppercase UUID values will not pass - use `uuid5_rfc4122` instead. Usage: uuid5 -Universally Unique Lexicographically Sortable Identifier ULID +# 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. NOTE: if the string is blank, this validates as true. Usage: ascii -Printable ASCII +# Printable ASCII This validates that a string value contains only printable ASCII characters. NOTE: if the string is blank, this validates as true. Usage: printascii -Multi-Byte Characters +# Multi-Byte Characters This validates that a string value contains one or more multibyte characters. NOTE: if the string is blank, this validates as true. Usage: multibyte -Data URL +# Data URL This validates that a string value contains a valid DataURI. NOTE: this will also validate that the data portion is valid base64 Usage: datauri -Latitude +# Latitude This validates that a string value contains a valid latitude. Usage: latitude -Longitude +# Longitude This validates that a string value contains a valid longitude. Usage: longitude -Social Security Number SSN +# Social Security Number SSN This validates that a string value contains a valid U.S. Social Security Number. Usage: ssn -Internet Protocol Address IP +# Internet Protocol Address IP This validates that a string value contains a valid IP Address. Usage: ip -Internet Protocol Address IPv4 +# Internet Protocol Address IPv4 This validates that a string value contains a valid v4 IP Address. Usage: ipv4 -Internet Protocol Address IPv6 +# Internet Protocol Address IPv6 This validates that a string value contains a valid v6 IP Address. Usage: ipv6 -Classless Inter-Domain Routing CIDR +# Classless Inter-Domain Routing CIDR This validates that a string value contains a valid CIDR Address. Usage: cidr -Classless Inter-Domain Routing CIDRv4 +# Classless Inter-Domain Routing CIDRv4 This validates that a string value contains a valid v4 CIDR Address. Usage: cidrv4 -Classless Inter-Domain Routing CIDRv6 +# Classless Inter-Domain Routing CIDRv6 This validates that a string value contains a valid v6 CIDR Address. Usage: cidrv6 -Transmission Control Protocol Address TCP +# Transmission Control Protocol Address TCP This validates that a string value contains a valid resolvable TCP Address. Usage: tcp_addr -Transmission Control Protocol Address TCPv4 +# Transmission Control Protocol Address TCPv4 This validates that a string value contains a valid resolvable v4 TCP Address. Usage: tcp4_addr -Transmission Control Protocol Address TCPv6 +# Transmission Control Protocol Address TCPv6 This validates that a string value contains a valid resolvable v6 TCP Address. Usage: tcp6_addr -User Datagram Protocol Address UDP +# User Datagram Protocol Address UDP This validates that a string value contains a valid resolvable UDP Address. Usage: udp_addr -User Datagram Protocol Address UDPv4 +# User Datagram Protocol Address UDPv4 This validates that a string value contains a valid resolvable v4 UDP Address. Usage: udp4_addr -User Datagram Protocol Address UDPv6 +# User Datagram Protocol Address UDPv6 This validates that a string value contains a valid resolvable v6 UDP Address. Usage: udp6_addr -Internet Protocol Address IP +# Internet Protocol Address IP This validates that a string value contains a valid resolvable IP Address. Usage: ip_addr -Internet Protocol Address IPv4 +# Internet Protocol Address IPv4 This validates that a string value contains a valid resolvable v4 IP Address. Usage: ip4_addr -Internet Protocol Address IPv6 +# Internet Protocol Address IPv6 This validates that a string value contains a valid resolvable v6 IP Address. Usage: ip6_addr -Unix domain socket end point Address +# Unix domain socket end point Address This validates that a string value contains a valid Unix Address. Usage: unix_addr -Media Access Control Address MAC +# Media Access Control Address MAC This validates that a string value contains a valid MAC Address. @@ -1199,13 +1207,13 @@ Note: See Go's ParseMAC for accepted formats and types: http://golang.org/src/net/mac.go?s=866:918#L29 -Hostname RFC 952 +# Hostname RFC 952 This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952 Usage: hostname -Hostname RFC 1123 +# Hostname RFC 1123 This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123 @@ -1217,28 +1225,28 @@ This validates that a string value contains a valid FQDN. Usage: fqdn -HTML Tags +# HTML Tags This validates that a string value appears to be an HTML element tag including those described at https://developer.mozilla.org/en-US/docs/Web/HTML/Element Usage: html -HTML Encoded +# HTML Encoded This validates that a string value is a proper character reference in decimal or hexadecimal format Usage: html_encoded -URL Encoded +# URL Encoded This validates that a string value is percent-encoded (URL encoded) according to https://tools.ietf.org/html/rfc3986#section-2.1 Usage: url_encoded -Directory +# Directory This validates that a string value contains a valid directory and that it exists on the machine. @@ -1246,42 +1254,42 @@ This is done using os.Stat, which is a platform independent function. Usage: dir -HostPort +# HostPort This validates that a string value contains a valid DNS hostname and port that can be used to valiate fields typically passed to sockets and connections. Usage: hostname_port -Datetime +# Datetime This validates that a string value is a valid datetime based on the supplied datetime format. Supplied format must match the official Go time format layout as documented in https://golang.org/pkg/time/ Usage: datetime=2006-01-02 -Iso3166-1 alpha-2 +# Iso3166-1 alpha-2 This validates that a string value is a valid country code based on iso3166-1 alpha-2 standard. see: https://www.iso.org/iso-3166-country-codes.html Usage: iso3166_1_alpha2 -Iso3166-1 alpha-3 +# Iso3166-1 alpha-3 This validates that a string value is a valid country code based on iso3166-1 alpha-3 standard. see: https://www.iso.org/iso-3166-country-codes.html Usage: iso3166_1_alpha3 -Iso3166-1 alpha-numeric +# Iso3166-1 alpha-numeric This validates that a string value is a valid country code based on iso3166-1 alpha-numeric standard. see: https://www.iso.org/iso-3166-country-codes.html Usage: iso3166_1_alpha3 -BCP 47 Language Tag +# BCP 47 Language Tag This validates that a string value is a valid BCP 47 language tag, as parsed by language.Parse. More information on https://pkg.go.dev/golang.org/x/text/language @@ -1295,14 +1303,14 @@ More information on https://www.iso.org/standard/60390.html Usage: bic -RFC 1035 label +# 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. Although empty value and Local value are allowed by time.LoadLocation golang function, they are not allowed by this validator. @@ -1310,20 +1318,21 @@ More information on https://golang.org/pkg/time/#LoadLocation Usage: timezone -Semantic Version +# 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 +# Credit Card This validates that a string value contains a valid credit card number using Luhn algoritm. Usage: credit_card -Cron + +# Cron This validates that a string value contains a valid cron expression. @@ -1360,7 +1369,7 @@ Validator notes: And the best reason, you can submit a pull request and we can keep on adding to the validation library of this package! -Non standard validators +# Non standard validators A collection of validation rules that are frequently needed but are more complex than the ones found in the baked in validators. @@ -1389,7 +1398,7 @@ Here is a list of the current non standard validators: Usage: notblank -Panics +# Panics This package panics when bad input is provided, this is by design, bad code like that should not make it to production. diff --git a/regexes.go b/regexes.go index 5436e3f..029dd91 100644 --- a/regexes.go +++ b/regexes.go @@ -19,6 +19,7 @@ const ( e164RegexString = "^\\+[1-9]?[0-9]{7,14}$" base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$" + base64RawURLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2,4})$" iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$" iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$" uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" @@ -84,6 +85,7 @@ var ( emailRegex = regexp.MustCompile(emailRegexString) base64Regex = regexp.MustCompile(base64RegexString) base64URLRegex = regexp.MustCompile(base64URLRegexString) + base64RawURLRegex = regexp.MustCompile(base64RawURLRegexString) iSBN10Regex = regexp.MustCompile(iSBN10RegexString) iSBN13Regex = regexp.MustCompile(iSBN13RegexString) uUID3Regex = regexp.MustCompile(uUID3RegexString) diff --git a/validator.go b/validator.go index 80da095..6f6d53a 100644 --- a/validator.go +++ b/validator.go @@ -452,7 +452,6 @@ OUTER: v.ct = ct if !ct.fn(ctx, v) { - v.str1 = string(append(ns, cf.altName...)) if v.v.hasTagNameFunc { diff --git a/validator_instance.go b/validator_instance.go index 9493da4..51ae1aa 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -190,14 +190,14 @@ func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]int // // eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names: // -// validate.RegisterTagNameFunc(func(fld reflect.StructField) string { -// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] -// // skip if tag key says it should be ignored -// if name == "-" { -// return "" -// } -// return name -// }) +// validate.RegisterTagNameFunc(func(fld reflect.StructField) string { +// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] +// // skip if tag key says it should be ignored +// if name == "-" { +// return "" +// } +// return name +// }) func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { v.tagNameFunc = fn v.hasTagNameFunc = true @@ -613,7 +613,7 @@ func (v *Validate) Var(field interface{}, tag string) error { } // VarCtx validates a single variable using tag style validation and allows passing of contextual -// validation validation information via context.Context. +// validation information via context.Context. // eg. // var i int // validate.Var(i, "gt=1,lt=10") @@ -632,6 +632,7 @@ func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (e } ctag := v.fetchCacheTag(tag) + val := reflect.ValueOf(field) vd := v.pool.Get().(*validate) vd.top = val diff --git a/validator_test.go b/validator_test.go index fdf8a54..ec7e3aa 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5652,6 +5652,53 @@ func TestBase64URLValidation(t *testing.T) { } } +func TestBase64RawURLValidation(t *testing.T) { + validate := New() + + testCases := []struct { + decoded, encoded string + success bool + }{ + // empty string, although a valid base64 string, should fail + {"", "", false}, + // invalid length + {"", "a", false}, + // base64 with padding should fail + {"f", "Zg==", false}, + {"fo", "Zm8=", false}, + // base64 without padding + {"foo", "Zm9v", true}, + {"hello", "aGVsbG8", true}, + {"", "aGVsb", false}, + // // base64 URL safe encoding with invalid, special characters '+' and '/' + {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+", false}, + {"\x14\xfb\x9c\x03\xf9\x73", "FPucA/lz", false}, + // // base64 URL safe encoding with valid, special characters '-' and '_' + {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l-", true}, + {"\x14\xfb\x9c\x03\xf9\x73", "FPucA_lz", true}, + // non base64 characters + {"", "@mc=", false}, + {"", "Zm 9", false}, + } + for _, tc := range testCases { + err := validate.Var(tc.encoded, "base64rawurl") + if tc.success { + Equal(t, err, nil) + // make sure encoded value is decoded back to the expected value + d, innerErr := base64.RawURLEncoding.DecodeString(tc.encoded) + Equal(t, innerErr, nil) + Equal(t, tc.decoded, string(d)) + } else { + NotEqual(t, err, nil) + if len(tc.encoded) > 0 { + // make sure that indeed the encoded value was faulty + _, err := base64.RawURLEncoding.DecodeString(tc.encoded) + NotEqual(t, err, nil) + } + } + } +} + func TestFileValidation(t *testing.T) { validate := New() From f560fd4e07f7ea3d0b1f1e1d201bc70022c5b9bc Mon Sep 17 00:00:00 2001 From: just Alan Date: Mon, 20 Mar 2023 08:40:33 +1300 Subject: [PATCH 24/37] Feat: support mongodb objectID validation (#1023) Co-authored-by: Dean Karn --- README.md | 1 + baked_in.go | 7 +++++++ doc.go | 7 +++++++ regexes.go | 2 ++ validator_test.go | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 53 insertions(+) diff --git a/README.md b/README.md index 3d52c73..ef073fa 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ Baked-in Validations | btc_addr | Bitcoin Address | | btc_addr_bech32 | Bitcoin Bech32 Address (segwit) | | credit_card | Credit Card Number | +| mongodb | MongoDB ObjectID | | cron | Cron | | datetime | Datetime | | e164 | e164 formatted phone number | diff --git a/baked_in.go b/baked_in.go index 9ce2282..122f30f 100644 --- a/baked_in.go +++ b/baked_in.go @@ -219,6 +219,7 @@ var ( "semver": isSemverFormat, "dns_rfc1035_label": isDnsRFC1035LabelFormat, "credit_card": isCreditCard, + "mongodb": isMongoDB, "cron": isCron, } ) @@ -2567,6 +2568,12 @@ func isDnsRFC1035LabelFormat(fl FieldLevel) bool { return dnsRegexRFC1035Label.MatchString(val) } +// isMongoDB is the validation function for validating if the current field's value is valid mongoDB objectID +func isMongoDB(fl FieldLevel) bool { + val := fl.Field().String() + return mongodbRegex.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() diff --git a/doc.go b/doc.go index 2b44b53..e0b79b7 100644 --- a/doc.go +++ b/doc.go @@ -1332,6 +1332,13 @@ This validates that a string value contains a valid credit card number using Luh Usage: credit_card +#MongoDb ObjectID + +This validates that a string is a valid 24 character hexadecimal string. + + Usage: mongodb + + # Cron This validates that a string value contains a valid cron expression. diff --git a/regexes.go b/regexes.go index 029dd91..a65ac6f 100644 --- a/regexes.go +++ b/regexes.go @@ -65,6 +65,7 @@ const ( 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}$" + mongodbRegexString = "^[a-f\\d]{24}$" cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})` ) @@ -129,5 +130,6 @@ var ( bicRegex = regexp.MustCompile(bicRegexString) semverRegex = regexp.MustCompile(semverRegexString) dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label) + mongodbRegex = regexp.MustCompile(mongodbRegexString) cronRegex = regexp.MustCompile(cronRegexString) ) diff --git a/validator_test.go b/validator_test.go index ec7e3aa..8e1ea3e 100644 --- a/validator_test.go +++ b/validator_test.go @@ -12546,6 +12546,42 @@ func TestValidate_ValidateMapCtx(t *testing.T) { } } +func TestMongoDBObjectIDFormatValidation(t *testing.T) { + tests := []struct { + value string `validate:"mongodb"` + tag string + expected bool + }{ + {"507f191e810c19729de860ea", "mongodb", true}, + {"507f191e810c19729de860eG", "mongodb", false}, + {"M07f191e810c19729de860eG", "mongodb", false}, + {"07f191e810c19729de860ea", "mongodb", false}, + {"507f191e810c19729de860e", "mongodb", false}, + {"507f191e810c19729de860ea4", "mongodb", 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 mongodb failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d mongodb failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "mongodb" { + t.Fatalf("Index: %d mongodb failed Error: %s", i, errs) + } + } + } + } +} + func TestCreditCardFormatValidation(t *testing.T) { tests := []struct { value string `validate:"credit_card"` From 2e436710019c5f551adde73de2813a862598d96f Mon Sep 17 00:00:00 2001 From: brent s Date: Sun, 19 Mar 2023 15:47:10 -0400 Subject: [PATCH 25/37] (New validator) Validate non-existing but valid file/directory paths (#1022) --- README.md | 6 ++- baked_in.go | 119 ++++++++++++++++++++++++++++++++++++++++++++-- doc.go | 27 ++++++++++- validator_test.go | 88 ++++++++++++++++++++++++++++++---- 4 files changed, 226 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ef073fa..9c631d6 100644 --- a/README.md +++ b/README.md @@ -222,8 +222,10 @@ Baked-in Validations ### Other: | Tag | Description | | - | - | -| dir | Directory | -| file | File path | +| dir | Existing Directory | +| dirpath | Directory Path | +| file | Existing File | +| filepath | File Path | | isdefault | Is Default | | len | Length | | max | Maximum | diff --git a/baked_in.go b/baked_in.go index 122f30f..028bc84 100644 --- a/baked_in.go +++ b/baked_in.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io/fs" "net" "net/url" "os" @@ -14,13 +15,14 @@ import ( "strconv" "strings" "sync" + "syscall" "time" "unicode/utf8" "golang.org/x/crypto/sha3" "golang.org/x/text/language" - urn "github.com/leodido/go-urn" + "github.com/leodido/go-urn" ) // Func accepts a FieldLevel interface for all validation needs. The return @@ -127,6 +129,7 @@ var ( "uri": isURI, "urn_rfc2141": isUrnRFC2141, // RFC 2141 "file": isFile, + "filepath": isFilePath, "base64": isBase64, "base64url": isBase64URL, "base64rawurl": isBase64RawURL, @@ -199,6 +202,7 @@ var ( "html_encoded": isHTMLEncoded, "url_encoded": isURLEncoded, "dir": isDir, + "dirpath": isDirPath, "json": isJSON, "jwt": isJWT, "hostname_port": isHostnamePort, @@ -1464,7 +1468,7 @@ func isUrnRFC2141(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } -// isFile is the validation function for validating if the current field's value is a valid file path. +// isFile is the validation function for validating if the current field's value is a valid existing file path. func isFile(fl FieldLevel) bool { field := fl.Field() @@ -1481,6 +1485,57 @@ func isFile(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isFilePath is the validation function for validating if the current field's value is a valid file path. +func isFilePath(fl FieldLevel) bool { + + var exists bool + var err error + + field := fl.Field() + + // If it exists, it obviously is valid. + // This is done first to avoid code duplication and unnecessary additional logic. + if exists = isFile(fl); exists { + return true + } + + // It does not exist but may still be a valid filepath. + switch field.Kind() { + case reflect.String: + // Every OS allows for whitespace, but none + // let you use a file with no filename (to my knowledge). + // Unless you're dealing with raw inodes, but I digress. + if strings.TrimSpace(field.String()) == "" { + return false + } + // We make sure it isn't a directory. + if strings.HasSuffix(field.String(), string(os.PathSeparator)) { + return false + } + if _, err = os.Stat(field.String()); err != nil { + switch t := err.(type) { + case *fs.PathError: + if t.Err == syscall.EINVAL { + // It's definitely an invalid character in the filepath. + return false + } + // It could be a permission error, a does-not-exist error, etc. + // Out-of-scope for this validation, though. + return true + default: + // Something went *seriously* wrong. + /* + Per https://pkg.go.dev/os#Stat: + "If there is an error, it will be of type *PathError." + */ + panic(err) + } + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number. func isE164(fl FieldLevel) bool { return e164Regex.MatchString(fl.Field().String()) @@ -2354,7 +2409,7 @@ func isFQDN(fl FieldLevel) bool { return fqdnRegexRFC1123.MatchString(val) } -// isDir is the validation function for validating if the current field's value is a valid directory. +// isDir is the validation function for validating if the current field's value is a valid existing directory. func isDir(fl FieldLevel) bool { field := fl.Field() @@ -2370,6 +2425,64 @@ func isDir(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isDirPath is the validation function for validating if the current field's value is a valid directory. +func isDirPath(fl FieldLevel) bool { + + var exists bool + var err error + + field := fl.Field() + + // If it exists, it obviously is valid. + // This is done first to avoid code duplication and unnecessary additional logic. + if exists = isDir(fl); exists { + return true + } + + // It does not exist but may still be a valid path. + switch field.Kind() { + case reflect.String: + // Every OS allows for whitespace, but none + // let you use a dir with no name (to my knowledge). + // Unless you're dealing with raw inodes, but I digress. + if strings.TrimSpace(field.String()) == "" { + return false + } + if _, err = os.Stat(field.String()); err != nil { + switch t := err.(type) { + case *fs.PathError: + if t.Err == syscall.EINVAL { + // It's definitely an invalid character in the path. + return false + } + // It could be a permission error, a does-not-exist error, etc. + // Out-of-scope for this validation, though. + // Lastly, we make sure it is a directory. + if strings.HasSuffix(field.String(), string(os.PathSeparator)) { + return true + } else { + return false + } + default: + // Something went *seriously* wrong. + /* + Per https://pkg.go.dev/os#Stat: + "If there is an error, it will be of type *PathError." + */ + panic(err) + } + } + // We repeat the check here to make sure it is an explicit directory in case the above os.Stat didn't trigger an error. + if strings.HasSuffix(field.String(), string(os.PathSeparator)) { + return true + } else { + return false + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // isJSON is the validation function for validating if the current field's value is a valid json string. func isJSON(fl FieldLevel) bool { field := fl.Field() diff --git a/doc.go b/doc.go index e0b79b7..bffabc4 100644 --- a/doc.go +++ b/doc.go @@ -863,7 +863,8 @@ This validates that a string value is a valid JWT Usage: jwt -# File path + +# File This validates that a string value contains a valid file path and that the file exists on the machine. @@ -871,6 +872,16 @@ This is done using os.Stat, which is a platform independent function. Usage: file + +# File Path + +This validates that a string value contains a valid file path but does not +validate the existence of that file. +This is done using os.Stat, which is a platform independent function. + + Usage: filepath + + # URL String This validates that a string value contains a valid url @@ -912,6 +923,7 @@ you can use this with the omitempty tag. Usage: base64url + # Base64RawURL String This validates that a string value contains a valid base64 URL safe value, @@ -922,6 +934,7 @@ you can use this with the omitempty tag. Usage: base64url + # Bitcoin Address This validates that a string value contains a valid bitcoin address. @@ -1254,6 +1267,18 @@ This is done using os.Stat, which is a platform independent function. Usage: dir + +# Directory Path + +This validates that a string value contains a valid directory but does +not validate the existence of that directory. +This is done using os.Stat, which is a platform independent function. +It is safest to suffix the string with os.PathSeparator if the directory +may not exist at the time of validation. + + Usage: dirpath + + # HostPort This validates that a string value contains a valid DNS hostname and port that diff --git a/validator_test.go b/validator_test.go index 8e1ea3e..a4beb9f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "os" "path/filepath" "reflect" "strings" @@ -3819,12 +3820,14 @@ func TestDataURIValidation(t *testing.T) { {"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true}, {"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true}, {"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, - {"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + - "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + - "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + - "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + - "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + - "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true}, + { + "data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + + "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + + "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + + "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + + "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + + "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true, + }, {"data:image/png;base64,12345", false}, {"", false}, {"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, @@ -5732,6 +5735,39 @@ func TestFileValidation(t *testing.T) { }, "Bad field type int") } +func TestFilePathValidation(t *testing.T) { + validate := New() + + tests := []struct { + title string + param string + expected bool + }{ + {"empty filepath", "", false}, + {"valid filepath", filepath.Join("testdata", "a.go"), true}, + {"invalid filepath", filepath.Join("testdata", "no\000.go"), false}, + {"directory, not a filepath", "testdata" + string(os.PathSeparator), false}, + } + + for _, test := range tests { + errs := validate.Var(test.param, "filepath") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Test: '%s' failed Error: %s", test.title, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Test: '%s' failed Error: %s", test.title, errs) + } + } + } + + PanicMatches(t, func() { + _ = validate.Var(6, "filepath") + }, "Bad field type int") +} + func TestEthereumAddressValidation(t *testing.T) { validate := New() @@ -10569,6 +10605,40 @@ func TestDirValidation(t *testing.T) { }, "Bad field type int") } +func TestDirPathValidation(t *testing.T) { + validate := New() + + tests := []struct { + title string + param string + expected bool + }{ + {"empty dirpath", "", false}, + {"valid dirpath - exists", "testdata", true}, + {"valid dirpath - explicit", "testdatanoexist" + string(os.PathSeparator), true}, + {"invalid dirpath", "testdata\000" + string(os.PathSeparator), false}, + {"file, not a dirpath", filepath.Join("testdata", "a.go"), false}, + } + + for _, test := range tests { + errs := validate.Var(test.param, "dirpath") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Test: '%s' failed Error: %s", test.title, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Test: '%s' failed Error: %s", test.title, errs) + } + } + } + + PanicMatches(t, func() { + _ = validate.Var(6, "filepath") + }, "Bad field type int") +} + func TestStartsWithValidation(t *testing.T) { tests := []struct { Value string `validate:"startswith=(/^ヮ^)/*:・゚✧"` @@ -12361,10 +12431,12 @@ func TestPostCodeByIso3166Alpha2(t *testing.T) { {"00803", true}, {"1234567", false}, }, - "LC": { // not support regexp for post code + "LC": { + // not support regexp for post code {"123456", false}, }, - "XX": { // not support country + "XX": { + // not support country {"123456", false}, }, } From 2f3efda84fce2af18a7f9c1876668d7e0d38d0e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kmilo=20Denis=20Gonz=C3=A1lez?= Date: Sun, 19 Mar 2023 15:48:07 -0400 Subject: [PATCH 26/37] Added a genre enum to the user model and a custom validation (#1014) --- _examples/struct-level/main.go | 78 ++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/_examples/struct-level/main.go b/_examples/struct-level/main.go index 6002a3b..a16cf4e 100644 --- a/_examples/struct-level/main.go +++ b/_examples/struct-level/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "fmt" "reflect" "strings" @@ -8,6 +9,36 @@ import ( "github.com/go-playground/validator/v10" ) +type validationError struct { + Namespace string `json:"namespace"` // can differ when a custom TagNameFunc is registered or + Field string `json:"field"` // by passing alt name to ReportError like below + StructNamespace string `json:"structNamespace"` + StructField string `json:"structField"` + Tag string `json:"tag"` + ActualTag string `json:"actualTag"` + Kind string `json:"kind"` + Type string `json:"type"` + Value string `json:"value"` + Param string `json:"param"` + Message string `json:"message"` +} + +type Gender uint + +const ( + Male Gender = iota + 1 + Female + Intersex +) + +func (gender Gender) String() string { + terms := []string{"Male", "Female", "Intersex"} + if gender < Male || gender > Intersex { + return "unknown" + } + return terms[gender] +} + // User contains user information type User struct { FirstName string `json:"fname"` @@ -16,6 +47,7 @@ type User struct { Email string `json:"e-mail" validate:"required,email"` FavouriteColor string `validate:"hexcolor|rgb|rgba"` Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... + Gender Gender `json:"gender" validate:"required,gender_custom_validation"` } // Address houses a users address information @@ -47,6 +79,17 @@ func main() { // internally dereferences during it's type checks. validate.RegisterStructValidation(UserStructLevelValidation, User{}) + // register a custom validation for user genre on a line + // validates that an enum is within the interval + err := validate.RegisterValidation("gender_custom_validation", func(fl validator.FieldLevel) bool { + value := fl.Field().Interface().(Gender) + return value.String() != "unknown" + }) + if err != nil { + fmt.Println(err) + return + } + // build 'User' info, normally posted data etc... address := &Address{ Street: "Eavesdown Docks", @@ -65,7 +108,7 @@ func main() { } // returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError ) - err := validate.Struct(user) + err = validate.Struct(user) if err != nil { // this check is only needed when your code could produce @@ -77,18 +120,27 @@ func main() { } for _, err := range err.(validator.ValidationErrors) { - - fmt.Println(err.Namespace()) // can differ when a custom TagNameFunc is registered or - fmt.Println(err.Field()) // by passing alt name to ReportError like below - fmt.Println(err.StructNamespace()) - fmt.Println(err.StructField()) - fmt.Println(err.Tag()) - fmt.Println(err.ActualTag()) - fmt.Println(err.Kind()) - fmt.Println(err.Type()) - fmt.Println(err.Value()) - fmt.Println(err.Param()) - fmt.Println() + e := validationError{ + Namespace: err.Namespace(), + Field: err.Field(), + StructNamespace: err.StructNamespace(), + StructField: err.StructField(), + Tag: err.Tag(), + ActualTag: err.ActualTag(), + Kind: fmt.Sprintf("%v", err.Kind()), + Type: fmt.Sprintf("%v", err.Type()), + Value: fmt.Sprintf("%v", err.Value()), + Param: err.Param(), + Message: err.Error(), + } + + indent, err := json.MarshalIndent(e, "", " ") + if err != nil { + fmt.Println(err) + panic(err) + } + + fmt.Println(string(indent)) } // from here you can create your own error messages in whatever language you wish From 4f5cf1d4999d26ce4a80ac18b4c488ed0e147680 Mon Sep 17 00:00:00 2001 From: jnst Date: Mon, 20 Mar 2023 04:49:53 +0900 Subject: [PATCH 27/37] Update Japanese translations (#1013) * Update japanese translations for required_if, unique, json, jwt, lowercase, uppercase, datetime, postcode_iso3166_alpha2, postcode_iso3166_alpha2_field, boolean * Remove unnecessary blank lines --- translations/ja/ja.go | 114 ++++++++++----- translations/ja/ja_test.go | 287 +++++++++++++++++++++++-------------- 2 files changed, 256 insertions(+), 145 deletions(-) diff --git a/translations/ja/ja.go b/translations/ja/ja.go index 1cc67a4..4510649 100644 --- a/translations/ja/ja.go +++ b/translations/ja/ja.go @@ -16,7 +16,6 @@ import ( // RegisterDefaultTranslations registers a set of default translations // for all built in tag's in validator; you may add your own as desired. func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) { - translations := []struct { tag string translation string @@ -29,10 +28,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は必須フィールドです", override: false, }, + { + tag: "required_if", + translation: "{0}は必須フィールドです", + override: false, + }, { tag: "len", customRegisFunc: func(ut ut.Translator) (err error) { - if err = ut.Add("len-string", "{0}の長さは{1}でなければなりません", false); err != nil { return } @@ -64,7 +67,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er }, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - var err error var t string @@ -123,7 +125,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { tag: "min", customRegisFunc: func(ut ut.Translator) (err error) { - if err = ut.Add("min-string", "{0}の長さは少なくとも{1}はなければなりません", false); err != nil { return } @@ -155,7 +156,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er }, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - var err error var t string @@ -214,7 +214,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { tag: "max", customRegisFunc: func(ut ut.Translator) (err error) { - if err = ut.Add("max-string", "{0}の長さは最大でも{1}でなければなりません", false); err != nil { return } @@ -307,7 +306,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}と等しくありません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { fmt.Printf("warning: error translating FieldError: %#v", fe) @@ -391,7 +389,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { tag: "lt", customRegisFunc: func(ut ut.Translator) (err error) { - if err = ut.Add("lt-string", "{0}の長さは{1}よりも少なくなければなりません", false); err != nil { return } @@ -512,7 +509,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { tag: "lte", customRegisFunc: func(ut ut.Translator) (err error) { - if err = ut.Add("lte-string", "{0}の長さは最大でも{1}でなければなりません", false); err != nil { return } @@ -632,7 +628,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { tag: "gt", customRegisFunc: func(ut ut.Translator) (err error) { - if err = ut.Add("gt-string", "{0}の長さは{1}よりも多くなければなりません", false); err != nil { return } @@ -752,7 +747,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er { tag: "gte", customRegisFunc: func(ut ut.Translator) (err error) { - if err = ut.Add("gte-string", "{0}の長さは少なくとも{1}以上はなければなりません", false); err != nil { return } @@ -874,7 +868,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}と等しくなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -889,7 +882,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}と等しくなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -904,7 +896,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}とは異ならなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -919,7 +910,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}よりも大きくなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -934,7 +924,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}以上でなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -949,7 +938,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}よりも小さくなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -964,7 +952,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}以下でなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -979,7 +966,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}とは異ならなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -994,7 +980,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}よりも大きくなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1009,7 +994,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}以上でなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1024,7 +1008,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}よりも小さくなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1039,7 +1022,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は{1}以下でなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1124,7 +1106,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は'{1}'を含まなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1139,7 +1120,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は'{1}'の少なくとも1つを含まなければなりません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1154,7 +1134,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}には'{1}'というテキストを含むことはできません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1169,7 +1148,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}には'{1}'のどれも含めることはできません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1184,7 +1162,6 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}には'{1}'を含めることはできません", override: false, customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) @@ -1354,6 +1331,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0}は正しいMACアドレスを含まなければなりません", override: false, }, + { + tag: "unique", + translation: "{0}は一意な値のみを含まなければなりません", + override: false, + }, { tag: "iscolor", translation: "{0}は正しい色でなければなりません", @@ -1372,22 +1354,83 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "json", + translation: "{0}は正しいJSON文字列でなければなりません", + override: false, + }, + { + tag: "jwt", + translation: "{0}は正しいJWT文字列でなければなりません", + override: false, + }, + { + tag: "lowercase", + translation: "{0}は小文字でなければなりません", + override: false, + }, + { + tag: "uppercase", + translation: "{0}は大文字でなければなりません", + override: false, + }, + { + tag: "datetime", + translation: "{0}は{1}の書式と一致しません", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "postcode_iso3166_alpha2", + translation: "{0}は国名コード{1}の郵便番号形式と一致しません", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "postcode_iso3166_alpha2_field", + translation: "{0}は{1}フィールドで指定された国名コードの郵便番号形式と一致しません", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "boolean", + translation: "{0}は正しいブール値でなければなりません", + override: false, + }, } for _, t := range translations { if t.customTransFunc != nil && t.customRegisFunc != nil { - err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc) - } else if t.customTransFunc != nil && t.customRegisFunc == nil { - err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc) - } else if t.customTransFunc == nil && t.customRegisFunc != nil { - err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc) - } else { err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc) } @@ -1401,9 +1444,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er } func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc { - return func(ut ut.Translator) (err error) { - if err = ut.Add(tag, translation, override); err != nil { return } @@ -1415,7 +1456,6 @@ func registrationFunc(tag string, translation string, override bool) validator.R } func translateFunc(ut ut.Translator, fe validator.FieldError) string { - t, err := ut.T(fe.Tag(), fe.Field()) if err != nil { log.Printf("warning: error translating FieldError: %#v", fe) diff --git a/translations/ja/ja_test.go b/translations/ja/ja_test.go index 7950251..9fe1251 100644 --- a/translations/ja/ja_test.go +++ b/translations/ja/ja_test.go @@ -11,7 +11,6 @@ import ( ) func TestTranslations(t *testing.T) { - japanese := ja_locale.New() uni := ut.New(japanese, japanese) trans, _ := uni.GetTranslator("ja") @@ -28,117 +27,131 @@ func TestTranslations(t *testing.T) { GteCSFieldString string LtCSFieldString string LteCSFieldString string + RequiredIf 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"` + RequiredString string `validate:"required"` + RequiredNumber int `validate:"required"` + RequiredMultiple []string `validate:"required"` + RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"` + 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"` + JSONString string `validate:"json"` + JWTString string `validate:"jwt"` + LowercaseString string `validate:"lowercase"` + UppercaseString string `validate:"uppercase"` + Datetime string `validate:"datetime=2006-01-02"` + PostCode string `validate:"postcode_iso3166_alpha2=SG"` + PostCodeCountry string + PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + BooleanString string `validate:"boolean"` } var test Test @@ -181,10 +194,20 @@ func TestTranslations(t *testing.T) { test.MultiByte = "1234feerf" + test.LowercaseString = "ABCDEFG" + test.UppercaseString = "abcdefg" + 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" + test.BooleanString = "A" + + test.Inner.RequiredIf = "abcd" + err = validate.Struct(test) NotEqual(t, err, nil) @@ -575,6 +598,10 @@ func TestTranslations(t *testing.T) { ns: "Test.RequiredString", expected: "RequiredStringは必須フィールドです", }, + { + ns: "Test.RequiredIf", + expected: "RequiredIfは必須フィールドです", + }, { ns: "Test.RequiredNumber", expected: "RequiredNumberは必須フィールドです", @@ -619,6 +646,50 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfIntは[5 63]のうちのいずれかでなければなりません", }, + { + ns: "Test.UniqueSlice", + expected: "UniqueSliceは一意な値のみを含まなければなりません", + }, + { + ns: "Test.UniqueArray", + expected: "UniqueArrayは一意な値のみを含まなければなりません", + }, + { + ns: "Test.UniqueMap", + expected: "UniqueMapは一意な値のみを含まなければなりません", + }, + { + ns: "Test.JSONString", + expected: "JSONStringは正しいJSON文字列でなければなりません", + }, + { + ns: "Test.JWTString", + expected: "JWTStringは正しいJWT文字列でなければなりません", + }, + { + ns: "Test.LowercaseString", + expected: "LowercaseStringは小文字でなければなりません", + }, + { + ns: "Test.UppercaseString", + expected: "UppercaseStringは大文字でなければなりません", + }, + { + ns: "Test.Datetime", + expected: "Datetimeは2006-01-02の書式と一致しません", + }, + { + ns: "Test.PostCode", + expected: "PostCodeは国名コードSGの郵便番号形式と一致しません", + }, + { + ns: "Test.PostCodeByField", + expected: "PostCodeByFieldはPostCodeCountryフィールドで指定された国名コードの郵便番号形式と一致しません", + }, + { + ns: "Test.BooleanString", + expected: "BooleanStringは正しいブール値でなければなりません", + }, } for _, tt := range tests { From 21b88d1a2508bd449add2df77b4fb216a38b00a5 Mon Sep 17 00:00:00 2001 From: jnst Date: Mon, 20 Mar 2023 04:51:46 +0900 Subject: [PATCH 28/37] Fix the Japanese translations of gte and lte (#1012) --- translations/ja/ja.go | 8 ++++---- translations/ja/ja_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/translations/ja/ja.go b/translations/ja/ja.go index 4510649..cdc2934 100644 --- a/translations/ja/ja.go +++ b/translations/ja/ja.go @@ -137,7 +137,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } - if err = ut.Add("min-number", "{0}は{1}より大きくなければなりません", false); err != nil { + if err = ut.Add("min-number", "{0}は{1}以上でなければなりません", false); err != nil { return } @@ -226,7 +226,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } - if err = ut.Add("max-number", "{0}は{1}より小さくなければなりません", false); err != nil { + if err = ut.Add("max-number", "{0}は{1}以下でなければなりません", false); err != nil { return } @@ -521,7 +521,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } - if err = ut.Add("lte-number", "{0}は{1}より小さくなければなりません", false); err != nil { + if err = ut.Add("lte-number", "{0}は{1}以下でなければなりません", false); err != nil { return } @@ -759,7 +759,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return } - if err = ut.Add("gte-number", "{0}は{1}より大きくなければなりません", false); err != nil { + if err = ut.Add("gte-number", "{0}は{1}以上でなければなりません", false); err != nil { return } diff --git a/translations/ja/ja_test.go b/translations/ja/ja_test.go index 9fe1251..56e14cf 100644 --- a/translations/ja/ja_test.go +++ b/translations/ja/ja_test.go @@ -476,7 +476,7 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.GteNumber", - expected: "GteNumberは5.56より大きくなければなりません", + expected: "GteNumberは5.56以上でなければなりません", }, { ns: "Test.GteMultiple", @@ -508,7 +508,7 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.LteNumber", - expected: "LteNumberは5.56より小さくなければなりません", + expected: "LteNumberは5.56以下でなければなりません", }, { ns: "Test.LteMultiple", @@ -564,7 +564,7 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.MaxNumber", - expected: "MaxNumberは1,113.00より小さくなければなりません", + expected: "MaxNumberは1,113.00以下でなければなりません", }, { ns: "Test.MaxMultiple", @@ -576,7 +576,7 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.MinNumber", - expected: "MinNumberは1,113.00より大きくなければなりません", + expected: "MinNumberは1,113.00以上でなければなりません", }, { ns: "Test.MinMultiple", From ae3e7287630f7ea4f42942abe562e6e9ae6157da Mon Sep 17 00:00:00 2001 From: konstantin Date: Sun, 19 Mar 2023 21:04:10 +0100 Subject: [PATCH 30/37] Introduce Luhn Checksum Validation (#1009) --- README.md | 1 + baked_in.go | 63 ++++++++++++++++++++++++++++++++++------------- doc.go | 8 ++++++ validator_test.go | 45 +++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 9c631d6..acdf773 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ Baked-in Validations | jwt | JSON Web Token (JWT) | | latitude | Latitude | | longitude | Longitude | +| luhn_checksum | Luhn Algorithm Checksum (for strings and (u)int) | | postcode_iso3166_alpha2 | Postcode | | postcode_iso3166_alpha2_field | Postcode | | rgb | RGB String | diff --git a/baked_in.go b/baked_in.go index 028bc84..48d71b9 100644 --- a/baked_in.go +++ b/baked_in.go @@ -223,6 +223,7 @@ var ( "semver": isSemverFormat, "dns_rfc1035_label": isDnsRFC1035LabelFormat, "credit_card": isCreditCard, + "luhn_checksum": hasLuhnChecksum, "mongodb": isMongoDB, "cron": isCron, } @@ -2681,6 +2682,29 @@ func isDnsRFC1035LabelFormat(fl FieldLevel) bool { return dnsRegexRFC1035Label.MatchString(val) } +// digitsHaveLuhnChecksum returns true if and only if the last element of the given digits slice is the Luhn checksum of the previous elements +func digitsHaveLuhnChecksum(digits []string) bool { + size := len(digits) + sum := 0 + for i, digit := range digits { + 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 +} + // isMongoDB is the validation function for validating if the current field's value is valid mongoDB objectID func isMongoDB(fl FieldLevel) bool { val := fl.Field().String() @@ -2705,24 +2729,29 @@ func isCreditCard(fl FieldLevel) bool { 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 digitsHaveLuhnChecksum(ccDigits) +} + +// hasLuhnChecksum is the validation for validating if the current field's value has a valid Luhn checksum +func hasLuhnChecksum(fl FieldLevel) bool { + field := fl.Field() + var str string // convert to a string which will then be split into single digits; easier and more readable than shifting/extracting single digits from a number + switch field.Kind() { + case reflect.String: + str = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + str = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + str = strconv.FormatUint(field.Uint(), 10) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) } - return (sum % 10) == 0 + size := len(str) + if size < 2 { // there has to be at least one digit that carries a meaning + the checksum + return false + } + digits := strings.Split(str, "") + return digitsHaveLuhnChecksum(digits) } // isCron is the validation function for validating if the current field's value is a valid cron expression diff --git a/doc.go b/doc.go index bffabc4..66d5e98 100644 --- a/doc.go +++ b/doc.go @@ -1357,6 +1357,13 @@ This validates that a string value contains a valid credit card number using Luh Usage: credit_card +# Luhn Checksum + + Usage: luhn_checksum + +This validates that a string or (u)int value contains a valid checksum using the Luhn algorithm. + + #MongoDb ObjectID This validates that a string is a valid 24 character hexadecimal string. @@ -1372,6 +1379,7 @@ This validates that a string value contains a valid cron expression. Alias Validators and Tags +Alias Validators and Tags NOTE: When returning an error, the tag returned in "FieldError" will be the alias tag unless the dive tag is part of the alias. Everything after the dive tag is not reported as the alias tag. Also, the "ActualTag" in the before diff --git a/validator_test.go b/validator_test.go index a4beb9f..fddf79b 100644 --- a/validator_test.go +++ b/validator_test.go @@ -12692,6 +12692,51 @@ func TestCreditCardFormatValidation(t *testing.T) { } } +func TestLuhnChecksumValidation(t *testing.T) { + testsUint := []struct { + value interface{} `validate:"luhn_checksum"` // the type is interface{} because the luhn_checksum works on both strings and numbers + tag string + expected bool + }{ + {uint64(586824160825533338), "luhn_checksum", true}, // credit card numbers are just special cases of numbers with luhn checksum + {586824160825533338, "luhn_checksum", true}, + {"586824160825533338", "luhn_checksum", true}, + {uint64(586824160825533328), "luhn_checksum", false}, + {586824160825533328, "luhn_checksum", false}, + {"586824160825533328", "luhn_checksum", false}, + {10000000116, "luhn_checksum", true}, // but there may be shorter numbers (11 digits) + {"10000000116", "luhn_checksum", true}, + {10000000117, "luhn_checksum", false}, + {"10000000117", "luhn_checksum", false}, + {uint64(12345678123456789011), "luhn_checksum", true}, // or longer numbers (19 digits) + {"12345678123456789011", "luhn_checksum", true}, + {1, "luhn_checksum", false}, // single digits (checksum only) are not allowed + {"1", "luhn_checksum", false}, + {-10, "luhn_checksum", false}, // negative ints are not allowed + {"abcdefghijklmnop", "luhn_checksum", false}, + } + + validate := New() + + for i, test := range testsUint { + errs := validate.Var(test.value, test.tag) + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d luhn_checksum failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d luhn_checksum failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "luhn_checksum" { + t.Fatalf("Index: %d luhn_checksum failed Error: %s", i, errs) + } + } + } + } +} + func TestMultiOrOperatorGroup(t *testing.T) { tests := []struct { Value int `validate:"eq=1|gte=5,eq=1|lt=7"` From f6f934c4e9238275d58780d0bc4a61c51c1bacb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gatis=20R=C4=ABders?= Date: Sun, 19 Mar 2023 22:05:27 +0200 Subject: [PATCH 31/37] Added Latvian translations (#1005) --- translations/lv/lv.go | 1399 ++++++++++++++++++++++++++++++++++++ translations/lv/lv_test.go | 709 ++++++++++++++++++ 2 files changed, 2108 insertions(+) create mode 100644 translations/lv/lv.go create mode 100644 translations/lv/lv_test.go diff --git a/translations/lv/lv.go b/translations/lv/lv.go new file mode 100644 index 0000000..0804ff3 --- /dev/null +++ b/translations/lv/lv.go @@ -0,0 +1,1399 @@ +package lv + +import ( + "fmt" + "log" + "reflect" + "strconv" + "strings" + "time" + + "github.com/go-playground/locales" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" +) + +// RegisterDefaultTranslations registers a set of default translations +// for all built in tag's in validator; you may add your own as desired. +func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (err error) { + translations := []struct { + tag string + translation string + override bool + customRegisFunc validator.RegisterTranslationsFunc + customTransFunc validator.TranslationFunc + }{ + { + tag: "required", + translation: "{0} ir obligāts lauks", + override: false, + }, + { + tag: "required_if", + translation: "{0} ir obligāts lauks", + override: false, + }, + { + tag: "len", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("len-string", "{0} garumam jābūt {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("len-string-character", "{0} rakstu zīme", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("len-string-character", "{0} rakstu zīmes", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("len-number", "{0} vērtībai jābūt {1}", false); err != nil { + return + } + + if err = ut.Add("len-items", "{0} vērtībai jāsatur {1}", false); err != nil { + return + } + if err = ut.AddCardinal("len-items-item", "{0} elements", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("len-items-item", "{0} elementi", locales.PluralRuleOther, false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + + var digits uint64 + var kind reflect.Kind + + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err := strconv.ParseFloat(fe.Param(), 64) + if err != nil { + goto END + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + c, err = ut.C("len-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("len-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + c, err = ut.C("len-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("len-items", fe.Field(), c) + + default: + t, err = ut.T("len-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "min", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("min-string", "{0} garumam jābūt minimums {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("min-string-character", "{0} rakstu zīme", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("min-string-character", "{0} rakstu zīmes", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("min-number", "{0} vērtībai jābūt {1} vai lielākai", false); err != nil { + return + } + + if err = ut.Add("min-items", "{0} jāsatur minimums {1}", false); err != nil { + return + } + if err = ut.AddCardinal("min-items-item", "{0} elements", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("min-items-item", "{0} elementi", locales.PluralRuleOther, false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + + var digits uint64 + var kind reflect.Kind + + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err := strconv.ParseFloat(fe.Param(), 64) + if err != nil { + goto END + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + c, err = ut.C("min-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("min-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + c, err = ut.C("min-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("min-items", fe.Field(), c) + + default: + t, err = ut.T("min-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "max", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("max-string", "{0} vērtība pārsniedz maksimālo garumu {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("max-string-character", "{0} rakstu zīme", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("max-string-character", "{0} rakstu zīmes", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("max-number", "{0} vērtībai jābūt {1} vai mazākai", false); err != nil { + return + } + + if err = ut.Add("max-items", "{0} jāsatur maksimums {1}", false); err != nil { + return + } + if err = ut.AddCardinal("max-items-item", "{0} elements", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("max-items-item", "{0} elementi", locales.PluralRuleOther, false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + + var digits uint64 + var kind reflect.Kind + + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err := strconv.ParseFloat(fe.Param(), 64) + if err != nil { + goto END + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + c, err = ut.C("max-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("max-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + c, err = ut.C("max-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("max-items", fe.Field(), c) + + default: + t, err = ut.T("max-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eq", + translation: "{0} nav vienāds ar {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + fmt.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ne", + translation: "{0} nedrīkst būt vienāds ar {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + fmt.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "lt", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("lt-string", "{0} garumam jābūt mazākam par {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("lt-string-character", "{0} rakstu zīmi", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("lt-string-character", "{0} rakstu zīmēm", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lt-number", "{0} jābūt mazākam par {1}", false); err != nil { + return + } + + if err = ut.Add("lt-items", "{0} jāsatur mazāk par {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("lt-items-item", "{0} elements", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("lt-items-item", "{0} elementiem", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lt-datetime", "{0} jābūt mazākam par šī brīža Datumu un laiku", false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + var f64 float64 + var digits uint64 + var kind reflect.Kind + + fn := func() (err error) { + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err = strconv.ParseFloat(fe.Param(), 64) + + return + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("lt-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("lt-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("lt-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("lt-items", fe.Field(), c) + + case reflect.Struct: + if fe.Type() != reflect.TypeOf(time.Time{}) { + err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag()) + goto END + } + + t, err = ut.T("lt-datetime", fe.Field()) + + default: + err = fn() + if err != nil { + goto END + } + + t, err = ut.T("lt-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "lte", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("lte-string", "{0} garumam jābūt maksimums {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("lte-string-character", "{0} rakstu zīme", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("lte-string-character", "{0} rakstu zīmes", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lte-number", "{0} jābūt {1} vai mazākam", false); err != nil { + return + } + + if err = ut.Add("lte-items", "{0} jāsatur maksimums {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("lte-items-item", "{0} elements", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("lte-items-item", "{0} elementi", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("lte-datetime", "{0} jābūt mazākam par šī brīža Datumu un laiku vai vienādam", false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + var f64 float64 + var digits uint64 + var kind reflect.Kind + + fn := func() (err error) { + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err = strconv.ParseFloat(fe.Param(), 64) + + return + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("lte-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("lte-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("lte-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("lte-items", fe.Field(), c) + + case reflect.Struct: + if fe.Type() != reflect.TypeOf(time.Time{}) { + err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag()) + goto END + } + + t, err = ut.T("lte-datetime", fe.Field()) + + default: + err = fn() + if err != nil { + goto END + } + + t, err = ut.T("lte-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gt", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("gt-string", "{0} ir jābūt garākam par {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("gt-string-character", "{0} rakstu zīme", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("gt-string-character", "{0} rakstu zīmēm", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gt-number", "{0} jābūt lielākam par {1}", false); err != nil { + return + } + + if err = ut.Add("gt-items", "{0} jāsatur vairāk par {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("gt-items-item", "{0} elements", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("gt-items-item", "{0} elementiem", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gt-datetime", "{0} jābūt lielākam par šī brīža Datumu un laiku", false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + var f64 float64 + var digits uint64 + var kind reflect.Kind + + fn := func() (err error) { + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err = strconv.ParseFloat(fe.Param(), 64) + + return + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("gt-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("gt-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("gt-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("gt-items", fe.Field(), c) + + case reflect.Struct: + if fe.Type() != reflect.TypeOf(time.Time{}) { + err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag()) + goto END + } + + t, err = ut.T("gt-datetime", fe.Field()) + + default: + err = fn() + if err != nil { + goto END + } + + t, err = ut.T("gt-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gte", + customRegisFunc: func(ut ut.Translator) (err error) { + if err = ut.Add("gte-string", "{0} garumam jābūt minimums {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("gte-string-character", "{0} rakstu zīme", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("gte-string-character", "{0} rakstu zīmes", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gte-number", "{0} jābūt {1} vai lielākam", false); err != nil { + return + } + + if err = ut.Add("gte-items", "{0} jāsatur minimums {1}", false); err != nil { + return + } + + if err = ut.AddCardinal("gte-items-item", "{0} elements", locales.PluralRuleOne, false); err != nil { + return + } + + if err = ut.AddCardinal("gte-items-item", "{0} elementi", locales.PluralRuleOther, false); err != nil { + return + } + + if err = ut.Add("gte-datetime", "{0} jābūt lielākam par šī brīža Datumu un laiku vai vienādam", false); err != nil { + return + } + + return + }, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + var err error + var t string + var f64 float64 + var digits uint64 + var kind reflect.Kind + + fn := func() (err error) { + if idx := strings.Index(fe.Param(), "."); idx != -1 { + digits = uint64(len(fe.Param()[idx+1:])) + } + + f64, err = strconv.ParseFloat(fe.Param(), 64) + + return + } + + kind = fe.Kind() + if kind == reflect.Ptr { + kind = fe.Type().Elem().Kind() + } + + switch kind { + case reflect.String: + + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("gte-string-character", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("gte-string", fe.Field(), c) + + case reflect.Slice, reflect.Map, reflect.Array: + var c string + + err = fn() + if err != nil { + goto END + } + + c, err = ut.C("gte-items-item", f64, digits, ut.FmtNumber(f64, digits)) + if err != nil { + goto END + } + + t, err = ut.T("gte-items", fe.Field(), c) + + case reflect.Struct: + if fe.Type() != reflect.TypeOf(time.Time{}) { + err = fmt.Errorf("tag '%s' cannot be used on a struct type", fe.Tag()) + goto END + } + + t, err = ut.T("gte-datetime", fe.Field()) + + default: + err = fn() + if err != nil { + goto END + } + + t, err = ut.T("gte-number", fe.Field(), ut.FmtNumber(f64, digits)) + } + + END: + if err != nil { + fmt.Printf("warning: error translating FieldError: %s", err) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eqfield", + translation: "{0} jābūt vienādam ar {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "eqcsfield", + translation: "{0} jābūt vienādam ar {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "necsfield", + translation: "{0} nedrīkst būt vienāds ar {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtcsfield", + translation: "{0} jābūt lielākam par {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtecsfield", + translation: "{0} jābūt lielākam par {1} vai vienādam", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltcsfield", + translation: "{0} jābūt mazākam par {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltecsfield", + translation: "{0} jābūt mazākam par {1} vai vienādam", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "nefield", + translation: "{0} nedrīkst būt vienāds ar {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtfield", + translation: "{0} jābūt lielākam par {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "gtefield", + translation: "{0} jābūt lielākam par {1} vai vienādam", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltfield", + translation: "{0} jābūt mazākam par {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "ltefield", + translation: "{0} jābūt mazākam par {1} vai vienādam", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "alpha", + translation: "{0} jāsatur tikai simboli no alfabēta", + override: false, + }, + { + tag: "alphanum", + translation: "{0} jāsatur tikai simboli no alfabēta vai cipari (Alphanumeric)", + override: false, + }, + { + tag: "numeric", + translation: "{0} jāsatur tikai cipari", + override: false, + }, + { + tag: "number", + translation: "{0} jāsatur derīgs skaitlis", + override: false, + }, + { + tag: "hexadecimal", + translation: "{0} jābūt heksadecimālam skaitlim", + override: false, + }, + { + tag: "hexcolor", + translation: "{0} jābūt derīgai HEX krāsai", + override: false, + }, + { + tag: "rgb", + translation: "{0} jābūt derīgai RGB krāsai", + override: false, + }, + { + tag: "rgba", + translation: "{0} jābūt derīgai RGBA krāsai", + override: false, + }, + { + tag: "hsl", + translation: "{0} jābūt derīgai HSL krāsai", + override: false, + }, + { + tag: "hsla", + translation: "{0} jābūt derīgai HSLA krāsai", + override: false, + }, + { + tag: "e164", + translation: "{0} jābūt derīgam, pēc E.164 formatētam talruņa numuram", + override: false, + }, + { + tag: "email", + translation: "{0} jābūt derīgai e-pasta adresei", + override: false, + }, + { + tag: "url", + translation: "{0} jābūt derīgam URL", + override: false, + }, + { + tag: "uri", + translation: "{0} jābūt derīgam URI", + override: false, + }, + { + tag: "base64", + translation: "{0} jābūt derīgai Base64 virknei", + override: false, + }, + { + tag: "contains", + translation: "{0} jāsatur teksts '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "containsany", + translation: "{0} jāsatur minimums 1 no rakstu zīmēm '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludes", + translation: "{0} nedrīkst saturēt tekstu '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludesall", + translation: "{0} nedrīkst saturēt nevienu no sekojošām rakstu zīmēm '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "excludesrune", + translation: "{0} nedrīkst saturēt sekojošo '{1}'", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "isbn", + translation: "{0} jābūt derīgam ISBN numuram", + override: false, + }, + { + tag: "isbn10", + translation: "{0} jābūt derīgam ISBN-10 numuram", + override: false, + }, + { + tag: "isbn13", + translation: "{0} jābūt derīgam ISBN-13 numuram", + override: false, + }, + { + tag: "uuid", + translation: "{0} jābūt derīgam UUID", + override: false, + }, + { + tag: "uuid3", + translation: "{0} jābūt derīgam 3. versijas UUID", + override: false, + }, + { + tag: "uuid4", + translation: "{0} jābūt derīgam 4. versijas UUID", + override: false, + }, + { + tag: "uuid5", + translation: "{0} jābūt derīgam 5. versijas UUID", + override: false, + }, + { + tag: "ulid", + translation: "{0} jābūt derīgam ULID", + override: false, + }, + { + tag: "ascii", + translation: "{0} jāsatur tikai ascii rakstu zīmes", + override: false, + }, + { + tag: "printascii", + translation: "{0} jāsatur tikai drukājamas ascii rakstu zīmes", + override: false, + }, + { + tag: "multibyte", + translation: "{0} jāsatur multibyte rakstu zīmes", + override: false, + }, + { + tag: "datauri", + translation: "{0} jāsatur derīgs Data URI", + override: false, + }, + { + tag: "latitude", + translation: "{0} jāsatur derīgus platuma grādus", + override: false, + }, + { + tag: "longitude", + translation: "{0} jāsatur derīgus garuma grādus", + override: false, + }, + { + tag: "ssn", + translation: "{0} jābūt derīgam SSN numuram", + override: false, + }, + { + tag: "ipv4", + translation: "{0} jābūt derīgai IPv4 adresei", + override: false, + }, + { + tag: "ipv6", + translation: "{0} jābūt derīgai IPv6 adresei", + override: false, + }, + { + tag: "ip", + translation: "{0} jābūt derīgai IP adresei", + override: false, + }, + { + tag: "cidr", + translation: "{0} jāsatur derīgu CIDR notāciju", + override: false, + }, + { + tag: "cidrv4", + translation: "{0} jāsatur derīgu CIDR notāciju IPv4 adresei", + override: false, + }, + { + tag: "cidrv6", + translation: "{0} jāsatur derīgu CIDR notāciju IPv6 adresei", + override: false, + }, + { + tag: "tcp_addr", + translation: "{0} jābūt derīgai TCP adresei", + override: false, + }, + { + tag: "tcp4_addr", + translation: "{0} jābūt derīgai IPv4 TCP adresei", + override: false, + }, + { + tag: "tcp6_addr", + translation: "{0} jābūt derīgai IPv6 TCP adresei", + override: false, + }, + { + tag: "udp_addr", + translation: "{0} jābūt derīgai UDP adresei", + override: false, + }, + { + tag: "udp4_addr", + translation: "{0} jābūt derīgai IPv4 UDP adresei", + override: false, + }, + { + tag: "udp6_addr", + translation: "{0} jābūt derīgai IPv6 UDP adresei", + override: false, + }, + { + tag: "ip_addr", + translation: "{0} jābūt atrisināmai IP adresei", + override: false, + }, + { + tag: "ip4_addr", + translation: "{0} jābūt atrisināmai IPv4 adresei", + override: false, + }, + { + tag: "ip6_addr", + translation: "{0} jābūt atrisināmai IPv6 adresei", + override: false, + }, + { + tag: "unix_addr", + translation: "{0} jābūt atrisināmai UNIX adresei", + override: false, + }, + { + tag: "mac", + translation: "{0} jābūt derīgai MAC adresei", + override: false, + }, + { + tag: "unique", + translation: "{0} jāsatur unikālas vērtības", + override: false, + }, + { + tag: "iscolor", + translation: "{0} jābūt derīgai krāsai", + override: false, + }, + { + tag: "oneof", + translation: "{0} jābūt vienam no [{1}]", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + s, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + return s + }, + }, + { + tag: "json", + translation: "{0} jābūt derīgai json virknei", + override: false, + }, + { + tag: "jwt", + translation: "{0} jābūt derīgai jwt virknei", + override: false, + }, + { + tag: "lowercase", + translation: "{0} jābūt mazo burtu virknei", + override: false, + }, + { + tag: "uppercase", + translation: "{0} jābūt lielo burtu virknei", + override: false, + }, + { + tag: "datetime", + translation: "{0} neatbilst formātam {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "postcode_iso3166_alpha2", + translation: "{0} neatbilst pasta indeksa formātam valstī {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "postcode_iso3166_alpha2_field", + translation: "{0} neatbilst pasta indeksa formātam valstī, kura norādīta laukā {1}", + override: false, + customTransFunc: func(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field(), fe.Param()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t + }, + }, + { + tag: "boolean", + translation: "{0} jābūt derīgai boolean vērtībai", + override: false, + }, + } + + for _, t := range translations { + + if t.customTransFunc != nil && t.customRegisFunc != nil { + err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc) + } else if t.customTransFunc != nil && t.customRegisFunc == nil { + err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), t.customTransFunc) + } else if t.customTransFunc == nil && t.customRegisFunc != nil { + err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, translateFunc) + } else { + err = v.RegisterTranslation(t.tag, trans, registrationFunc(t.tag, t.translation, t.override), translateFunc) + } + + if err != nil { + return + } + } + + return +} + +func registrationFunc(tag string, translation string, override bool) validator.RegisterTranslationsFunc { + return func(ut ut.Translator) (err error) { + if err = ut.Add(tag, translation, override); err != nil { + return + } + + return + } +} + +func translateFunc(ut ut.Translator, fe validator.FieldError) string { + t, err := ut.T(fe.Tag(), fe.Field()) + if err != nil { + log.Printf("warning: error translating FieldError: %#v", fe) + return fe.(error).Error() + } + + return t +} diff --git a/translations/lv/lv_test.go b/translations/lv/lv_test.go new file mode 100644 index 0000000..ca99784 --- /dev/null +++ b/translations/lv/lv_test.go @@ -0,0 +1,709 @@ +package lv + +import ( + "testing" + "time" + + . "github.com/go-playground/assert/v2" + english "github.com/go-playground/locales/en" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" +) + +func TestTranslations(t *testing.T) { + eng := english.New() + uni := ut.New(eng, eng) + 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 + RequiredIf string + } + + type Test struct { + Inner Inner + RequiredString string `validate:"required"` + RequiredNumber int `validate:"required"` + RequiredMultiple []string `validate:"required"` + RequiredIf string `validate:"required_if=Inner.RequiredIf abcd"` + 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"` + JSONString string `validate:"json"` + JWTString string `validate:"jwt"` + LowercaseString string `validate:"lowercase"` + UppercaseString string `validate:"uppercase"` + Datetime string `validate:"datetime=2006-01-02"` + PostCode string `validate:"postcode_iso3166_alpha2=SG"` + PostCodeCountry string + PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + BooleanString string `validate:"boolean"` + } + + 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" + + 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" + test.BooleanString = "A" + + test.Inner.RequiredIf = "abcd" + + 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 jābūt derīgai krāsai", + }, + { + ns: "Test.MAC", + expected: "MAC jābūt derīgai MAC adresei", + }, + { + ns: "Test.IPAddr", + expected: "IPAddr jābūt atrisināmai IP adresei", + }, + { + ns: "Test.IPAddrv4", + expected: "IPAddrv4 jābūt atrisināmai IPv4 adresei", + }, + { + ns: "Test.IPAddrv6", + expected: "IPAddrv6 jābūt atrisināmai IPv6 adresei", + }, + { + ns: "Test.UDPAddr", + expected: "UDPAddr jābūt derīgai UDP adresei", + }, + { + ns: "Test.UDPAddrv4", + expected: "UDPAddrv4 jābūt derīgai IPv4 UDP adresei", + }, + { + ns: "Test.UDPAddrv6", + expected: "UDPAddrv6 jābūt derīgai IPv6 UDP adresei", + }, + { + ns: "Test.TCPAddr", + expected: "TCPAddr jābūt derīgai TCP adresei", + }, + { + ns: "Test.TCPAddrv4", + expected: "TCPAddrv4 jābūt derīgai IPv4 TCP adresei", + }, + { + ns: "Test.TCPAddrv6", + expected: "TCPAddrv6 jābūt derīgai IPv6 TCP adresei", + }, + { + ns: "Test.CIDR", + expected: "CIDR jāsatur derīgu CIDR notāciju", + }, + { + ns: "Test.CIDRv4", + expected: "CIDRv4 jāsatur derīgu CIDR notāciju IPv4 adresei", + }, + { + ns: "Test.CIDRv6", + expected: "CIDRv6 jāsatur derīgu CIDR notāciju IPv6 adresei", + }, + { + ns: "Test.SSN", + expected: "SSN jābūt derīgam SSN numuram", + }, + { + ns: "Test.IP", + expected: "IP jābūt derīgai IP adresei", + }, + { + ns: "Test.IPv4", + expected: "IPv4 jābūt derīgai IPv4 adresei", + }, + { + ns: "Test.IPv6", + expected: "IPv6 jābūt derīgai IPv6 adresei", + }, + { + ns: "Test.DataURI", + expected: "DataURI jāsatur derīgs Data URI", + }, + { + ns: "Test.Latitude", + expected: "Latitude jāsatur derīgus platuma grādus", + }, + { + ns: "Test.Longitude", + expected: "Longitude jāsatur derīgus garuma grādus", + }, + { + ns: "Test.MultiByte", + expected: "MultiByte jāsatur multibyte rakstu zīmes", + }, + { + ns: "Test.ASCII", + expected: "ASCII jāsatur tikai ascii rakstu zīmes", + }, + { + ns: "Test.PrintableASCII", + expected: "PrintableASCII jāsatur tikai drukājamas ascii rakstu zīmes", + }, + { + ns: "Test.UUID", + expected: "UUID jābūt derīgam UUID", + }, + { + ns: "Test.UUID3", + expected: "UUID3 jābūt derīgam 3. versijas UUID", + }, + { + ns: "Test.UUID4", + expected: "UUID4 jābūt derīgam 4. versijas UUID", + }, + { + ns: "Test.UUID5", + expected: "UUID5 jābūt derīgam 5. versijas UUID", + }, + { + ns: "Test.ULID", + expected: "ULID jābūt derīgam ULID", + }, + { + ns: "Test.ISBN", + expected: "ISBN jābūt derīgam ISBN numuram", + }, + { + ns: "Test.ISBN10", + expected: "ISBN10 jābūt derīgam ISBN-10 numuram", + }, + { + ns: "Test.ISBN13", + expected: "ISBN13 jābūt derīgam ISBN-13 numuram", + }, + { + ns: "Test.Excludes", + expected: "Excludes nedrīkst saturēt tekstu 'text'", + }, + { + ns: "Test.ExcludesAll", + expected: "ExcludesAll nedrīkst saturēt nevienu no sekojošām rakstu zīmēm '!@#$'", + }, + { + ns: "Test.ExcludesRune", + expected: "ExcludesRune nedrīkst saturēt sekojošo '☻'", + }, + { + ns: "Test.ContainsAny", + expected: "ContainsAny jāsatur minimums 1 no rakstu zīmēm '!@#$'", + }, + { + ns: "Test.Contains", + expected: "Contains jāsatur teksts 'purpose'", + }, + { + ns: "Test.Base64", + expected: "Base64 jābūt derīgai Base64 virknei", + }, + { + ns: "Test.Email", + expected: "Email jābūt derīgai e-pasta adresei", + }, + { + ns: "Test.URL", + expected: "URL jābūt derīgam URL", + }, + { + ns: "Test.URI", + expected: "URI jābūt derīgam URI", + }, + { + ns: "Test.RGBColorString", + expected: "RGBColorString jābūt derīgai RGB krāsai", + }, + { + ns: "Test.RGBAColorString", + expected: "RGBAColorString jābūt derīgai RGBA krāsai", + }, + { + ns: "Test.HSLColorString", + expected: "HSLColorString jābūt derīgai HSL krāsai", + }, + { + ns: "Test.HSLAColorString", + expected: "HSLAColorString jābūt derīgai HSLA krāsai", + }, + { + ns: "Test.HexadecimalString", + expected: "HexadecimalString jābūt heksadecimālam skaitlim", + }, + { + ns: "Test.HexColorString", + expected: "HexColorString jābūt derīgai HEX krāsai", + }, + { + ns: "Test.NumberString", + expected: "NumberString jāsatur derīgs skaitlis", + }, + { + ns: "Test.NumericString", + expected: "NumericString jāsatur tikai cipari", + }, + { + ns: "Test.AlphanumString", + expected: "AlphanumString jāsatur tikai simboli no alfabēta vai cipari (Alphanumeric)", + }, + { + ns: "Test.AlphaString", + expected: "AlphaString jāsatur tikai simboli no alfabēta", + }, + { + ns: "Test.LtFieldString", + expected: "LtFieldString jābūt mazākam par MaxString", + }, + { + ns: "Test.LteFieldString", + expected: "LteFieldString jābūt mazākam par MaxString vai vienādam", + }, + { + ns: "Test.GtFieldString", + expected: "GtFieldString jābūt lielākam par MaxString", + }, + { + ns: "Test.GteFieldString", + expected: "GteFieldString jābūt lielākam par MaxString vai vienādam", + }, + { + ns: "Test.NeFieldString", + expected: "NeFieldString nedrīkst būt vienāds ar EqFieldString", + }, + { + ns: "Test.LtCSFieldString", + expected: "LtCSFieldString jābūt mazākam par Inner.LtCSFieldString", + }, + { + ns: "Test.LteCSFieldString", + expected: "LteCSFieldString jābūt mazākam par Inner.LteCSFieldString vai vienādam", + }, + { + ns: "Test.GtCSFieldString", + expected: "GtCSFieldString jābūt lielākam par Inner.GtCSFieldString", + }, + { + ns: "Test.GteCSFieldString", + expected: "GteCSFieldString jābūt lielākam par Inner.GteCSFieldString vai vienādam", + }, + { + ns: "Test.NeCSFieldString", + expected: "NeCSFieldString nedrīkst būt vienāds ar Inner.NeCSFieldString", + }, + { + ns: "Test.EqCSFieldString", + expected: "EqCSFieldString jābūt vienādam ar Inner.EqCSFieldString", + }, + { + ns: "Test.EqFieldString", + expected: "EqFieldString jābūt vienādam ar MaxString", + }, + { + ns: "Test.GteString", + expected: "GteString garumam jābūt minimums 3 rakstu zīmes", + }, + { + ns: "Test.GteNumber", + expected: "GteNumber jābūt 5.56 vai lielākam", + }, + { + ns: "Test.GteMultiple", + expected: "GteMultiple jāsatur minimums 2 elementi", + }, + { + ns: "Test.GteTime", + expected: "GteTime jābūt lielākam par šī brīža Datumu un laiku vai vienādam", + }, + { + ns: "Test.GtString", + expected: "GtString ir jābūt garākam par 3 rakstu zīmēm", + }, + { + ns: "Test.GtNumber", + expected: "GtNumber jābūt lielākam par 5.56", + }, + { + ns: "Test.GtMultiple", + expected: "GtMultiple jāsatur vairāk par 2 elementiem", + }, + { + ns: "Test.GtTime", + expected: "GtTime jābūt lielākam par šī brīža Datumu un laiku", + }, + { + ns: "Test.LteString", + expected: "LteString garumam jābūt maksimums 3 rakstu zīmes", + }, + { + ns: "Test.LteNumber", + expected: "LteNumber jābūt 5.56 vai mazākam", + }, + { + ns: "Test.LteMultiple", + expected: "LteMultiple jāsatur maksimums 2 elementi", + }, + { + ns: "Test.LteTime", + expected: "LteTime jābūt mazākam par šī brīža Datumu un laiku vai vienādam", + }, + { + ns: "Test.LtString", + expected: "LtString garumam jābūt mazākam par 3 rakstu zīmēm", + }, + { + ns: "Test.LtNumber", + expected: "LtNumber jābūt mazākam par 5.56", + }, + { + ns: "Test.LtMultiple", + expected: "LtMultiple jāsatur mazāk par 2 elementiem", + }, + { + ns: "Test.LtTime", + expected: "LtTime jābūt mazākam par šī brīža Datumu un laiku", + }, + { + ns: "Test.NeString", + expected: "NeString nedrīkst būt vienāds ar ", + }, + { + ns: "Test.NeNumber", + expected: "NeNumber nedrīkst būt vienāds ar 0.00", + }, + { + ns: "Test.NeMultiple", + expected: "NeMultiple nedrīkst būt vienāds ar 0", + }, + { + ns: "Test.EqString", + expected: "EqString nav vienāds ar 3", + }, + { + ns: "Test.EqNumber", + expected: "EqNumber nav vienāds ar 2.33", + }, + { + ns: "Test.EqMultiple", + expected: "EqMultiple nav vienāds ar 7", + }, + { + ns: "Test.MaxString", + expected: "MaxString vērtība pārsniedz maksimālo garumu 3 rakstu zīmes", + }, + { + ns: "Test.MaxNumber", + expected: "MaxNumber vērtībai jābūt 1,113.00 vai mazākai", + }, + { + ns: "Test.MaxMultiple", + expected: "MaxMultiple jāsatur maksimums 7 elementi", + }, + { + ns: "Test.MinString", + expected: "MinString garumam jābūt minimums 1 rakstu zīme", + }, + { + ns: "Test.MinNumber", + expected: "MinNumber vērtībai jābūt 1,113.00 vai lielākai", + }, + { + ns: "Test.MinMultiple", + expected: "MinMultiple jāsatur minimums 7 elementi", + }, + { + ns: "Test.LenString", + expected: "LenString garumam jābūt 1 rakstu zīme", + }, + { + ns: "Test.LenNumber", + expected: "LenNumber vērtībai jābūt 1,113.00", + }, + { + ns: "Test.LenMultiple", + expected: "LenMultiple vērtībai jāsatur 7 elementi", + }, + { + ns: "Test.RequiredString", + expected: "RequiredString ir obligāts lauks", + }, + { + ns: "Test.RequiredIf", + expected: "RequiredIf ir obligāts lauks", + }, + { + ns: "Test.RequiredNumber", + expected: "RequiredNumber ir obligāts lauks", + }, + { + ns: "Test.RequiredMultiple", + expected: "RequiredMultiple ir obligāts lauks", + }, + { + ns: "Test.StrPtrMinLen", + expected: "StrPtrMinLen garumam jābūt minimums 10 rakstu zīmes", + }, + { + ns: "Test.StrPtrMaxLen", + expected: "StrPtrMaxLen vērtība pārsniedz maksimālo garumu 1 rakstu zīme", + }, + { + ns: "Test.StrPtrLen", + expected: "StrPtrLen garumam jābūt 2 rakstu zīmes", + }, + { + ns: "Test.StrPtrLt", + expected: "StrPtrLt garumam jābūt mazākam par 1 rakstu zīmi", + }, + { + ns: "Test.StrPtrLte", + expected: "StrPtrLte garumam jābūt maksimums 1 rakstu zīme", + }, + { + ns: "Test.StrPtrGt", + expected: "StrPtrGt ir jābūt garākam par 10 rakstu zīmēm", + }, + { + ns: "Test.StrPtrGte", + expected: "StrPtrGte garumam jābūt minimums 10 rakstu zīmes", + }, + { + ns: "Test.OneOfString", + expected: "OneOfString jābūt vienam no [red green]", + }, + { + ns: "Test.OneOfInt", + expected: "OneOfInt jābūt vienam no [5 63]", + }, + { + ns: "Test.UniqueSlice", + expected: "UniqueSlice jāsatur unikālas vērtības", + }, + { + ns: "Test.UniqueArray", + expected: "UniqueArray jāsatur unikālas vērtības", + }, + { + ns: "Test.UniqueMap", + expected: "UniqueMap jāsatur unikālas vērtības", + }, + { + ns: "Test.JSONString", + expected: "JSONString jābūt derīgai json virknei", + }, + { + ns: "Test.JWTString", + expected: "JWTString jābūt derīgai jwt virknei", + }, + { + ns: "Test.LowercaseString", + expected: "LowercaseString jābūt mazo burtu virknei", + }, + { + ns: "Test.UppercaseString", + expected: "UppercaseString jābūt lielo burtu virknei", + }, + { + ns: "Test.Datetime", + expected: "Datetime neatbilst formātam 2006-01-02", + }, + { + ns: "Test.PostCode", + expected: "PostCode neatbilst pasta indeksa formātam valstī SG", + }, + { + ns: "Test.PostCodeByField", + expected: "PostCodeByField neatbilst pasta indeksa formātam valstī, kura norādīta laukā PostCodeCountry", + }, + { + ns: "Test.BooleanString", + expected: "BooleanString jābūt derīgai boolean vērtībai", + }, + } + + 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)) + } +} From c6f86379834b301210fdd4c6c2a553c3b2717cae Mon Sep 17 00:00:00 2001 From: Aleksander Dejewski <87817186+adejewski-msol@users.noreply.github.com> Date: Mon, 20 Mar 2023 00:38:27 +0100 Subject: [PATCH 32/37] Fixed NotBlank validator to cover all whitespace characters (#997) --- non-standard/validators/notblank.go | 2 +- non-standard/validators/notblank_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/non-standard/validators/notblank.go b/non-standard/validators/notblank.go index 80c815f..2867243 100644 --- a/non-standard/validators/notblank.go +++ b/non-standard/validators/notblank.go @@ -14,7 +14,7 @@ func NotBlank(fl validator.FieldLevel) bool { switch field.Kind() { case reflect.String: - return len(strings.TrimSpace(field.String())) > 0 + return len(strings.Trim(strings.TrimSpace(field.String()), "\x1c\x1d\x1e\x1f")) > 0 case reflect.Chan, reflect.Map, reflect.Slice, reflect.Array: return field.Len() > 0 case reflect.Ptr, reflect.Interface, reflect.Func: diff --git a/non-standard/validators/notblank_test.go b/non-standard/validators/notblank_test.go index 78c8781..96c6720 100644 --- a/non-standard/validators/notblank_test.go +++ b/non-standard/validators/notblank_test.go @@ -3,8 +3,8 @@ package validators import ( "testing" - "github.com/go-playground/validator/v10" "github.com/go-playground/assert/v2" + "github.com/go-playground/validator/v10" ) type test struct { @@ -24,7 +24,7 @@ func TestNotBlank(t *testing.T) { // Errors var x *int invalid := test{ - String: " ", + String: " \x1c\x1d\x1e\x1f\r\n", Array: []int{}, Pointer: x, Number: 0, From 58d420de41816ab1547101ed9dd2e022b5e9fcb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Fonseca?= <56457144+Fonseca2570@users.noreply.github.com> Date: Sun, 19 Mar 2023 23:40:48 +0000 Subject: [PATCH 33/37] add kosovo (#1081) Co-authored-by: Fonseca --- country_codes.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/country_codes.go b/country_codes.go index 0d9eda0..b6c7a90 100644 --- a/country_codes.go +++ b/country_codes.go @@ -51,7 +51,7 @@ var iso3166_1_alpha2 = map[string]bool{ "TV": true, "UG": true, "UA": true, "AE": true, "GB": true, "US": true, "UM": true, "UY": true, "UZ": true, "VU": true, "VE": true, "VN": true, "VG": true, "VI": true, "WF": true, - "EH": true, "YE": true, "ZM": true, "ZW": true, + "EH": true, "YE": true, "ZM": true, "ZW": true, "XK": true, } var iso3166_1_alpha3 = map[string]bool{ @@ -105,7 +105,7 @@ var iso3166_1_alpha3 = map[string]bool{ "UGA": true, "UKR": true, "ARE": true, "GBR": true, "UMI": true, "USA": true, "URY": true, "UZB": true, "VUT": true, "VEN": true, "VNM": true, "VGB": true, "VIR": true, "WLF": true, "ESH": true, - "YEM": true, "ZMB": true, "ZWE": true, "ALA": true, + "YEM": true, "ZMB": true, "ZWE": true, "ALA": true, "UNK": true, } var iso3166_1_alpha_numeric = map[int]bool{ // see: https://www.iso.org/iso-3166-country-codes.html @@ -158,7 +158,7 @@ var iso3166_1_alpha_numeric = map[int]bool{ 800: true, 804: true, 784: true, 826: true, 581: true, 840: true, 858: true, 860: true, 548: true, 862: true, 704: true, 92: true, 850: true, 876: true, 732: true, - 887: true, 894: true, 716: true, 248: true, + 887: true, 894: true, 716: true, 248: true, 153:true, } var iso3166_2 = map[string]bool{ From b43d437012ec5766eee3a068f53c6581f8e64282 Mon Sep 17 00:00:00 2001 From: Eduardo Mello Date: Sun, 19 Mar 2023 20:55:24 -0300 Subject: [PATCH 34/37] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20cve=20validator?= =?UTF-8?q?=20(#983)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + baked_in.go | 8 ++++++ doc.go | 9 +++++++ regexes.go | 2 ++ translations/en/en.go | 5 ++++ translations/en/en_test.go | 6 +++++ translations/pt_BR/pt_BR.go | 5 ++++ translations/pt_BR/pt_BR_test.go | 6 +++++ validator_test.go | 44 ++++++++++++++++++++++++++++++++ 9 files changed, 86 insertions(+) diff --git a/README.md b/README.md index acdf773..06b0796 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ Baked-in Validations | tiger192 | TIGER192 hash | | semver | Semantic Versioning 2.0.0 | | ulid | Universally Unique Lexicographically Sortable Identifier ULID | +| cve | Common Vulnerabilities and Exposures Identifier (CVE id) | ### Comparisons: | Tag | Description | diff --git a/baked_in.go b/baked_in.go index 48d71b9..48a01ca 100644 --- a/baked_in.go +++ b/baked_in.go @@ -223,6 +223,7 @@ var ( "semver": isSemverFormat, "dns_rfc1035_label": isDnsRFC1035LabelFormat, "credit_card": isCreditCard, + "cve": isCveFormat, "luhn_checksum": hasLuhnChecksum, "mongodb": isMongoDB, "cron": isCron, @@ -2674,6 +2675,13 @@ func isSemverFormat(fl FieldLevel) bool { return semverRegex.MatchString(semverString) } +// isCveFormat is the validation function for validating if the current field's value is a valid cve id, defined in CVE mitre org +func isCveFormat(fl FieldLevel) bool { + cveString := fl.Field().String() + + return cveRegex.MatchString(cveString) +} + // isDnsRFC1035LabelFormat is the validation function // for validating if the current field's value is // a valid dns RFC 1035 label, defined in RFC 1035. diff --git a/doc.go b/doc.go index 66d5e98..4178462 100644 --- a/doc.go +++ b/doc.go @@ -1350,6 +1350,15 @@ More information on https://semver.org/ Usage: semver + +# CVE Identifier + +This validates that a string value is a valid cve id, defined in cve mitre. +More information on https://cve.mitre.org/ + + Usage: cve + + # Credit Card This validates that a string value contains a valid credit card number using Luhn algoritm. diff --git a/regexes.go b/regexes.go index a65ac6f..ba450b3 100644 --- a/regexes.go +++ b/regexes.go @@ -65,6 +65,7 @@ const ( 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}$" + cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html mongodbRegexString = "^[a-f\\d]{24}$" cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})` ) @@ -130,6 +131,7 @@ var ( bicRegex = regexp.MustCompile(bicRegexString) semverRegex = regexp.MustCompile(semverRegexString) dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label) + cveRegex = regexp.MustCompile(cveRegexString) mongodbRegex = regexp.MustCompile(mongodbRegexString) cronRegex = regexp.MustCompile(cronRegexString) ) diff --git a/translations/en/en.go b/translations/en/en.go index 0668bb2..486c95e 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1366,6 +1366,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} must be a valid boolean value", override: false, }, + { + tag: "cve", + translation: "{0} must be a valid cve identifier", + override: false, + }, } for _, t := range translations { diff --git a/translations/en/en_test.go b/translations/en/en_test.go index 69f4ee0..df07897 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -153,6 +153,7 @@ func TestTranslations(t *testing.T) { PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` BooleanString string `validate:"boolean"` + CveString string `validate:"cve"` } var test Test @@ -206,6 +207,7 @@ func TestTranslations(t *testing.T) { test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"} test.Datetime = "2008-Feb-01" test.BooleanString = "A" + test.CveString = "A" test.Inner.RequiredIf = "abcd" @@ -695,6 +697,10 @@ func TestTranslations(t *testing.T) { ns: "Test.BooleanString", expected: "BooleanString must be a valid boolean value", }, + { + ns: "Test.CveString", + expected: "CveString must be a valid cve identifier", + }, } for _, tt := range tests { diff --git a/translations/pt_BR/pt_BR.go b/translations/pt_BR/pt_BR.go index d6883aa..009646c 100644 --- a/translations/pt_BR/pt_BR.go +++ b/translations/pt_BR/pt_BR.go @@ -1321,6 +1321,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} deve ser um valor booleano válido", override: false, }, + { + tag: "cve", + translation: "{0} deve ser um identificador cve válido", + override: false, + }, } for _, t := range translations { diff --git a/translations/pt_BR/pt_BR_test.go b/translations/pt_BR/pt_BR_test.go index 426f246..d50ad9d 100644 --- a/translations/pt_BR/pt_BR_test.go +++ b/translations/pt_BR/pt_BR_test.go @@ -140,6 +140,7 @@ func TestTranslations(t *testing.T) { OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` BooleanString string `validate:"boolean"` + CveString string `validate:"cve"` } var test Test @@ -173,6 +174,7 @@ func TestTranslations(t *testing.T) { test.NumericString = "12E.00" test.NumberString = "12E" test.BooleanString = "A" + test.CveString = "A" test.Excludes = "este é um texto de teste" test.ExcludesAll = "Isso é Ótimo!" @@ -625,6 +627,10 @@ func TestTranslations(t *testing.T) { ns: "Test.BooleanString", expected: "BooleanString deve ser um valor booleano válido", }, + { + ns: "Test.CveString", + expected: "CveString deve ser um identificador cve válido", + }, } for _, tt := range tests { diff --git a/validator_test.go b/validator_test.go index fddf79b..bb94b3e 100644 --- a/validator_test.go +++ b/validator_test.go @@ -12374,6 +12374,50 @@ func TestSemverFormatValidation(t *testing.T) { } } +func TestCveFormatValidation(t *testing.T) { + + tests := []struct { + value string `validate:"cve"` + tag string + expected bool + }{ + {"CVE-1999-0001", "cve", true}, + {"CVE-1998-0001", "cve", false}, + {"CVE-2000-0001", "cve", true}, + {"CVE-2222-0001", "cve", true}, + {"2222-0001", "cve", false}, + {"-2222-0001", "cve", false}, + {"CVE22220001", "cve", false}, + {"CVE-2222-000001", "cve", false}, + {"CVE-2222-100001", "cve", true}, + {"CVE-2222-99999999999", "cve", true}, + {"CVE-3000-0001", "cve", false}, + {"CVE-1999-0000", "cve", false}, + {"CVE-2099-0000", "cve", 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 cve failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d cve failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "cve" { + t.Fatalf("Index: %d cve failed Error: %s", i, errs) + } + } + } + } +} + func TestRFC1035LabelFormatValidation(t *testing.T) { tests := []struct { value string `validate:"dns_rfc1035_label"` From e9f2d9f21a7123e5bdef611213d52a02098b66e1 Mon Sep 17 00:00:00 2001 From: Joselito Viveiros Nogueira Filho Date: Mon, 20 Mar 2023 00:06:42 +0000 Subject: [PATCH 35/37] fix: removed unnecessary cast so we can mock ValidationErrors (#980) (#981) --- errors.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/errors.go b/errors.go index 9a1b1ab..5856d57 100644 --- a/errors.go +++ b/errors.go @@ -44,12 +44,9 @@ func (ve ValidationErrors) Error() string { buff := bytes.NewBufferString("") - var fe *fieldError - for i := 0; i < len(ve); i++ { - fe = ve[i].(*fieldError) - buff.WriteString(fe.Error()) + buff.WriteString(ve[i].Error()) buff.WriteString("\n") } From 56e2f31322b7b169b067fceb3a48d9dc11f6f495 Mon Sep 17 00:00:00 2001 From: Cyro Dubeux Date: Sun, 19 Mar 2023 21:20:54 -0300 Subject: [PATCH 36/37] pt_BR translation improvements (#967) --- translations/pt_BR/pt_BR.go | 2 +- translations/pt_BR/pt_BR_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/translations/pt_BR/pt_BR.go b/translations/pt_BR/pt_BR.go index 009646c..91488e5 100644 --- a/translations/pt_BR/pt_BR.go +++ b/translations/pt_BR/pt_BR.go @@ -26,7 +26,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er }{ { tag: "required", - translation: "{0} é um campo requerido", + translation: "{0} é um campo obrigatório", override: false, }, { diff --git a/translations/pt_BR/pt_BR_test.go b/translations/pt_BR/pt_BR_test.go index d50ad9d..b149e1d 100644 --- a/translations/pt_BR/pt_BR_test.go +++ b/translations/pt_BR/pt_BR_test.go @@ -577,15 +577,15 @@ func TestTranslations(t *testing.T) { }, { ns: "Test.RequiredString", - expected: "RequiredString é um campo requerido", + expected: "RequiredString é um campo obrigatório", }, { ns: "Test.RequiredNumber", - expected: "RequiredNumber é um campo requerido", + expected: "RequiredNumber é um campo obrigatório", }, { ns: "Test.RequiredMultiple", - expected: "RequiredMultiple é um campo requerido", + expected: "RequiredMultiple é um campo obrigatório", }, { ns: "Test.StrPtrMinLen", From f5e5146b316bf0283fd8988dc9f26aebe94b599d Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 19 Mar 2023 17:23:43 -0700 Subject: [PATCH 37/37] update version README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 06b0796..84a4b80 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.11.2-green.svg) +[![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.12.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)