forked from picatic/norm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
valid.go
214 lines (188 loc) · 5.7 KB
/
valid.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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package norm
import (
"bytes"
"fmt"
"github.com/picatic/norm/field"
"reflect"
)
// ModelValidators implementation for a model to define its validators
type ModelValidators interface {
Model
Validators() []FieldValidator
}
// Validator expects Validate(Session) to do more generic/whole model validations.
//
// It should return ValidationError or ValidationErrors on validation errors
type ModelValidator interface {
Model
Validate(Session) error
}
// FieldValidator defines a validation on a field.
//
// Field() provides access to the field.Name being validated
// Alias() can be set to provide a unique error alias for the error itself: CODE0001, empty_string, etc.
// Validate() is expected to return a FieldValidationError with an appropriate message set informating about the error
type FieldValidator interface {
Field() field.Name
Alias() string
Validate(Session, Model) error
}
// ValidatorCache Store FieldValidators for models
type ValidatorCache map[reflect.Type][]FieldValidator
// Get Fetch the validators registered to a Model
func (vm ValidatorCache) Get(model Model) []FieldValidator {
modelType := reflect.TypeOf(model)
validators, ok := vm[modelType]
if !ok {
validators = []FieldValidator{}
vm[modelType] = validators
}
return validators
}
// Set validators for a model
func (vm ValidatorCache) Set(model Model, validators []FieldValidator) {
modelType := reflect.TypeOf(model)
vm[modelType] = validators
}
// Del deletes validators for a model
func (vm ValidatorCache) Del(model Model) {
modelType := reflect.TypeOf(model)
delete(vm, modelType)
}
// Clone the ValidatorCache
func (vm ValidatorCache) Clone() ValidatorCache {
clone := ValidatorCache{}
for k, v := range vm {
clone[k] = make([]FieldValidator, len(v))
for i, vv := range v {
clone[k][i] = vv
}
}
return clone
}
// Validate a model and specified fields. Returns nil if no errors.
//
// Returning a ValidationErrors if any errors (including non-validation related) or nil on success
func (vm ValidatorCache) Validate(sess Session, model Model, fields field.Names) *ValidationErrors {
errs := &ValidationErrors{}
for _, validator := range vm.Get(model) {
switch v := validator.(type) {
case FieldValidator:
for _, field := range fields {
if field == v.Field() {
if err := v.Validate(sess, model); err != nil {
switch err.(type) {
case *ValidationError:
err.(*ValidationError).Field = field
err.(*ValidationError).Alias = validator.Alias()
errs.Add(err)
case *FieldValidationError:
errs.Add(NewValidationError(field, validator.Alias(), err.(*FieldValidationError).Message))
default:
errs.Add(err)
}
break
}
}
}
}
}
if len(errs.Errors) > 0 {
return errs
}
return nil
}
// FieldValidatorFunc What a FieldValidator expects to have implemented
//
// Session can be used to execute queries as part of this validation
// Model is provided if further access to other fields will be needed to validate the model
// Field (implementing Valuer) provides access to the value, but you may have to cast to work with it
// args allows you to pass configuration params to the validator: range values, array of strings to match, regex, etc.
type FieldValidatorFunc func(sess Session, model Model, value field.Field, args ...interface{}) error
// FieldValidator a private implementation for a standard field validation
type fieldValidator struct {
field field.Name
alias string
Func FieldValidatorFunc
Args []interface{}
}
// NewFieldValidator Create a new FieldValidator, providing a FieldName, alias for the error,
// a function to run to validate and any args required for that validator.
func NewFieldValidator(
field field.Name,
alias string,
vFunc FieldValidatorFunc,
args ...interface{},
) FieldValidator {
return &fieldValidator{
field: field,
alias: alias,
Func: vFunc,
Args: args,
}
}
// Field that this validator is bound to
func (fv fieldValidator) Field() field.Name {
return fv.field
}
func (fv fieldValidator) Alias() string {
return fv.alias
}
// Validate a field on a model
func (fv fieldValidator) Validate(sess Session, model Model) error {
field, err := ModelGetField(model, fv.Field())
if err != nil {
return err
}
return fv.Func(sess, model, field, fv.Args...)
}
// FieldValidationError error that a field valdiations return
type FieldValidationError struct {
Message string
}
// NewFieldValidationError create a field validator error
func NewFieldValidationError(msg string) *FieldValidationError {
return &FieldValidationError{Message: msg}
}
// Error returns the message
func (fve FieldValidationError) Error() string {
return fve.Message
}
// ValidationError Represent a single validation error. Contains enough information to construct a useful validation error message.
type ValidationError struct {
Field field.Name
Alias string
Message string
}
// NewValidationError creator
func NewValidationError(f field.Name, a string, msg string) *ValidationError {
return &ValidationError{Field: f, Alias: a, Message: msg}
}
// Error String the error
func (ve ValidationError) Error() string {
return fmt.Sprintf("Field: [%s] Alias: [%s] Message: %s", ve.Field, ve.Alias, ve.Message)
}
// ValidationErrors Represent a set of ValidationErrors and/or error
type ValidationErrors struct {
Errors []error
Model Model
}
// Add an error to this set
func (ve *ValidationErrors) Add(err error) {
ve.Errors = append(ve.Errors, err)
}
// Error string the error
func (ve ValidationErrors) Error() string {
var el = len(ve.Errors)
if el == 0 {
return fmt.Sprintf("Empty errors")
}
var out = new(bytes.Buffer)
for i, e := range ve.Errors {
out.WriteString(e.Error())
if i < el-1 {
out.WriteString("; ")
}
}
return out.String()
}