Passed
Push — main ( 36e3e5...da04d5 )
by Rushan
01:11 queued 10s
created

validation.Valid   A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
nop 2
1
package validation
2
3
import (
4
	"context"
5
	"fmt"
6
	"time"
7
8
	"github.com/muonsoft/validation/generic"
9
	"golang.org/x/text/language"
10
)
11
12
// Argument used to set up the validation process. It is used to set up the current validation scope and to pass
13
// arguments for validation values.
14
type Argument interface {
15
	set(arguments *Arguments) error
16
}
17
18
type argumentFunc func(arguments *Arguments) error
19
20
func (f argumentFunc) set(arguments *Arguments) error {
21 1
	return f(arguments)
22
}
23
24
type Arguments struct {
25
	scope      Scope
26
	validators []validateFunc
27
}
28
29
func (args *Arguments) addValidator(validator validateFunc) {
30 1
	args.validators = append(args.validators, validator)
31
}
32
33
// Value argument is used to validate any supported value. It uses reflection to detect the type of the argument
34
// and pass it to a specific validation method.
35
//
36
// If the validator cannot determine the value or it is not supported, then NotValidatableError will be returned
37
// when calling the validator.Validate method.
38
//
39
// Example
40
//  v := 0
41
//  err := validator.Validate(Value(v, it.IsNotBlank()))
42
func Value(value interface{}, options ...Option) Argument {
43 1
	return argumentFunc(func(arguments *Arguments) error {
44 1
		v, err := newValueValidator(value, options)
45 1
		if err != nil {
46 1
			return err
47
		}
48
49 1
		arguments.addValidator(v)
50
51 1
		return nil
52
	})
53
}
54
55
// PropertyValue argument is an alias for Value that automatically adds property name to the current scope.
56
func PropertyValue(name string, value interface{}, options ...Option) Argument {
57 1
	return Value(value, append([]Option{PropertyName(name)}, options...)...)
58
}
59
60
// Bool argument is used to validate boolean values.
61
//
62
// Example
63
//  v := false
64
//  err := validator.Validate(Bool(&v, it.IsNotBlank()))
65
func Bool(value *bool, options ...Option) Argument {
66 1
	return argumentFunc(func(arguments *Arguments) error {
67 1
		arguments.addValidator(newBoolValidator(value, options))
68
69 1
		return nil
70
	})
71
}
72
73
// BoolProperty argument is an alias for Bool that automatically adds property name to the current scope.
74
func BoolProperty(name string, value *bool, options ...Option) Argument {
75 1
	return Bool(value, append([]Option{PropertyName(name)}, options...)...)
76
}
77
78
// Number argument is used to validate numbers (any types of integers or floats). At the moment it uses
79
// reflection to detect numeric value. Given value is internally converted into int64 or float64 to make comparisons.
80
//
81
// Warning! This method will be changed after generics implementation in Go.
82
//
83
// Example
84
//  v := 0
85
//  err := validator.Validate(Number(&v, it.IsNotBlank()))
86
func Number(value interface{}, options ...Option) Argument {
87 1
	return argumentFunc(func(arguments *Arguments) error {
88 1
		number, err := generic.NewNumber(value)
89 1
		if err != nil {
90 1
			return fmt.Errorf(`cannot convert value "%v" to number: %w`, value, err)
91
		}
92
93 1
		arguments.addValidator(newNumberValidator(*number, options))
94
95 1
		return nil
96
	})
97
}
98
99
// NumberProperty argument is an alias for Number that automatically adds property name to the current scope.
100
func NumberProperty(name string, value interface{}, options ...Option) Argument {
101 1
	return Number(value, append([]Option{PropertyName(name)}, options...)...)
102
}
103
104
// String argument is used to validate strings.
105
//
106
// Example
107
//  v := ""
108
//  err := validator.Validate(String(&v, it.IsNotBlank()))
109
func String(value *string, options ...Option) Argument {
110 1
	return argumentFunc(func(arguments *Arguments) error {
111 1
		arguments.addValidator(newStringValidator(value, options))
112
113 1
		return nil
114
	})
115
}
116
117
// StringProperty argument is an alias for String that automatically adds property name to the current scope.
118
func StringProperty(name string, value *string, options ...Option) Argument {
119 1
	return String(value, append([]Option{PropertyName(name)}, options...)...)
120
}
121
122
// Iterable argument is used to validate arrays, slices, or maps. At the moment it uses reflection
123
// to iterate over values. So you can expect a performance hit using this method. For better performance
124
// it is recommended to make a custom type that implements the Validatable interface.
125
//
126
// Warning! This argument is subject to change in the final versions of the library.
127
//
128
// Example
129
//  v := []string{}
130
//  err := validator.Validate(Iterable(&v, it.IsNotBlank()))
131
func Iterable(value interface{}, options ...Option) Argument {
132 1
	return argumentFunc(func(arguments *Arguments) error {
133 1
		iterable, err := generic.NewIterable(value)
134 1
		if err != nil {
135 1
			return fmt.Errorf(`cannot convert value "%v" to iterable: %w`, value, err)
136
		}
137
138 1
		arguments.addValidator(newIterableValidator(iterable, options))
139
140 1
		return nil
141
	})
142
}
143
144
// IterableProperty argument is an alias for Iterable that automatically adds property name to the current scope.
145
func IterableProperty(name string, value interface{}, options ...Option) Argument {
146 1
	return Iterable(value, append([]Option{PropertyName(name)}, options...)...)
147
}
148
149
// Countable argument can be used to validate size of an array, slice, or map. You can pass result of len()
150
// function as an argument.
151
//
152
// Example
153
//  s := []string{"a", "b"}
154
//  err := validator.Validate(Countable(len(s), it.HasMinCount(3)))
155
func Countable(count int, options ...Option) Argument {
156 1
	return argumentFunc(func(arguments *Arguments) error {
157 1
		arguments.addValidator(newCountableValidator(count, options))
158
159 1
		return nil
160
	})
161
}
162
163
// CountableProperty argument is an alias for Countable that automatically adds property name to the current scope.
164
func CountableProperty(name string, count int, options ...Option) Argument {
165 1
	return Countable(count, append([]Option{PropertyName(name)}, options...)...)
166
}
167
168
// Time argument is used to validate time.Time value.
169
//
170
// Example
171
//  t := time.Now()
172
//  err := validator.Validate(Time(&t, it.IsNotBlank()))
173
func Time(value *time.Time, options ...Option) Argument {
174 1
	return argumentFunc(func(arguments *Arguments) error {
175 1
		arguments.addValidator(newTimeValidator(value, options))
176
177 1
		return nil
178
	})
179
}
180
181
// TimeProperty argument is an alias for Time that automatically adds property name to the current scope.
182
func TimeProperty(name string, value *time.Time, options ...Option) Argument {
183 1
	return Time(value, append([]Option{PropertyName(name)}, options...)...)
184
}
185
186
// Each is used to validate each value of iterable (array, slice, or map). At the moment it uses reflection
187
// to iterate over values. So you can expect a performance hit using this method. For better performance
188
// it is recommended to make a custom type that implements the Validatable interface. Also, you can use
189
// EachString argument to validate slice of strings.
190
//
191
// Warning! This argument is subject to change in the final versions of the library.
192
//
193
// Example
194
//  v := []string{""}
195
//  err := validator.Validate(Each(&v, it.IsNotBlank()))
196
func Each(value interface{}, options ...Option) Argument {
197 1
	return argumentFunc(func(arguments *Arguments) error {
198 1
		iterable, err := generic.NewIterable(value)
199 1
		if err != nil {
200 1
			return fmt.Errorf(`cannot convert value "%v" to iterable: %w`, value, err)
201
		}
202
203 1
		arguments.addValidator(newEachValidator(iterable, options))
204
205 1
		return nil
206
	})
207
}
208
209
// EachProperty argument is an alias for Each that automatically adds property name to the current scope.
210
func EachProperty(name string, value interface{}, options ...Option) Argument {
211 1
	return Each(value, append([]Option{PropertyName(name)}, options...)...)
212
}
213
214
// EachString is used to validate a slice of strings. This is a more performant version of Each argument.
215
//
216
// Example
217
//  v := []string{""}
218
//  err := validator.Validate(EachString(&v, it.IsNotBlank()))
219
func EachString(values []string, options ...Option) Argument {
220 1
	return argumentFunc(func(arguments *Arguments) error {
221 1
		arguments.addValidator(newEachStringValidator(values, options))
222
223 1
		return nil
224
	})
225
}
226
227
// EachStringProperty argument is an alias for EachString that automatically adds property name to the current scope.
228
func EachStringProperty(name string, values []string, options ...Option) Argument {
229 1
	return EachString(values, append([]Option{PropertyName(name)}, options...)...)
230
}
231
232
// Valid is used to run validation on the Validatable type. This method is recommended
233
// to run a complex validation process.
234
func Valid(value Validatable, options ...Option) Argument {
235 1
	return argumentFunc(func(arguments *Arguments) error {
236 1
		arguments.addValidator(newValidValidator(value, options))
237
238 1
		return nil
239
	})
240
}
241
242
// ValidProperty argument is an alias for Valid that automatically adds property name to the current scope.
243
func ValidProperty(name string, value Validatable, options ...Option) Argument {
244 1
	return Valid(value, append([]Option{PropertyName(name)}, options...)...)
245
}
246
247
// Context can be used to pass context to validation constraints via scope.
248
//
249
// Example
250
//  err := validator.Validate(
251
//      Context(request.Context()),
252
//      String(&s, it.IsNotBlank()), // now all called constraints will use passed context in their methods
253
//  )
254
func Context(ctx context.Context) Argument {
255 1
	return argumentFunc(func(arguments *Arguments) error {
256 1
		arguments.scope.context = ctx
257
258 1
		return nil
259
	})
260
}
261
262
// Language argument sets the current language for translation of a violation message.
263
//
264
// Example
265
//  err := validator.Validate(
266
//      Language(language.Russian),
267
//      String(&s, it.IsNotBlank()), // all violations created in scope will be translated into Russian
268
//  )
269
func Language(tag language.Tag) Argument {
270 1
	return argumentFunc(func(arguments *Arguments) error {
271 1
		arguments.scope.language = tag
272
273 1
		return nil
274
	})
275
}
276
277
// NewArgument can be used to implement your own validation arguments for the specific types.
278
// See example for more details.
279
func NewArgument(options []Option, validate ValidateByConstraintFunc) Argument {
280 1
	return argumentFunc(func(arguments *Arguments) error {
281 1
		arguments.addValidator(newValidator(options, validate))
282
283 1
		return nil
284
	})
285
}
286