Skip to content

Commit 2f0fea0

Browse files
committed
sub validation and enum validation
1 parent 8564233 commit 2f0fea0

File tree

6 files changed

+205
-82
lines changed

6 files changed

+205
-82
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
*.jpg
1+
*.jpg
2+
bin/

README.md

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,46 +13,66 @@ import (
1313
)
1414

1515
func main() {
16+
type Children struct {
17+
Level string `json:"level" enum:"beginner,intermediate,advanced"`
18+
}
1619
type T struct {
17-
Username string `json:"username" validate:"required,email"`
18-
Password string `json:"password" validate:"min=20"`
19-
ConfirmPassword string `json:"confirm_password" validate:"eqfield=Password"`
20+
Username string `json:"username" validate:"required,email"`
21+
Password string `json:"password" validate:"min=20"`
22+
ConfirmPassword string `json:"confirm_password" validate:"eqfield=Password"`
23+
Children []Children `json:"children" validate:"required"`
2024
}
2125
data := T{
2226
Username: "",
2327
Password: "foo",
2428
ConfirmPassword: "bar",
29+
Children: []Children{
30+
{
31+
Level: "",
32+
},
33+
},
2534
}
26-
a := validation.New()
35+
v := validation.New()
2736
customMessage := map[string]string{
2837
"required": "your custom message",
2938
"min": "minimum {{.}} char",
3039
}
31-
a.SetLanguage(customMessage)
32-
ValidationErrors, err := a.Validate(data)
40+
v.SetLanguage(customMessage)
41+
ValidationErrorMessage, err := v.Validate(data)
3342
if err != nil {
34-
userJson, _ := json.Marshal(ValidationErrors)
43+
userJson, _ := json.Marshal(ValidationErrorMessage)
3544
fmt.Println(string(userJson))
3645
// [
37-
// {
38-
// "key": "username",
39-
// "message": [
40-
// "your message",
41-
// "This field must be a valid email address"
42-
// ]
43-
// },
44-
// {
45-
// "key": "password",
46-
// "message": [
47-
// "minimum 20 char"
48-
// ]
49-
// },
50-
// {
51-
// "key": "confirm_password",
52-
// "message": [
53-
// "This field must be a equal with password"
54-
// ]
55-
// }
46+
// {
47+
// "key": "username",
48+
// "message": [
49+
// "This field is required",
50+
// "This field must be a valid email address"
51+
// ]
52+
// },
53+
// {
54+
// "key": "password",
55+
// "message": [
56+
// "This field must be a minimum 20"
57+
// ]
58+
// },
59+
// {
60+
// "key": "confirm_password",
61+
// "message": [
62+
// "This field must be a equal with password"
63+
// ]
64+
// },
65+
// {
66+
// "key": "children",
67+
// "message": [
68+
// {
69+
// "key": "level",
70+
// "message": [
71+
// "This field must be one of [beginner,intermediate,advanced]"
72+
// ]
73+
// }
74+
// ]
75+
// }
5676
// ]
5777
}
5878
}
@@ -76,4 +96,5 @@ func main() {
7696
- date
7797
- min
7898
- max
79-
- eqfield
99+
- eqfield
100+
- enum

language.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ var (
2525
"minwidth": "This field must be a minimum width {{.}}",
2626
"maxhight": "This field must be a maximum hight {{.}}",
2727
"minhight": "This field must be a minimum hight {{.}}",
28+
"enum": "This field must be one of {{.}}",
2829
}
2930
)

new_validation.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package validation
22

