Passed
Pull Request — main (#36)
by Rushan
03:06
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
// 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 := constraint.validateConditionConstraints(scope, &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 validateIterableOfValidatables(scope Scope, iterable generic.Iterable) (ViolationList, error) {
278 1
	violations := make(ViolationList, 0)
279
280 1
	err := iterable.Iterate(func(key generic.Key, value interface{}) error {
281 1
		s := scope
282 1
		if key.IsIndex() {
283 1
			s = s.atIndex(key.Index())
284
		} else {
285 1
			s = s.atProperty(key.String())
286
		}
287
288 1
		validate := newValidValidator(value.(Validatable), nil)
289 1
		vs, err := validate(s)
290 1
		if err != nil {
291
			return err
292
		}
293
294 1
		violations = append(violations, vs...)
295
296 1
		return nil
297
	})
298
299 1
	return violations, err
300
}
301