Passed
Pull Request — main (#37)
by Rushan
02:17
created

validation.validateOnScope   B

Complexity

Conditions 6

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.027

Importance

Changes 0
Metric Value
cc 6
eloc 12
dl 0
loc 18
ccs 10
cts 11
cp 0.9091
crap 6.027
rs 8.6666
c 0
b 0
f 0
nop 3
1
package validation
2
3
import (
4
	"reflect"
5
	"time"
6
7
	"github.com/muonsoft/validation/generic"
8
)
9
10
type ConstraintsPool interface {
11
	validate(scope Scope, violations *ViolationList, validate ValidateByConstraintFunc) error
12
}
13
14
// ValidateByConstraintFunc is used for building validation functions for the values of specific types.
15
type ValidateByConstraintFunc func(constraint Constraint, scope Scope) error
16
17
type validateFunc func(scope Scope) (ViolationList, error)
18
19
func newValueValidator(value interface{}, options []Option) (validateFunc, error) {
20 1
	switch v := value.(type) {
21
	case Validatable:
22 1
		return newValidValidator(v, options), nil
23
	case time.Time:
24 1
		return newTimeValidator(&v, options), nil
25
	case *time.Time:
26 1
		return newTimeValidator(v, options), nil
27
	}
28
29 1
	v := reflect.ValueOf(value)
30
31 1
	switch v.Kind() {
32
	case reflect.Ptr:
33 1
		return newValuePointerValidator(v, options)
34
	case reflect.Bool:
35 1
		b := v.Bool()
36 1
		return newBoolValidator(&b, options), nil
37
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
38
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
39
		reflect.Float32, reflect.Float64:
40 1
		n, err := generic.NewNumber(value)
41 1
		if err != nil {
42
			return nil, err
43
		}
44
45 1
		return newNumberValidator(*n, options), nil
46
	case reflect.String:
47 1
		s := v.String()
48 1
		return newStringValidator(&s, options), nil
49
	case reflect.Array, reflect.Slice, reflect.Map:
50 1
		i, err := generic.NewIterable(value)
51 1
		if err != nil {
52
			return nil, err
53
		}
54
55 1
		return newIterableValidator(i, options), nil
56
	}
57
58 1
	return nil, &NotValidatableError{Value: v}
59
}
60
61
func newValuePointerValidator(value reflect.Value, options []Option) (validateFunc, error) {
62 1
	p := value.Elem()
63 1
	if value.IsNil() {
64 1
		return newNilValidator(options), nil
65
	}
66
67 1
	switch p.Kind() {
68
	case reflect.Bool:
69 1
		b := p.Bool()
70 1
		return newBoolValidator(&b, options), nil
71
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
72
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
73
		reflect.Float32, reflect.Float64:
74 1
		n, err := generic.NewNumber(p.Interface())
75 1
		if err != nil {
76
			return nil, err
77
		}
78
79 1
		return newNumberValidator(*n, options), nil
80
	case reflect.String:
81 1
		s := p.String()
82 1
		return newStringValidator(&s, options), nil
83
	case reflect.Array, reflect.Slice, reflect.Map:
84 1
		i, err := generic.NewIterable(p.Interface())
85 1
		if err != nil {
86
			return nil, err
87
		}
88
89 1
		return newIterableValidator(i, options), nil
90
	}
91
92
	return nil, &NotValidatableError{Value: value}
93
}
94
95
func newNilValidator(options []Option) validateFunc {
96 1
	return newValidator(options, func(constraint Constraint, scope Scope) error {
97 1
		if constraintValidator, ok := constraint.(NilConstraint); ok {
98 1
			return constraintValidator.ValidateNil(scope)
99
		}
100
101
		return nil
102
	})
103
}
104
105
func newBoolValidator(value *bool, options []Option) validateFunc {
106 1
	return newValidator(options, func(constraint Constraint, scope Scope) error {
107 1
		if c, ok := constraint.(BoolConstraint); ok {
108 1
			return c.ValidateBool(value, scope)
109
		}
110
111 1
		return NewInapplicableConstraintError(constraint, "bool")
112
	})
113
}
114
115
func newNumberValidator(value generic.Number, options []Option) validateFunc {
116 1
	return newValidator(options, func(constraint Constraint, scope Scope) error {
117 1
		if c, ok := constraint.(NumberConstraint); ok {
118 1
			return c.ValidateNumber(value, scope)
119
		}
120
121 1
		return NewInapplicableConstraintError(constraint, "number")
122
	})
123
}
124
125
func newStringValidator(value *string, options []Option) validateFunc {
126 1
	return newValidator(options, func(constraint Constraint, scope Scope) error {
127 1
		if c, ok := constraint.(StringConstraint); ok {
128 1
			return c.ValidateString(value, scope)
129
		}
130
131 1
		return NewInapplicableConstraintError(constraint, "string")
132
	})
133
}
134
135
func newIterableValidator(iterable generic.Iterable, options []Option) validateFunc {
136 1
	return func(scope Scope) (ViolationList, error) {
137 1
		err := scope.applyOptions(options...)
138 1
		if err != nil {
139
			return nil, err
140
		}
141
142 1
		violations, err := validateOnScope(scope, options, func(constraint Constraint, scope Scope) error {
143 1
			if c, ok := constraint.(IterableConstraint); ok {
144 1
				return c.ValidateIterable(iterable, scope)
145
			}
146
147 1
			return NewInapplicableConstraintError(constraint, "iterable")
148
		})
149 1
		if err != nil {
150 1
			return nil, err
151
		}
152
153 1
		if iterable.IsElementImplements(validatableType) {
154 1
			vs, err := validateIterableOfValidatables(scope, iterable)
155 1
			if err != nil {
156
				return nil, err
157
			}
158 1
			violations = append(violations, vs...)
159
		}
160
161 1
		return violations, nil
162
	}
163
}
164
165
func newCountableValidator(count int, options []Option) validateFunc {
166 1
	return newValidator(options, func(constraint Constraint, scope Scope) error {
167 1
		if c, ok := constraint.(CountableConstraint); ok {
168 1
			return c.ValidateCountable(count, scope)
169
		}
170
171 1
		return NewInapplicableConstraintError(constraint, "countable")
172
	})
173
}
174
175
func newTimeValidator(value *time.Time, options []Option) validateFunc {
176 1
	return newValidator(options, func(constraint Constraint, scope Scope) error {
177 1
		if c, ok := constraint.(TimeConstraint); ok {
178 1
			return c.ValidateTime(value, scope)
179
		}
180
181 1
		return NewInapplicableConstraintError(constraint, "time")
182
	})
183
}
184
185
func newEachValidator(iterable generic.Iterable, options []Option) validateFunc {
186 1
	return func(scope Scope) (ViolationList, error) {
187 1
		violations := make(ViolationList, 0)
188
189 1
		err := iterable.Iterate(func(key generic.Key, value interface{}) error {
190 1
			opts := options
191 1
			if key.IsIndex() {
192 1
				opts = append(opts, ArrayIndex(key.Index()))
193
			} else {
194 1
				opts = append(opts, PropertyName(key.String()))
195
			}
196
197 1
			validate, err := newValueValidator(value, opts)
198 1
			if err != nil {
199
				return err
200
			}
201
202 1
			vs, err := validate(scope)
203 1
			if err != nil {
204
				return err
205
			}
206 1
			violations = append(violations, vs...)
207
208 1
			return nil
209
		})
210
211 1
		return violations, err
212
	}
213
}
214
215
func newEachStringValidator(values []string, options []Option) validateFunc {
216 1
	return func(scope Scope) (ViolationList, error) {
217 1
		violations := make(ViolationList, 0)
218
219 1
		for i := range values {
220 1
			opts := append(options, ArrayIndex(i))
221 1
			validate := newStringValidator(&values[i], opts)
222 1
			vs, err := validate(scope)
223 1
			if err != nil {
224
				return nil, err
225
			}
226 1
			violations = append(violations, vs...)
227
		}
228
229 1
		return violations, nil
230
	}
231
}
232
233
func newValidValidator(value Validatable, options []Option) validateFunc {
234 1
	return func(scope Scope) (ViolationList, error) {
235 1
		err := scope.applyOptions(options...)
236 1
		if err != nil {
237
			return nil, err
238
		}
239
240 1
		err = value.Validate(newScopedValidator(scope))
241 1
		violations, ok := UnwrapViolationList(err)
242 1
		if ok {
243 1
			return violations, nil
244
		}
245
246
		return nil, err
247
	}
248
}
249
250
func newValidator(options []Option, validate ValidateByConstraintFunc) validateFunc {
251 1
	return func(scope Scope) (ViolationList, error) {
252 1
		err := scope.applyOptions(options...)
253 1
		if err != nil {
254 1
			return nil, err
255
		}
256
257 1
		return validateOnScope(scope, options, validate)
258
	}
259
}
260
261
func validateOnScope(scope Scope, options []Option, validate ValidateByConstraintFunc) (ViolationList, error) {
262 1
	violations := make(ViolationList, 0)
263
264 1
	for _, option := range options {
265 1
		if constraint, ok := option.(ConstraintsPool); ok {
266 1
			err := constraint.validate(scope, &violations, validate)
267 1
			if err != nil {
268
				return nil, err
269
			}
270 1
		} else if constraint, ok := option.(Constraint); ok {
271 1
			err := violations.AppendFromError(validate(constraint, scope))
272 1
			if err != nil {
273 1
				return nil, err
274
			}
275
		}
276
	}
277
278 1
	return violations, nil
279
}
280
281
func validateIterableOfValidatables(scope Scope, iterable generic.Iterable) (ViolationList, error) {
282 1
	violations := make(ViolationList, 0)
283
284 1
	err := iterable.Iterate(func(key generic.Key, value interface{}) error {
285 1
		s := scope
286 1
		if key.IsIndex() {
287 1
			s = s.atIndex(key.Index())
288
		} else {
289 1
			s = s.atProperty(key.String())
290
		}
291
292 1
		validate := newValidValidator(value.(Validatable), nil)
293 1
		vs, err := validate(s)
294 1
		if err != nil {
295
			return err
296
		}
297
298 1
		violations = append(violations, vs...)
299
300 1
		return nil
301
	})
302
303 1
	return violations, err
304
}
305