From daf1b48a0bfcac9156804c025ad22896b2f9d6f6 Mon Sep 17 00:00:00 2001 From: Cesar Cejudo Date: Thu, 3 Sep 2020 10:24:04 -0500 Subject: [PATCH 1/3] Support Required Any Kind of Slice --- validate.go | 34 +++++++++++++++++----------------- validate_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/validate.go b/validate.go index d4c54cd..7b3f608 100644 --- a/validate.go +++ b/validate.go @@ -239,31 +239,31 @@ func (v *Validator) Required(key string, value interface{}, message ...string) { if len(val) == 0 { v.Append(key, msg) } - case []string: - if len(val) == 0 { - v.Append(key, msg) + default: + vv := reflect.ValueOf(value) + if vv.Kind() == reflect.Ptr { + if value == reflect.Zero(vv.Type()).Interface() { + v.Append(key, msg) + } return } - // Make sure there is at least one non-empty entry. - nonEmpty := false - for i := range val { - if val[i] != "" { // Consider " " to be non-empty on purpose. - nonEmpty = true - break + if vv.Kind() == reflect.Slice { + if vv.Len() == 0 { + v.Append(key, msg) + return } - } - if !nonEmpty { - v.Append(key, msg) - } - default: - if vv := reflect.ValueOf(value); vv.Kind() == reflect.Ptr { - if value == reflect.Zero(vv.Type()).Interface() { - v.Append(key, msg) + for i := 0; i < vv.Len(); i++ { + if !vv.Index(i).IsZero() && !(vv.Kind() == reflect.Ptr && vv.Index(i).IsNil()) { + return + } } + + v.Append(key, msg) return } + panic(fmt.Sprintf("validate: not a supported type: %T", value)) } } diff --git a/validate_test.go b/validate_test.go index 5534c09..8d221e4 100644 --- a/validate_test.go +++ b/validate_test.go @@ -64,6 +64,32 @@ func TestRequiredString(t *testing.T) { } } +func TestRequiredSlice(t *testing.T) { + tests := []struct { + a interface{} + want bool + }{ + {[]struct{}{}, true}, + {[]struct{}{{}}, true}, + {[]*struct{}{nil}, true}, + {[]*struct{}{nil, {}}, false}, + {[]string{}, true}, + {[]string{""}, true}, + {[]string{"text"}, false}, + } + + for i, tt := range tests { + name := fmt.Sprintf("%v", i) + t.Run(name, func(t *testing.T) { + v := New() + v.Required(name, tt.a) + if got := v.HasErrors(); got != tt.want { + t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.want) + } + }) + } +} + func TestRequiredPtr(t *testing.T) { type customStruct struct { String string From d213f45088de86cda0b92c3ad8fd9d90c6203377 Mon Sep 17 00:00:00 2001 From: Cesar Cejudo Date: Thu, 3 Sep 2020 10:27:48 -0500 Subject: [PATCH 2/3] upgrade travis golang --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index df47605..64c0bfa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: go go: - - 1.11.x + - 1.14.x go_import_path: github.com/teamwork/validate notifications: email: false From 42f8571cd80f1089e1cfacc75b746feb4db95de6 Mon Sep 17 00:00:00 2001 From: Cesar Cejudo Date: Thu, 3 Sep 2020 10:44:33 -0500 Subject: [PATCH 3/3] remove Gopkg.* files change 'want' for 'hasErrors' for clarity upgrade to go1.5 --- .travis.yml | 2 +- Gopkg.lock | 81 ------------------------------------------------ Gopkg.toml | 30 ------------------ go.mod | 2 +- validate_test.go | 70 ++++++++++++++++++++--------------------- 5 files changed, 37 insertions(+), 148 deletions(-) delete mode 100644 Gopkg.lock delete mode 100644 Gopkg.toml diff --git a/.travis.yml b/.travis.yml index 64c0bfa..3cf3105 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: go go: - - 1.14.x + - 1.15.x go_import_path: github.com/teamwork/validate notifications: email: false diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 84ffa47..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,81 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:f9f45f75f332e03fc7e9fe9188ea4e1ce4d14779ef34fa1b023da67518e36327" - name = "github.com/google/go-cmp" - packages = [ - "cmp", - "cmp/internal/diff", - "cmp/internal/function", - "cmp/internal/value", - ] - pruneopts = "" - revision = "3af367b6b30c263d47e8895973edcca9a49cf029" - version = "v0.2.0" - -[[projects]] - digest = "1:8e3bd93036b4a925fe2250d3e4f38f21cadb8ef623561cd80c3c50c114b13201" - name = "github.com/hashicorp/errwrap" - packages = ["."] - pruneopts = "" - revision = "8a6fb523712970c966eefc6b39ed2c5e74880354" - version = "v1.0.0" - -[[projects]] - branch = "master" - digest = "1:72308fdd6d5ef61106a95be7ca72349a5565809042b6426a3cfb61d99483b824" - name = "github.com/hashicorp/go-multierror" - packages = ["."] - pruneopts = "" - revision = "886a7fbe3eb1c874d46f623bfa70af45f425b3d1" - -[[projects]] - branch = "master" - digest = "1:631151c2f67561bd1824b7fe8f23cf34a4951d7465380264eeb8a16fac05dd85" - name = "github.com/teamwork/mailaddress" - packages = ["."] - pruneopts = "" - revision = "e0bce973c1a85a932776b0aed8919ac86dcd82d6" - -[[projects]] - branch = "master" - digest = "1:ca24bfdf83d228ef34c43baa94a18942fbe783c4b631922c3fff945855798d37" - name = "github.com/teamwork/toutf8" - packages = ["."] - pruneopts = "" - revision = "908c4b127591e39bd79a8214ca158a4f8baff682" - -[[projects]] - digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" - name = "golang.org/x/text" - packages = [ - "encoding", - "encoding/charmap", - "encoding/ianaindex", - "encoding/internal", - "encoding/internal/identifier", - "encoding/japanese", - "encoding/korean", - "encoding/simplifiedchinese", - "encoding/traditionalchinese", - "encoding/unicode", - "internal/gen", - "internal/utf8internal", - "runes", - "transform", - "unicode/cldr", - ] - pruneopts = "" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/google/go-cmp/cmp", - "github.com/teamwork/mailaddress", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index ec13241..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,30 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - branch = "master" - name = "github.com/teamwork/mailaddress" - -[[constraint]] - name = "github.com/google/go-cmp" - version = "0.2.0" diff --git a/go.mod b/go.mod index fa25da2..4f65d9d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/teamwork/validate -go 1.12 +go 1.15 require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/validate_test.go b/validate_test.go index 8d221e4..9b4b0db 100644 --- a/validate_test.go +++ b/validate_test.go @@ -13,8 +13,8 @@ import ( func TestRequiredInt(t *testing.T) { tests := []struct { - a interface{} - want bool + a interface{} + hasErrors bool }{ {0, true}, {int64(0), true}, @@ -31,8 +31,8 @@ func TestRequiredInt(t *testing.T) { t.Run(name, func(t *testing.T) { v := New() v.Required(name, tt.a) - if got := v.HasErrors(); got != tt.want { - t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.want) + if got := v.HasErrors(); got != tt.hasErrors { + t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.hasErrors) } }) } @@ -43,8 +43,8 @@ func TestRequiredString(t *testing.T) { nonEmpty := "test" tests := []struct { - a interface{} - want bool + a interface{} + hasErrors bool }{ {empty, true}, {&empty, true}, @@ -57,8 +57,8 @@ func TestRequiredString(t *testing.T) { t.Run(name, func(t *testing.T) { v := New() v.Required(name, tt.a) - if got := v.HasErrors(); got != tt.want { - t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.want) + if got := v.HasErrors(); got != tt.hasErrors { + t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.hasErrors) } }) } @@ -66,8 +66,8 @@ func TestRequiredString(t *testing.T) { func TestRequiredSlice(t *testing.T) { tests := []struct { - a interface{} - want bool + a interface{} + hasErrors bool }{ {[]struct{}{}, true}, {[]struct{}{{}}, true}, @@ -83,8 +83,8 @@ func TestRequiredSlice(t *testing.T) { t.Run(name, func(t *testing.T) { v := New() v.Required(name, tt.a) - if got := v.HasErrors(); got != tt.want { - t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.want) + if got := v.HasErrors(); got != tt.hasErrors { + t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.hasErrors) } }) } @@ -99,8 +99,8 @@ func TestRequiredPtr(t *testing.T) { nonEmpty := &customStruct{} tests := []struct { - a interface{} - want bool + a interface{} + hasErrors bool }{ {empty, true}, {nonEmpty, false}, @@ -111,8 +111,8 @@ func TestRequiredPtr(t *testing.T) { t.Run(name, func(t *testing.T) { v := New() v.Required(name, tt.a) - if got := v.HasErrors(); got != tt.want { - t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.want) + if got := v.HasErrors(); got != tt.hasErrors { + t.Errorf("\ngot: %#v\nwant: %#v\n", got, tt.hasErrors) } }) } @@ -120,7 +120,7 @@ func TestRequiredPtr(t *testing.T) { func TestMerge(t *testing.T) { tests := []struct { - a, b, want map[string][]string + a, b, hasErrors map[string][]string }{ { map[string][]string{}, @@ -158,8 +158,8 @@ func TestMerge(t *testing.T) { in.Merge(other) - if !reflect.DeepEqual(tt.want, in.Errors) { - t.Errorf("\nout: %#v\nwant: %#v\n", in.Errors, tt.want) + if !reflect.DeepEqual(tt.hasErrors, in.Errors) { + t.Errorf("\nout: %#v\nwant: %#v\n", in.Errors, tt.hasErrors) } }) } @@ -223,8 +223,8 @@ func TestSub(t *testing.T) { func TestString(t *testing.T) { tests := []struct { - in Validator - want string + in Validator + hasErrors string }{ {Validator{}, ""}, {Validator{map[string][]string{}}, ""}, @@ -251,8 +251,8 @@ func TestString(t *testing.T) { for i, tt := range tests { t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { out := tt.in.String() - if out != tt.want { - t.Errorf("\nout: %#v\nwant: %#v\n", out, tt.want) + if out != tt.hasErrors { + t.Errorf("\nout: %#v\nwant: %#v\n", out, tt.hasErrors) } }) } @@ -567,11 +567,11 @@ func TestValidators(t *testing.T) { }, { func(v Validator) { v.URL("v", "ex ample.com") }, - map[string][]string{"v": {"must be a valid url: parse http://ex%20ample.com: invalid URL escape \"%20\""}}, + map[string][]string{"v": {"must be a valid url: parse \"http://ex%20ample.com\": invalid URL escape \"%20\""}}, }, { func(v Validator) { v.URL("v", "unknown_schema://x.com") }, - map[string][]string{"v": {"must be a valid url: parse unknown_schema://x.com: " + + map[string][]string{"v": {"must be a valid url: parse \"unknown_schema://x.com\": " + "first path segment in URL cannot contain colon"}}, }, @@ -735,7 +735,7 @@ func TestValidators(t *testing.T) { func TestInteger(t *testing.T) { tests := []struct { val func(Validator) int64 - want int64 + hasErrors int64 wantErrors map[string][]string }{ { @@ -784,8 +784,8 @@ func TestInteger(t *testing.T) { t.Errorf("\nout: %#v\nwant: %#v\n", v.Errors, tt.wantErrors) } - if i != tt.want { - t.Errorf("\nout: %#v\nwant: %#v\n", i, tt.want) + if i != tt.hasErrors { + t.Errorf("\nout: %#v\nwant: %#v\n", i, tt.hasErrors) } }) } @@ -794,7 +794,7 @@ func TestInteger(t *testing.T) { func TestBoolean(t *testing.T) { tests := []struct { val func(Validator) bool - want bool + hasErrors bool wantErrors map[string][]string }{ { @@ -878,8 +878,8 @@ func TestBoolean(t *testing.T) { t.Errorf("\nout: %#v\nwant: %#v\n", v.Errors, tt.wantErrors) } - if i != tt.want { - t.Errorf("\nout: %#v\nwant: %#v\n", i, tt.want) + if i != tt.hasErrors { + t.Errorf("\nout: %#v\nwant: %#v\n", i, tt.hasErrors) } }) } @@ -887,8 +887,8 @@ func TestBoolean(t *testing.T) { func TestErrorOrNil(t *testing.T) { tests := []struct { - in *Validator - want error + in *Validator + hasErrors error }{ {&Validator{}, nil}, {&Validator{Errors: map[string][]string{}}, nil}, @@ -901,8 +901,8 @@ func TestErrorOrNil(t *testing.T) { for i, tt := range tests { t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { got := tt.in.ErrorOrNil() - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("\nout: %#v\nwant: %#v\n", got, tt.want) + if !reflect.DeepEqual(got, tt.hasErrors) { + t.Errorf("\nout: %#v\nwant: %#v\n", got, tt.hasErrors) } }) }