Passed
Push — main ( a82edc...08e96c )
by Igor
58s queued 11s
created

validation.*Arguments.addValidator   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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