-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharray_validaktor.go
143 lines (116 loc) · 2.93 KB
/
array_validaktor.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
package validaktor
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
)
type (
arrayValidator struct {
arrayOptions
regx *regexp.Regexp
}
arrayError struct {
message string
}
arrayOptions struct {
min, max int
isEmpty bool
}
ArrayOption interface {
apply(*arrayOptions)
}
fnArrayOptions func(*arrayOptions)
)
func (f fnArrayOptions) apply(options *arrayOptions) {
f(options)
}
func WithMin(n int) ArrayOption {
return fnArrayOptions(func(options *arrayOptions) {
options.min = n
})
}
func WithMax(n int) ArrayOption {
return fnArrayOptions(func(options *arrayOptions) {
options.max = n
})
}
func WithIsEmpty(isEmpty bool) ArrayOption {
return fnArrayOptions(func(options *arrayOptions) {
options.isEmpty = isEmpty
})
}
func NewArrayValidator(options ...ArrayOption) *arrayValidator {
opts := &arrayOptions{
min: 0,
max: 0,
isEmpty: false,
}
for _, opt := range options {
opt.apply(opts)
}
return &arrayValidator{
arrayOptions: *opts,
regx: regexp.MustCompile(`(min=(?P<min>\d+))?,?(max=(?P<max>\d+))?,?(contentType=(?P<contentType>(number|string|boolean)))?,?(isEmpty=(?P<isEmpty>(true|false)))?,?`),
}
}
func mapSubexpNames(m, n []string) map[string]string {
m, n = m[1:], n[1:]
r := make(map[string]string, len(m))
for i := range n {
if n[i] != "" {
r[n[i]] = m[i]
}
}
return r
}
func newArrayValidatorError(message string) *arrayError {
return &arrayError{message: message}
}
func (e *arrayError) Error() string {
return e.message
}
func (v *arrayValidator) applyValidatorOptions(args ...string) error {
m := v.regx.FindStringSubmatch(strings.Join(args[1:], ","))
n := v.regx.SubexpNames()
d := mapSubexpNames(m, n)
b, err := strconv.ParseBool(d["isEmpty"])
if err != nil {
return fmt.Errorf("arrayValidator: apply options error parse isEmpty: %w", err)
}
mx, err := strconv.Atoi(d["max"])
if err != nil {
return fmt.Errorf("arrayValidator: apply options error parse max: %w", err)
}
mn, err := strconv.Atoi(d["min"])
if err != nil {
return fmt.Errorf("arrayValidator: apply options error parse min: %w", err)
}
v.min = mn
v.max = mx
v.isEmpty = b
return nil
}
func (v *arrayValidator) validate(data interface{}) (bool, error) {
var sl reflect.Value
switch reflect.TypeOf(data).Kind() {
case reflect.Slice:
sl = reflect.ValueOf(data)
default:
return false, newArrayValidatorError("value is not an array")
}
if v.min > sl.Len() || sl.Len() > v.max {
return false, newArrayValidatorError(fmt.Sprintf("the element has invalid length %d (min=%d, max=%d)",
sl.Len(), v.min, v.max))
}
for i := 0; i < sl.Len(); i++ {
if !v.isEmpty && isZeroOfUnderlyingType(sl.Index(i).Interface()) {
return false, newArrayValidatorError(fmt.Sprintf("the element of index %d should not be empty", i+1))
}
}
return true, nil
}
func isZeroOfUnderlyingType(x interface{}) bool {
return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}