Skip to content

Commit 3eeb5cd

Browse files
Merge pull request #13 from zencoder/equals-validator
equals validator
2 parents 6b8b86a + b3af5bd commit 3eeb5cd

File tree

8 files changed

+253
-1
lines changed

8 files changed

+253
-1
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# gokay [![CircleCI](https://circleci.com/gh/zencoder/gokay.svg?style=svg&circle-token=90f42bc5cbb6fe74834f7649d67298130431d88d)](https://circleci.com/gh/zencoder/gokay) [![Coverage Status](https://coveralls.io/repos/github/zencoder/gokay/badge.svg?branch=circle-fixes&t=A2kWWv)](https://coveralls.io/github/zencoder/gokay?branch=circle-fixes)
1+
# gokay [![CircleCI](https://circleci.com/gh/zencoder/gokay.svg?style=svg&circle-token=90f42bc5cbb6fe74834f7649d67298130431d88d)](https://circleci.com/gh/zencoder/gokay) [![Coverage Status](https://coveralls.io/repos/github/zencoder/gokay/badge.svg?t=A2kWWv)](https://coveralls.io/github/zencoder/gokay)
22
Codegenerated struct validation tool for go.
33

44
## How it works
@@ -70,6 +70,8 @@ Hex | N/A | `(*)string` | Checks if a string field is a valid hexadecimal forma
7070
NotNil | N/A | pointers | Checks and fails if a pointer is nil
7171
Length | 1 | `(*)string` | Checks if a string's length matches the tag's parameter
7272
UUID | N/A | `(*)string` | Checks and fails if a string is not a valid UUID
73+
NotEqual | 1 | `(*)string` or `(*)number` | Checks and fails if field is equal to specific value
74+
Set | 1+ | `(*)string` | Checks and fails if field is not in a specific set of values
7375

7476
### Implicitly generated validations
7577
These sections of code will be added to the generated `Validate()` method regardless of a field's `valid` tag's contents.

gkgen/data_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,9 @@ type NotEqualTestPointerStruct struct {
5757
NotEqualComplex128 *complex128 `valid:"NotEqual=(0)"`
5858
NotEqualString string `valid:"NotEqual=(0)"`
5959
}
60+
61+
// SetTestStruct is used in the Set unit tests
62+
type SetTestStruct struct {
63+
SetString string `valid:"Set=(cat)(dog)(mouse)"`
64+
SetStringPtr *string `valid:"Set=(cat)(dog)(mouse)"`
65+
}