3-
func Check(data interface{}) ([]*ValidationErrors, error) {
3+
func Check(data interface{}) ([]*ValidationErrorMessage, error) {
44
a := New()
55
return a.Validate(data)
66
}

validation.go

Lines changed: 78 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package validation
22

33
import (
44
"errors"
5+
"fmt"
56
"html/template"
67
"image"
78
"os"
89
"reflect"
910
"strconv"
1011
"strings"
12+
"time"
1113
)
1214

1315
type Validation struct {
@@ -31,10 +33,10 @@ func (s *Validation) SetLanguage(lang map[string]string) {
3133
s.Language = lang
3234
}
3335

34-
type ValidationErrors struct {
35-
Index string `json:"index,omitempty"`
36-
Key string `json:"key,omitempty"`
37-
Message []string `json:"message,omitempty"`
36+
type ValidationErrorMessage struct {
37+
Index string `json:"index,omitempty"`
38+
Field string `json:"key,omitempty"`
39+
Message []interface{} `json:"message,omitempty"`
3840
}
3941

4042
func format(s string, v interface{}) string {
@@ -45,24 +47,26 @@ func format(s string, v interface{}) string {
4547
return sb.String()
4648
}
4749

48-
func (s *Validation) Validate(data interface{}) ([]*ValidationErrors, error) {
50+
func (s *Validation) Validate(data interface{}) ([]*ValidationErrorMessage, error) {
4951
typeT := reflect.TypeOf(data)
50-
out := make([]*ValidationErrors, 0)
52+
typeV := reflect.ValueOf(data)
53+
out := make([]*ValidationErrorMessage, 0)
5154
for i := 0; i < typeT.NumField(); i++ {
52-
field := typeT.Field(i)
55+
fieldType := typeT.Field(i)
56+
fieldValue := typeV.Field(i)
5357
var key string
54-
if field.Tag.Get("json") == "" {
55-
key = field.Name
58+
if fieldType.Tag.Get("json") == "" {
59+
key = fieldType.Name
5660
} else {
57-
key = strings.Split(field.Tag.Get("json"), ",")[0]
61+
key = strings.Split(fieldType.Tag.Get("json"), ",")[0]
5862
}
59-
validate := field.Tag.Get("validate")
60-
if validate != "" {
61-
rules := strings.Split(validate, ",")
62-
formErr := new(ValidationErrors)
63-
msg := []string{}
63+
64+
formErr := new(ValidationErrorMessage)
65+
msg := []interface{}{}
66+
if validate := fieldType.Tag.Get("validate"); validate != "" {
67+
rules := strings.Split(strings.ReplaceAll(validate, " ", ""), ",")
6468
for _, rule := range rules {
65-
value := reflect.ValueOf(data).FieldByName(field.Name)
69+
value := reflect.ValueOf(data).FieldByName(fieldType.Name)
6670
rl := strings.Split(rule, "=")
6771
switch rl[0] {
6872
case "eqfield":
@@ -86,8 +90,34 @@ func (s *Validation) Validate(data interface{}) ([]*ValidationErrors, error) {
8690
}
8791
}
8892
case "required":
89-
if !isRequired(value) {
90-
msg = append(msg, s.Language["required"])
93+
switch value.Kind() {
94+
case reflect.Slice:
95+
if value.Len() == 0 {
96+
msg = append(msg, s.Language["required"])
97+
}
98+
for j := 0; j < value.Len(); j++ {
99+
validationErrorMessage, err := s.Validate(value.Index(j).Interface())
100+
if err != nil {
101+
for _, a := range validationErrorMessage {
102+
msg = append(msg, a)
103+
}
104+
}
105+
}
106+
case reflect.String:
107+
if !isRequired(value) {
108+
msg = append(msg, s.Language["required"])
109+
}
110+
case reflect.Struct:
111+
if value.Type() == reflect.TypeOf(time.Time{}) {
112+
if value.Interface().(time.Time).IsZero() {
113+
msg = append(msg, s.Language["required"])
114+
}
115+
}
116+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
117+
if value.Int() == 0 {
118+
msg = append(msg, s.Language["required"])
119+
// return fmt.Errorf("field %s is required and must not be 0", fieldType.Name)
120+
}
91121
}
92122
case "alpha":
93123
if !isAlpha(value) {
@@ -143,11 +173,20 @@ func (s *Validation) Validate(data interface{}) ([]*ValidationErrors, error) {
143173
}
144174
}
145175
}
146-
if len(msg) > 0 {
147-
formErr.Message = msg
148-
formErr.Key = key
149-
out = append(out, formErr)
176+
}
177+
if enumTag := fieldType.Tag.Get("enum"); enumTag != "" {
178+
allowedValues := map[string]bool{}
179+
for _, val := range split(enumTag, ",") {
180+
allowedValues[val] = true
150181
}
182+
if !allowedValues[fieldValue.String()] {
183+
msg = append(msg, format(s.Language["enum"], fmt.Sprintf("[%s]", enumTag)))
184+
}
185+
}
186+
if len(msg) > 0 {
187+
formErr.Message = msg
188+
formErr.Field = key
189+
out = append(out, formErr)
151190
}
152191
}
153192
if len(out) > 0 {
@@ -156,9 +195,9 @@ func (s *Validation) Validate(data interface{}) ([]*ValidationErrors, error) {
156195
return nil, nil
157196
}
158197

159-
func (s *Validation) FileValidate(f *os.File, rules string) (*ValidationErrors, error) {
160-
formErr := new(ValidationErrors)
161-
msg := []string{}
198+
func (s *Validation) FileValidate(f *os.File, rules string) (*ValidationErrorMessage, error) {
199+
formErr := new(ValidationErrorMessage)
200+
msg := []interface{}{}
162201

163202
for _, v := range strings.Split(rules, ",") {
164203
rv := strings.Split(v, "=")
@@ -203,8 +242,21 @@ func (s *Validation) FileValidate(f *os.File, rules string) (*ValidationErrors,
203242
}
204243
if len(msg) > 0 {
205244
formErr.Message = msg
206-
formErr.Key = f.Name()
245+
formErr.Field = f.Name()
207246
return formErr, errors.New("form error")
208247
}
209248
return nil, nil
210249
}
250+
251+
func split(s string, delim string) []string {
252+
var result []string
253+
start := 0
254+
for i := 0; i < len(s); i++ {
255+
if string(s[i]) == delim {
256+
result = append(result, s[start:i])
257+
start = i + 1
258+
}
259+
}
260+
result = append(result, s[start:])
261+
return result
262+
}

0 commit comments

Comments
 (0)