Passed
Pull Request — main (#36)
by Rushan
02:12 queued 31s
created

validation.validateConditionConstraints   B

Complexity

Conditions 6

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6.288

Importance

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