Passed
Pull Request — main (#37)
by Rushan
01:59
created

functions.go   F

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Test Coverage

Coverage 88.81%

Importance

Changes 0
Metric Value
cc 74
eloc 186
dl 0
loc 307
rs 2.48
c 0
b 0
f 0
ccs 127
cts 143
cp 0.8881
crap 81.6727

15 Methods

Rating   Name   Duplication   Size   Complexity  
C validation.newValueValidator 0 40 11
A validation.newCountableValidator 0 7 3
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
A validation.newValidValidator 0 14 4
A validation.validateIterableOfValidatables 0 23 4
B validation.validateOnScope 0 26 8
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
		switch constraint := option.(type) {
262
		case SequentiallyConstraint:
263 1
			err := constraint.validateSequentiallyConstraints(scope, &violations, validate)
264 1
			if err != nil {
265
				return nil, err
266
			}
267
268
		case ConditionalConstraint:
269 1
			err := constraint.validateConditionConstraints(scope, &violations, validate)
270 1
			if err != nil {
271
				return nil, err
272
			}
273
274
		case Constraint:
275 1
			err := violations.AppendFromError(validate(constraint, scope))
276 1
			if err != nil {
277 1
				return nil, err
278
			}
279
		}
280
	}
281
282 1
	return violations, nil
283
}
284
285
func validateIterableOfValidatables(scope Scope, iterable generic.Iterable) (ViolationList, error) {
286 1
	violations := make(ViolationList, 0)
287
288 1
	err := iterable.Iterate(func(key generic.Key, value interface{}) error {
289 1
		s := scope
290 1
		if key.IsIndex() {
291 1
			s = s.atIndex(key.Index())
292
		} else {
293 1
			s = s.atProperty(key.String())
294
		}
295
296 1
		validate := newValidValidator(value.(Validatable), nil)
297 1
		vs, err := validate(s)
298 1
		if err != nil {
299
			return err
300
		}
301
302 1
		violations = append(violations, vs...)
303
304 1
		return nil
305
	})
306
307 1
	return violations, err
308
}
309