gkgen/generator.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ type ValidateGenerator struct {
2424
// - Hex: checks if a string is a valid hexadecimal format number
2525
// - Length: takes 1 integer argument and compares the length of a string field against that
2626
// - NotNil: Validate fails if field is nil
27+
// - Set: Validate fails if field is not in a specific set of values
2728
// - NotEqual: Validate fails if field is equal to specific value
2829
// - UUID: Checks and fails if a string is not a valid UUID
2930
func NewValidateGenerator() *ValidateGenerator {
3031
v := &ValidateGenerator{make(map[string]Generater)}
3132
v.AddValidation(NewNotNilValidator())
33+
v.AddValidation(NewSetValidator())
3234
v.AddValidation(NewNotEqualValidator())
3335
v.AddValidation(NewLengthValidator())
3436
v.AddValidation(NewHexValidator())

gkgen/set.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package gkgen
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"reflect"
7+
"strings"
8+
)
9+
10+
// SetValidator generates code that will verify a fields does Set one of an allowed set of values
11+
// The SetValidator will look at the field or the dereferenced value of the field
12+
// nil values for a field are not considered invalid
13+
type SetValidator struct {
14+
name string
15+
}
16+
17+
// NewSetValidator holds the SetValidator state
18+
func NewSetValidator() *SetValidator {
19+
return &SetValidator{name: "Set"}
20+
}
21+
22+
// Generate generates validation code
23+
func (s *SetValidator) Generate(sType reflect.Type, fieldStruct reflect.StructField, params []string) (string, error) {
24+
if len(params) == 0 {
25+
return "", errors.New("Set validation requires at least 1 parameter")
26+
}
27+
28+
field := fieldStruct.Type
29+
30+
switch field.Kind() {
31+
case reflect.String:
32+
conditions := make([]string, len(params))
33+
for i, param := range params {
34+
conditions[i] = fmt.Sprintf(`s.%[1]s == "%[2]s"`, fieldStruct.Name, param)
35+
}
36+
condition := strings.Join(conditions, " || ")
37+
return fmt.Sprintf(`
38+
if s.%[1]s != "" && !(%[2]s) {
39+
errors%[1]s = append(errors%[1]s, errors.New("%[1]s must equal %[3]s"))
40+
}`, fieldStruct.Name, condition, strings.Join(params, " or ")), nil
41+
case reflect.Ptr:
42+
field = field.Elem()
43+
switch field.Kind() {
44+
case reflect.String:
45+
conditions := make([]string, len(params))
46+
for i, param := range params {
47+
conditions[i] = fmt.Sprintf(`*s.%[1]s == "%[2]s"`, fieldStruct.Name, param)
48+
}
49+
condition := strings.Join(conditions, " || ")
50+
return fmt.Sprintf(`
51+
if s.%[1]s != nil && !(%[2]s) {
52+
errors%[1]s = append(errors%[1]s, errors.New("%[1]s must equal %[3]s"))
53+
}`, fieldStruct.Name, condition, strings.Join(params, " or ")), nil
54+
default:
55+
return "", fmt.Errorf("Set does not work on type '%s'", field.Kind())
56+
}
57+
default:
58+
return "", fmt.Errorf("Set does not work on type '%s'", field.Kind())
59+
}
60+
}
61+
62+
// Name provides access to the name field
63+
func (s *SetValidator) Name() string {
64+
return s.name
65+
}

gkgen/set_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package gkgen
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"strings"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestSet(t *testing.T) {
13+
nv := NewSetValidator()
14+
e := SetTestStruct{}
15+
et := reflect.TypeOf(e)
16+
17+
field, ok := et.FieldByName("SetString")
18+
require.True(t, ok)
19+
code, err := nv.Generate(et, field, []string{"cat"})
20+
require.NoError(t, err)
21+
code = strings.Replace(strings.TrimSpace(code), "\t", "", -1)
22+
require.Equal(
23+
t,
24+
fmt.Sprintf("if s.%[1]s != \"\" && !(s.%[1]s == \"cat\") {\nerrors%[1]s = append(errors%[1]s, errors.New(\"%[1]s must equal cat\"))\n}", field.Name),
25+
code,
26+
)
27+
28+
code, err = nv.Generate(et, field, []string{"cat", "dog", "mouse"})
29+
require.NoError(t, err)
30+
code = strings.Replace(strings.TrimSpace(code), "\t", "", -1)
31+
require.Equal(
32+
t,
33+
fmt.Sprintf("if s.%[1]s != \"\" && !(s.%[1]s == \"cat\" || s.%[1]s == \"dog\" || s.%[1]s == \"mouse\") {\nerrors%[1]s = append(errors%[1]s, errors.New(\"%[1]s must equal cat or dog or mouse\"))\n}", field.Name),
34+
code,
35+
)
36+
}
37+
38+
func TestSetPointer(t *testing.T) {
39+
nv := NewSetValidator()
40+
e := SetTestStruct{}
41+
et := reflect.TypeOf(e)
42+
43+
field, ok := et.FieldByName("SetStringPtr")
44+
require.True(t, ok)
45+
code, err := nv.Generate(et, field, []string{"cat"})
46+
require.NoError(t, err)
47+
code = strings.Replace(strings.TrimSpace(code), "\t", "", -1)
48+
require.Equal(
49+
t,
50+
fmt.Sprintf("if s.%[1]s != nil && !(*s.%[1]s == \"cat\") {\nerrors%[1]s = append(errors%[1]s, errors.New(\"%[1]s must equal cat\"))\n}", field.Name),
51+
code,
52+
)
53+
54+
code, err = nv.Generate(et, field, []string{"cat", "dog", "mouse"})
55+
require.NoError(t, err)
56+
code = strings.Replace(strings.TrimSpace(code), "\t", "", -1)
57+
require.Equal(
58+
t,
59+
fmt.Sprintf("if s.%[1]s != nil && !(*s.%[1]s == \"cat\" || *s.%[1]s == \"dog\" || *s.%[1]s == \"mouse\") {\nerrors%[1]s = append(errors%[1]s, errors.New(\"%[1]s must equal cat or dog or mouse\"))\n}", field.Name),
60+
code,
61+
)
62+
}
63+
64+
func TestSetInvalidTypes(t *testing.T) {
65+
nv := NewSetValidator()
66+
e := NotNilTestStruct{}
67+
et := reflect.TypeOf(e)
68+
69+
field, _ := et.FieldByName("NotNilMap")
70+
_, err := nv.Generate(et, field, []string{"0"})
71+
require.Error(t, err)
72+
require.Equal(
73+
t,
74+
"Set does not work on type 'map'",
75+
err.Error(),
76+
)
77+
78+
field, _ = et.FieldByName("NotNilSlice")
79+
_, err = nv.Generate(et, field, []string{"test"})
80+
require.Error(t, err)
81+
require.Equal(
82+
t,
83+
"Set does not work on type 'slice'",
84+
err.Error(),
85+
)
86+
}
87+
88+
func TestSetInvalidParameters(t *testing.T) {
89+
nv := NewSetValidator()
90+
e := SetTestStruct{}
91+
et := reflect.TypeOf(e)
92+
93+
field, _ := et.FieldByName("SetString")
94+
_, err := nv.Generate(et, field, []string{})
95+
require.Error(t, err)
96+
require.Equal(
97+
t,
98+
"Set validation requires at least 1 parameter",
99+
err.Error(),
100+
)
101+
}

internal/gkexample/example.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ type NotEqualTestStruct struct {
5757
NotEqualInt64Ptr *int64 `valid:"NotEqual=(7)"`
5858
}
5959

60+
type SetTestStruct struct {
61+
SetString string `valid:"Set=(cat)(dog)(mouse)"`
62+
SetStringPtr *string `valid:"Set=(cat)(dog)(mouse)"`
63+
}
64+
6065
type TestValidate struct {
6166
Valid bool
6267
}

internal/gkexample/example_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,3 +405,40 @@ func TestValidateNotEqual_Inalid(t *testing.T) {
405405
err := underTest.Validate()
406406
require.Equal(t, expected, err)
407407
}
408+
409+
func TestValidateSet_Valid(t *testing.T) {
410+
validCases := []string{"cat", "dog", "mouse"}
411+
412+
for _, tc := range validCases {
413+
underTest := SetTestStruct{
414+
SetString: tc,
415+
SetStringPtr: &tc,
416+
}
417+
418+
err := underTest.Validate()
419+
require.Nil(t, err)
420+
}
421+
}
422+
423+
func TestValidateSet_NilValid(t *testing.T) {
424+
underTest := SetTestStruct{}
425+
426+
err := underTest.Validate()
427+
require.Nil(t, err)
428+
}
429+
430+
func TestValidateSet_Inalid(t *testing.T) {
431+
expected := gokay.ErrorMap{
432+
"SetString": gokay.ErrorSlice{errors.New("SetString must equal cat or dog or mouse")},
433+
"SetStringPtr": gokay.ErrorSlice{errors.New("SetStringPtr must equal cat or dog or mouse")},
434+
}
435+
436+
gokay := "gokay"
437+
underTest := SetTestStruct{
438+
SetString: "Cat",
439+
SetStringPtr: &gokay,
440+
}
441+
442+
err := underTest.Validate()
443+
require.Equal(t, expected, err)
444+
}

internal/gkexample/example_validators.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,3 +470,37 @@ func (s NotNilTestStruct) Validate() error {
470470
}
471471

472472
}
473+
func (s SetTestStruct) Validate() error {
474+
em := make(gokay.ErrorMap)
475+
476+
// BEGIN SetString field Validations
477+
errorsSetString := make(gokay.ErrorSlice, 0, 0)
478+
// Set
479+
if s.SetString != "" && !(s.SetString == "cat" || s.SetString == "dog" || s.SetString == "mouse") {
480+
errorsSetString = append(errorsSetString, errors.New("SetString must equal cat or dog or mouse"))
481+
}
482+
483+
if len(errorsSetString) > 0 {
484+
em["SetString"] = errorsSetString
485+
}
486+
// END SetString field Validations
487+
488+
// BEGIN SetStringPtr field Validations
489+
errorsSetStringPtr := make(gokay.ErrorSlice, 0, 0)
490+
// Set
491+
if s.SetStringPtr != nil && !(*s.SetStringPtr == "cat" || *s.SetStringPtr == "dog" || *s.SetStringPtr == "mouse") {
492+
errorsSetStringPtr = append(errorsSetStringPtr, errors.New("SetStringPtr must equal cat or dog or mouse"))
493+
}
494+
495+
if len(errorsSetStringPtr) > 0 {
496+
em["SetStringPtr"] = errorsSetStringPtr
497+
}
498+
// END SetStringPtr field Validations
499+
500+
if len(em) > 0 {
501+
return em
502+
} else {
503+
return nil
504+
}
505+
506+
}

0 commit comments

Comments
 (0)