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

validation.Scope.IsIgnored   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
nop 1
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
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).WithPropertyPath(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
// IsIgnored is the reverse condition for applying validation groups to the IsApplied method.
97
// It is recommended to use this method in every validation method of the constraint.
98
func (s Scope) IsIgnored(groups ...string) bool {
99 1
	return !s.IsApplied(groups...)
100
}
101
102
// IsApplied compares scope validation groups and constraint groups. If one of the scope groups intersects with
103 1
// the constraint groups, the validation scope should be applied (returns true).
104 1
// Empty groups are treated as DefaultGroup. To set validation groups use the validator.WithGroups() method.
105 1
func (s Scope) IsApplied(groups ...string) bool {
106 1
	if len(s.groups) == 0 {
107
		if len(groups) == 0 {
108 1
			return true
109
		}
110 1
		for _, g := range groups {
111 1
			if g == DefaultGroup {
112
				return true
113
			}
114
		}
115 1
	}
116
117
	for _, g1 := range s.groups {
118
		if len(groups) == 0 {
119 1
			if g1 == DefaultGroup {
120 1
				return true
121
			}
122
		}
123
		for _, g2 := range groups {
124 1
			if g1 == g2 {
125 1
				return true
126
			}
127 1
		}
128
	}
129
130 1
	return false
131
}
132
133
func (s Scope) validate(arguments ...Argument) error {
134 1
	return s.Validator().Validate(s.context, arguments...)
135
}
136 1
137
func (s *Scope) applyOptions(options ...Option) error {
138
	for _, option := range options {
139
		err := option.SetUp(s)
140 1
		if err != nil {
141
			return fmt.Errorf(`failed to set up option: %w`, err)
142 1
		}
143
	}
144
145
	return nil
146 1
}
147 1
148
func (s Scope) withContext(ctx context.Context) Scope {
149
	s.context = ctx
150
151
	return s
152
}
153
154
func (s Scope) withLanguage(tag language.Tag) Scope {
155 1
	s.language = tag
156
157
	return s
158
}
159
160
func (s Scope) withGroups(groups ...string) Scope {
161
	s.groups = groups
162
	return s
163
}
164
165
func newScope(
166
	translator Translator,
167
	violationFactory ViolationFactory,
168
	constraints map[string]interface{},
169
) Scope {
170
	return Scope{
171
		context:          context.Background(),
172
		translator:       translator,
173
		violationFactory: violationFactory,
174
		constraints:      constraints,
175
	}
176
}
177