-
Notifications
You must be signed in to change notification settings - Fork 0
/
validator.go
182 lines (139 loc) · 5.1 KB
/
validator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package validator
import (
"reflect"
"regexp"
)
type Scheme map[string]Validation
type Validation struct {
Validators []*func(string, interface{}) (string, bool, bool)
}
type ValidationScheme struct {
validation map[string]Validation
forbidFieldsIfNotInScheme bool
}
// Create new scheme validator
// forbidFieldsIfNotInScheme - if true, then validator will throw
// an error if scheme doesn't has some field from struct
func CreateValidationScheme(forbidFieldsIfNotInScheme bool, scheme map[string]Validation) *ValidationScheme {
return &ValidationScheme{
validation: scheme,
forbidFieldsIfNotInScheme: forbidFieldsIfNotInScheme,
}
}
// Create new validation ruler
func GetValidation() *Validation {
return &Validation{}
}
// Returns true if field is string
func (v *Validation) String(message string) *Validation {
checkFunction := func(fieldType string, fieldValue interface{}) (string, bool, bool) {
return message, fieldType == "string", true
}
v.Validators = append(v.Validators, &checkFunction)
return v
}
// Returns true if string length is lower or equal provided length
func (v *Validation) LenLte(length int, message string) *Validation {
checkFunction := func(fieldType string, fieldValue interface{}) (string, bool, bool) {
return message, fieldType == "string" && len(fieldValue.(string)) <= length, false
}
v.Validators = append(v.Validators, &checkFunction)
return v
}
// Returns true if string length is greeter or equal provided length
func (v *Validation) LenGte(length int, message string) *Validation {
checkFunction := func(fieldType string, fieldValue interface{}) (string, bool, bool) {
return message, fieldType == "string" && len(fieldValue.(string)) >= length, false
}
v.Validators = append(v.Validators, &checkFunction)
return v
}
// Returns true if string length is equal @param length
func (v *Validation) Len(length int, message string) *Validation {
checkFunction := func(fieldType string, fieldValue interface{}) (string, bool, bool) {
return message, fieldType == "string" && len(fieldValue.(string)) == length, false
}
v.Validators = append(v.Validators, &checkFunction)
return v
}
// Returns true if string is not empty
func (v *Validation) NotEmpty(message string) *Validation {
checkFunction := func(fieldType string, fieldValue interface{}) (string, bool, bool) {
return message, fieldType == "string" && len(fieldValue.(string)) > 0, false
}
v.Validators = append(v.Validators, &checkFunction)
return v
}
var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
// Returns true if string is email
func (v *Validation) Email(message string) *Validation {
checkFunction := func(fieldType string, fieldValue interface{}) (string, bool, bool) {
return message, fieldType == "string" && emailRegexp.MatchString(fieldValue.(string)), false
}
v.Validators = append(v.Validators, &checkFunction)
return v
}
// Add custom validation function.
// validateFunc must return 3 parameters (string (message of an error), bool (isValid),
// bool (immediateStop (if true then validation will stop checking other validation functions)))
func (v *Validation) CustomValidator(validateFunc *func(string, interface{}) (string, bool, bool)) *Validation {
v.Validators = append(v.Validators, validateFunc)
return v
}
func addError(fieldName, msg string, errors map[string][]string) map[string][]string {
if errors == nil {
errors = make(map[string][]string)
}
errors[fieldName] = append(errors[fieldName], msg)
return errors
}
func ValidateVar(val interface{}, scheme Validation) (map[string][]string, bool) {
var errors map[string][]string
structVal := reflect.TypeOf(val)
fieldType := structVal.String()
fieldValue := val
for _, validator := range scheme.Validators {
msg, valid, immediateStop := (*validator)(fieldType, fieldValue)
if !valid {
errors = addError("val", msg, errors)
if immediateStop {
break
}
}
}
return errors, errors == nil
}
// Validate scheme with given rules
// First return value - map of field with an array of error messages | nil
// Seconds return value - bool, true if no errors
func (vs *ValidationScheme) Validate(structure interface{}) (map[string][]string, bool) {
var errors map[string][]string
structVal := reflect.ValueOf(structure)
if structVal.Kind() != reflect.Ptr {
panic("structure parameter must be a pointer to struct (*)")
}
e := structVal.Elem()
for i := 0; i < e.NumField(); i++ {
field := e.Type().Field(i)
fieldName := field.Name
fieldType := field.Type.String()
fieldValue := e.Field(i).Interface()
validators, fieldExists := vs.validation[fieldName]
if !fieldExists {
if vs.forbidFieldsIfNotInScheme {
errors = addError(fieldName, "not allowed in this struct", errors)
}
continue
}
for _, validator := range validators.Validators {
msg, valid, immediateStop := (*validator)(fieldType, fieldValue)
if !valid {
errors = addError(fieldName, msg, errors)
if immediateStop {
break
}
}
}
}
return errors, errors == nil
}