Test Failed
Pull Request — main (#73)
by Igor
01:52
created

validation.*Scope.applyOptions   A

Complexity

Conditions 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nop 1
dl 0
loc 9
ccs 3
cts 3
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
package validation
2
3
import (
4
	"context"
5
	"fmt"
6
7
	"github.com/muonsoft/language"
8
)
9
10
const DefaultGroup = "default"
11
12
// Scope holds the current state of validation. On the client-side of the package,
13
// it can be used to build violations.
14
type Scope struct {
15
	context          context.Context
16
	propertyPath     *PropertyPath
17
	language         language.Tag
18
	translator       Translator
19
	violationFactory ViolationFactory
20
	groups           []string
21
	constraints      map[string]interface{}
22
}
23
24
// Context returns context value that was passed to the validator by Context argument or
25
// by creating scoped validator with the validator.WithContext method.
26
func (s Scope) Context() context.Context {
27 1
	return s.context
28
}
29
30
// NewConstraintError creates a new ConstraintError, which can be used to stop validation process
31
// if constraint is not properly configured.
32
func (s Scope) NewConstraintError(constraintName, description string) ConstraintError {
33 1
	return ConstraintError{
34 1
		ConstraintName: constraintName,
35
		Path:           s.propertyPath,
36 1
		Description:    description,
37 1
	}
38 1
}
39 1
40
// CreateViolation is used to quickly create a violation with only code and message attributes.
41
// This method automatically injects the property path and language of the current validation scope.
42 1
func (s Scope) CreateViolation(code, message string, path ...PropertyPathElement) Violation {
43
	return s.BuildViolation(code, message).At(path...).Create()
44
}
45
46
// BuildViolation is used to create violations in validation methods of constraints.
47 1
// This method automatically injects the property path and language of the current validation scope.
48
func (s Scope) BuildViolation(code, message string) *ViolationBuilder {
49 1
	b := NewViolationBuilder(s.violationFactory).BuildViolation(code, message)
50
	b = b.SetPropertyPath(s.propertyPath)
51
52
	if s.language != language.Und {
53
		b = b.WithLanguage(s.language)
54 1
	} else if s.context != nil {
55
		b = b.WithLanguage(language.FromContext(s.context))
56 1
	}
57
58
	return b
59
}
60
61
// BuildViolationList is used to create a violation list in validation methods of constraints.
62 1
// This method automatically injects the property path and language of the current validation scope.
63
func (s Scope) BuildViolationList() *ViolationListBuilder {
64
	b := NewViolationListBuilder(s.violationFactory)
65
	b = b.SetPropertyPath(s.propertyPath)
66
67
	if s.language != language.Und {
68 1
		b = b.WithLanguage(s.language)
69
	} else if s.context != nil {
70
		b = b.WithLanguage(language.FromContext(s.context))
71
	}
72
73
	return b
74
}
75 1
76 1
// AtProperty returns a copy of the scope with property path appended by the given property name.
77 1
func (s Scope) AtProperty(name string) Scope {
78
	s.propertyPath = s.propertyPath.WithProperty(name)
79 1
80 1
	return s
81 1
}
82
83
// AtIndex returns a copy of the scope with property path appended by the given array index.
84
func (s Scope) AtIndex(index int) Scope {
85
	s.propertyPath = s.propertyPath.WithIndex(index)
86 1
87 1
	return s
88 1
}
89 1
90
// Validator creates a new validator for the given scope. This validator can be used to perform
91
// complex validation on a custom constraint using existing constraints.
92 1
func (s Scope) Validator() *Validator {
93 1
	return newScopedValidator(s)
94 1
}
95
96
// Validate can be used to run complex validation inside a constraint.
97
func (s Scope) Validate(arguments ...Argument) error {
98
	return s.Validator().Validate(s.context, arguments...)
99 1
}
100
101
// IsIgnored is the reverse condition for applying validation groups to the IsApplied method.
102
// It is recommended to use this method in every validation method of the constraint.
103 1
func (s Scope) IsIgnored(groups ...string) bool {
104 1
	return !s.IsApplied(groups...)
105 1
}
106 1
107
// IsApplied compares scope validation groups and constraint groups. If one of the scope groups intersects with
108 1
// the constraint groups, the validation scope should be applied (returns true).
109
// Empty groups are treated as DefaultGroup. To set validation groups use the validator.WithGroups() method.
110 1
func (s Scope) IsApplied(groups ...string) bool {
111 1
	if len(s.groups) == 0 {
112
		if len(groups) == 0 {
113
			return true
114
		}
115 1
		for _, g := range groups {
116
			if g == DefaultGroup {
117
				return true
118
			}
119 1
		}
120 1
	}
121
122
	for _, g1 := range s.groups {
123
		if len(groups) == 0 {
124 1
			if g1 == DefaultGroup {
125 1
				return true
126
			}
127 1
		}
128
		for _, g2 := range groups {
129
			if g1 == g2 {
130 1
				return true
131
			}
132
		}
133
	}
134 1
135
	return false
136 1
}
137
138
func (s *Scope) applyOptions(options ...Option) error {
139
	for _, option := range options {
140 1
		err := option.SetUp(s)
141
		if err != nil {
142 1
			return fmt.Errorf(`failed to set up option: %w`, err)
143
		}
144
	}
145
146 1
	return nil
147 1
}
148
149
func (s Scope) withContext(ctx context.Context) Scope {
150
	s.context = ctx
151
152
	return s
153
}
154
155 1
func (s Scope) withLanguage(tag language.Tag) Scope {
156
	s.language = tag
157
158
	return s
159
}
160
161
func (s Scope) withGroups(groups ...string) Scope {
162
	s.groups = groups
163
	return s
164
}
165
166
func newScope(
167
	translator Translator,
168
	violationFactory ViolationFactory,
169
	constraints map[string]interface{},
170
) Scope {
171
	return Scope{
172
		context:          context.Background(),
173
		translator:       translator,
174
		violationFactory: violationFactory,
175
		constraints:      constraints,
176
	}
177
}
178