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

functions.go   F

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Test Coverage

Coverage 89.36%

Importance

Changes 0
Metric Value
cc 72
eloc 183
dl 0
loc 303
rs 2.64
c 0
b 0
f 0
ccs 126
cts 141
cp 0.8936
crap 78.2443

15 Methods

Rating   Name   Duplication   Size   Complexity  
C validation.newValueValidator 0 40 11
A validation.newCountableValidator 0 7 3
A validation.validateIterableOfValidatables 0 23 4
A validation.newStringValidator 0 7 3
A validation.newTimeValidator 0 7 3
B validation.newIterableValidator 0 27 8
B validation.newValuePointerValidator 0 32 8
A validation.newEachStringValidator 0 15 4
A validation.newNumberValidator 0 7 3
A validation.newValidator 0 8 3
B validation.newEachValidator 0 27 6
A validation.newBoolValidator 0 7 3
A validation.newNilValidator 0 7 3
B validation.validateOnScope 0 18 6
A validation.newValidValidator 0 14 4
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