Passed
Push — main ( aa8c07...4607a7 )
by Igor
02:58 queued 01:09
created

validation.newScopedValidator   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
	"time"
6
7
	"golang.org/x/text/language"
8
	"golang.org/x/text/message/catalog"
9
)
10
11
// Validator is the main validation service. It can be created by NewValidator constructor.
12
// Also, you can use singleton version from the package "github.com/muonsoft/validation/validator".
13
type Validator struct {
14
	scope Scope
15
}
16
17
// ValidatorOption is a base type for configuration options used to create a new instance of Validator.
18
type ValidatorOption func(validator *Validator) error
19
20
// NewValidator is a constructor for creating an instance of Validator.
21
// You can configure it by using validator options.
22
func NewValidator(options ...ValidatorOption) (*Validator, error) {
23 1
	validator := &Validator{scope: newScope()}
24
25 1
	for _, setOption := range options {
26 1
		err := setOption(validator)
27 1
		if err != nil {
28 1
			return nil, err
29
		}
30
	}
31
32 1
	err := validator.scope.translator.init()
33 1
	if err != nil {
34 1
		return nil, err
35
	}
36
37 1
	return validator, nil
38
}
39
40
func newScopedValidator(scope Scope) *Validator {
41 1
	return &Validator{scope: scope}
42
}
43
44
// DefaultLanguage is used to set up the default language for translation of violation messages.
45
func DefaultLanguage(tag language.Tag) ValidatorOption {
46 1
	return func(validator *Validator) error {
47 1
		validator.scope.translator.defaultLanguage = tag
48 1
		return nil
49
	}
50
}
51
52
// Translations is used to load translation messages into the validator.
53
//
54
// By default, all violation messages are generated in the English language with pluralization capabilities.
55
// To use a custom language you have to load translations on validator initialization.
56
// Built-in translations are available in the sub-packages of the package "github.com/muonsoft/message/translations".
57
// The translation mechanism is provided by the "golang.org/x/text" package (be aware, it has no stable version yet).
58
func Translations(messages map[language.Tag]map[string]catalog.Message) ValidatorOption {
59 1
	return func(validator *Validator) error {
60 1
		return validator.scope.translator.loadMessages(messages)
61
	}
62
}
63
64
// SetViolationFactory can be used to override the mechanism of violation creation.
65
func SetViolationFactory(factory ViolationFactory) ValidatorOption {
66 1
	return func(validator *Validator) error {
67 1
		validator.scope.violationFactory = factory
68
69 1
		return nil
70
	}
71
}
72
73
// StoredConstraint option can be used to store a constraint in an internal validator store.
74
// It can later be used by the validator.ValidateBy method. This can be useful for passing
75
// custom or prepared constraints to Validatable.
76
//
77
// If the constraint already exists, a ConstraintAlreadyStoredError will be returned.
78
func StoredConstraint(key string, constraint Constraint) ValidatorOption {
79 1
	return func(validator *Validator) error {
80 1
		if _, exists := validator.scope.constraints[key]; exists {
81 1
			return ConstraintAlreadyStoredError{Key: key}
82
		}
83
84 1
		validator.scope.constraints[key] = constraint
85
86 1
		return nil
87
	}
88
}
89
90
// Validate is the main validation method. It accepts validation arguments. Arguments can be
91
// used to tune up the validation process or to pass values of a specific type.
92
func (validator *Validator) Validate(ctx context.Context, arguments ...Argument) error {
93 1
	args := &Arguments{scope: validator.scope.withContext(ctx)}
94 1
	for _, argument := range arguments {
95 1
		err := argument.set(args)
96 1
		if err != nil {
97 1
			return err
98
		}
99
	}
100
101 1
	violations := &ViolationList{}
102 1
	for _, validate := range args.validators {
103 1
		vs, err := validate(args.scope)
104 1
		if err != nil {
105 1
			return err
106
		}
107 1
		violations.Join(vs)
108
	}
109
110 1
	return violations.AsError()
111
}
112
113
// ValidateValue is an alias for validating a single value of any supported type.
114
func (validator *Validator) ValidateValue(ctx context.Context, value interface{}, options ...Option) error {
115 1
	return validator.Validate(ctx, Value(value, options...))
116
}
117
118
// ValidateBool is an alias for validating a single boolean value.
119
func (validator *Validator) ValidateBool(ctx context.Context, value bool, options ...Option) error {
120 1
	return validator.Validate(ctx, Bool(value, options...))
121
}
122
123
// ValidateNumber is an alias for validating a single numeric value (integer or float).
124
func (validator *Validator) ValidateNumber(ctx context.Context, value interface{}, options ...Option) error {
125 1
	return validator.Validate(ctx, Number(value, options...))
126
}
127
128
// ValidateString is an alias for validating a single string value.
129
func (validator *Validator) ValidateString(ctx context.Context, value string, options ...Option) error {
130 1
	return validator.Validate(ctx, String(value, options...))
131
}
132
133
// ValidateStrings is an alias for validating slice of strings.
134
func (validator *Validator) ValidateStrings(ctx context.Context, values []string, options ...Option) error {
135
	return validator.Validate(ctx, Strings(values, options...))
136
}
137
138
// ValidateIterable is an alias for validating a single iterable value (an array, slice, or map).
139
func (validator *Validator) ValidateIterable(ctx context.Context, value interface{}, options ...Option) error {
140 1
	return validator.Validate(ctx, Iterable(value, options...))
141
}
142
143
// ValidateCountable is an alias for validating a single countable value (an array, slice, or map).
144
func (validator *Validator) ValidateCountable(ctx context.Context, count int, options ...Option) error {
145 1
	return validator.Validate(ctx, Countable(count, options...))
146
}
147
148
// ValidateTime is an alias for validating a single time value.
149
func (validator *Validator) ValidateTime(ctx context.Context, value time.Time, options ...Option) error {
150 1
	return validator.Validate(ctx, Time(value, options...))
151
}
152
153
// ValidateEach is an alias for validating each value of an iterable (an array, slice, or map).
154
func (validator *Validator) ValidateEach(ctx context.Context, value interface{}, options ...Option) error {
155 1
	return validator.Validate(ctx, Each(value, options...))
156
}
157
158
// ValidateEachString is an alias for validating each value of a strings slice.
159
func (validator *Validator) ValidateEachString(ctx context.Context, values []string, options ...Option) error {
160 1
	return validator.Validate(ctx, EachString(values, options...))
161
}
162
163
// ValidateValidatable is an alias for validating value that implements the Validatable interface.
164
func (validator *Validator) ValidateValidatable(ctx context.Context, validatable Validatable, options ...Option) error {
165 1
	return validator.Validate(ctx, Valid(validatable, options...))
166
}
167
168
// ValidateBy is used to get the constraint from the internal validator store.
169
// If the constraint does not exist, then the validator will
170
// return a ConstraintNotFoundError during the validation process.
171
// For storing a constraint you should use the StoredConstraint option.
172
func (validator *Validator) ValidateBy(constraintKey string) Constraint {
173 1
	if constraint, exists := validator.scope.constraints[constraintKey]; exists {
174 1
		return constraint
175
	}
176
177 1
	return notFoundConstraint{key: constraintKey}
178
}
179
180
// WithLanguage method creates a new scoped validator with a given language tag. All created violations
181
// will be translated into this language.
182
func (validator *Validator) WithLanguage(tag language.Tag) *Validator {
183 1
	return newScopedValidator(validator.scope.withLanguage(tag))
184
}
185
186
// AtProperty method creates a new scoped validator with injected property name element to scope property path.
187
func (validator *Validator) AtProperty(name string) *Validator {
188 1
	return newScopedValidator(validator.scope.AtProperty(name))
189
}
190
191
// AtIndex method creates a new scoped validator with injected array index element to scope property path.
192
func (validator *Validator) AtIndex(index int) *Validator {
193 1
	return newScopedValidator(validator.scope.AtIndex(index))
194
}
195
196
// BuildViolation can be used to build a custom violation on the client-side.
197
func (validator *Validator) BuildViolation(ctx context.Context, code, message string) *ViolationBuilder {
198 1
	return validator.scope.withContext(ctx).BuildViolation(code, message)
199
}
200