-
Notifications
You must be signed in to change notification settings - Fork 2
/
slice_validator.go
148 lines (122 loc) · 3.38 KB
/
slice_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
package validator
import (
"context"
"fmt"
"reflect"
"github.com/go-courier/validator/errors"
"github.com/go-courier/validator/rules"
)
var (
TargetSliceLength = "slice length"
)
/*
Validator for slice
Rules:
@slice<ELEM_RULE>[minLen,maxLen]
@slice<ELEM_RULE>[length]
@slice<@string{A,B,C}>[,100]
Aliases
@array = @slice // and range must to be use length
*/
type SliceValidator struct {
ElemValidator Validator
MinItems uint64
MaxItems *uint64
}
func init() {
ValidatorMgrDefault.Register(&SliceValidator{})
}
func (SliceValidator) Names() []string {
return []string{"slice", "array"}
}
func (validator *SliceValidator) Validate(v interface{}) error {
switch rv := v.(type) {
case reflect.Value:
return validator.ValidateReflectValue(rv)
default:
return validator.ValidateReflectValue(reflect.ValueOf(v))
}
}
func (validator *SliceValidator) ValidateReflectValue(rv reflect.Value) error {
lenOfValue := uint64(0)
if !rv.IsNil() {
lenOfValue = uint64(rv.Len())
}
if lenOfValue < validator.MinItems {
return &errors.OutOfRangeError{
Target: TargetSliceLength,
Current: lenOfValue,
Minimum: validator.MinItems,
}
}
if validator.MaxItems != nil && lenOfValue > *validator.MaxItems {
return &errors.OutOfRangeError{
Target: TargetSliceLength,
Current: lenOfValue,
Maximum: validator.MaxItems,
}
}
if validator.ElemValidator != nil {
errs := errors.NewErrorSet("")
for i := 0; i < rv.Len(); i++ {
err := validator.ElemValidator.Validate(rv.Index(i))
if err != nil {
errs.AddErr(err, i)
}
}
return errs.Err()
}
return nil
}
func (SliceValidator) New(ctx context.Context, rule *Rule) (Validator, error) {
sliceValidator := &SliceValidator{}
if rule.ExclusiveLeft || rule.ExclusiveRight {
return nil, errors.NewSyntaxError("range mark of %s should not be `(` or `)`", sliceValidator.Names()[0])
}
if rule.Range != nil {
if rule.Name == "array" && len(rule.Range) > 1 {
return nil, errors.NewSyntaxError("array should declare length only")
}
min, max, err := UintRange("length of slice", 64, rule.Range...)
if err != nil {
return nil, err
}
sliceValidator.MinItems = min
sliceValidator.MaxItems = max
}
switch rule.Type.Kind() {
case reflect.Array:
if rule.Type.Len() != int(sliceValidator.MinItems) {
return nil, fmt.Errorf("length(%d) or rule should equal length(%d) of array", sliceValidator.MinItems, rule.Type.Len())
}
case reflect.Slice:
default:
return nil, errors.NewUnsupportedTypeError(rule.String(), sliceValidator.String())
}
elemRule := []byte("")
if rule.Params != nil {
if len(rule.Params) != 1 {
return nil, fmt.Errorf("slice should only 1 parameter, but got %d", len(rule.Params))
}
r, ok := rule.Params[0].(*rules.Rule)
if !ok {
return nil, fmt.Errorf("slice parameter should be a valid rule")
}
elemRule = r.RAW
}
mgr := ValidatorMgrFromContext(ctx)
v, err := mgr.Compile(ctx, elemRule, rule.Type.Elem(), nil)
if err != nil {
return nil, fmt.Errorf("slice elem %s", err)
}
sliceValidator.ElemValidator = v
return sliceValidator, nil
}
func (validator *SliceValidator) String() string {
rule := rules.NewRule(validator.Names()[0])
if validator.ElemValidator != nil {
rule.Params = append(rule.Params, rules.NewRuleLit([]byte(validator.ElemValidator.String())))
}
rule.Range = RangeFromUint(validator.MinItems, validator.MaxItems)
return string(rule.Bytes())
}