-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
flow.go
146 lines (128 loc) · 3.87 KB
/
flow.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
package valis
import (
"github.com/soranoba/valis/code"
"reflect"
)
type (
// WhenRule is a rule that verifies the value meets the rules when conditions return true.
WhenRule struct {
condAndRules []*condAndRule
}
// WhenContext is an argument of conditions used by WhenRule.
WhenContext struct {
value interface{}
loc *Location
}
)
type (
andRule struct {
rules []Rule
}
orRule struct {
rules []Rule
}
condAndRule struct {
cond func(ctx *WhenContext) bool
rules []Rule
}
eachRule struct {
rules []Rule
}
eachFieldsRule struct {
rules []Rule
}
)
// And returns a new rule that verifies the value meets the rules and all common rules.
// Should only use it in your own rules, because to avoid validating common rules multiple times.
func And(rules ...Rule) Rule {
return &andRule{rules: rules}
}
func (r *andRule) Validate(validator *Validator, value interface{}) {
if len(validator.commonRules) > 0 {
rules := append(validator.commonRules, r.rules...)
for _, rule := range rules {
rule.Validate(validator, value)
}
} else {
for _, rule := range r.rules {
rule.Validate(validator, value)
}
}
}
// Or returns a new rule that verifies the value meets the rules at least one.
func Or(rules ...Rule) Rule {
return &orRule{rules: rules}
}
func (r *orRule) Validate(validator *Validator, value interface{}) {
for _, rule := range r.rules {
newValidator := validator.Clone(&CloneOpts{InheritLocation: true})
rule.Validate(newValidator, value)
if !newValidator.ErrorCollector().HasError() {
return
}
}
validator.ErrorCollector().Add(validator.Location(), NewError(code.Invalid, value))
}
// If is equiv to When
func If(cond func(ctx *WhenContext) bool, rules ...Rule) *WhenRule {
return When(cond, rules...)
}
// When returns a new Rule verify the value meets the rules when cond returns true.
func When(cond func(ctx *WhenContext) bool, rules ...Rule) *WhenRule {
return &WhenRule{condAndRules: []*condAndRule{{cond: cond, rules: rules}}}
}
// ElseWhen set the WhenRule that verified when all before conditions, and returns self.
func (r *WhenRule) ElseWhen(rule *WhenRule) *WhenRule {
r.condAndRules = append(r.condAndRules, rule.condAndRules...)
return r
}
// ElseIf set some Rule verified when all before conditions return false and cond returns true. And it returns self.
func (r *WhenRule) ElseIf(cond func(ctx *WhenContext) bool, rules ...Rule) *WhenRule {
r.condAndRules = append(r.condAndRules, &condAndRule{cond: cond, rules: rules})
return r
}
// Else set the rules verified when all conditions return false, and returns self.
func (r WhenRule) Else(rules ...Rule) Rule {
return r.ElseIf(func(ctx *WhenContext) bool { return true }, rules...)
}
// See Rule.Validate
func (r WhenRule) Validate(validator *Validator, value interface{}) {
ctx := &WhenContext{loc: validator.loc, value: value}
for _, condAndRule := range r.condAndRules {
if condAndRule.cond(ctx) {
for _, rule := range condAndRule.rules {
rule.Validate(validator, value)
}
return
}
}
}
// Location returns a current location.
func (ctx *WhenContext) Location() *Location {
return ctx.loc
}
// Value returns the validating value.
func (ctx *WhenContext) Value() interface{} {
return ctx.value
}
// Each returns a new rule that verifies all elements of the array or slice meet the rules and all common rules.
func Each(rules ...Rule) Rule {
return &eachRule{rules: rules}
}
func (rule *eachRule) Validate(validator *Validator, value interface{}) {
val := reflect.ValueOf(value)
for val.Kind() == reflect.Ptr {
val = val.Elem()
}
switch val.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < val.Len(); i++ {
indexValue := val.Index(i).Interface()
validator.DiveIndex(i, func(v *Validator) {
And(rule.rules...).Validate(v, indexValue)
})
}
default:
validator.ErrorCollector().Add(validator.Location(), NewError(code.NotArray, value))
}
}