Passed
Push — main ( 957b58...866640 )
by Igor
02:11 queued 24s
created

validation.newScope   A

Complexity

Conditions 1

Size

Total Lines 10
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 10
nop 3
dl 0
loc 10
ccs 1
cts 1
cp 1
crap 1
rs 9.9
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]Constraint
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
// BuildViolation is used to create violations in validation methods of constraints.
31
// This method automatically injects the property path and language of the current validation scope.
32
func (s Scope) BuildViolation(code, message string) *ViolationBuilder {
33 1
	b := NewViolationBuilder(s.violationFactory).BuildViolation(code, message)
34 1
	b.SetPropertyPath(s.propertyPath)
35
36 1
	if s.language != language.Und {
37 1
		b.SetLanguage(s.language)
38 1
	} else if s.context != nil {
39 1
		b.SetLanguage(language.FromContext(s.context))
40
	}
41
42 1
	return b
43
}
44
45
// AtProperty returns a copy of the scope with property path appended by the given property name.
46
func (s Scope) AtProperty(name string) Scope {
47 1
	s.propertyPath = s.propertyPath.WithProperty(name)
48
49 1
	return s
50
}
51
52
// AtIndex returns a copy of the scope with property path appended by the given array index.
53
func (s Scope) AtIndex(index int) Scope {
54 1
	s.propertyPath = s.propertyPath.WithIndex(index)
55
56 1
	return s
57
}
58
59
// Validator creates a new validator for the given scope. This validator can be used to perform
60
// complex validation on a custom constraint using existing constraints.
61
func (s Scope) Validator() *Validator {
62 1
	return newScopedValidator(s)
63
}
64
65
// IsIgnored is the reverse condition for applying validation groups to the IsApplied method.
66
// It is recommended to use this method in every validation method of the constraint.
67
func (s Scope) IsIgnored(groups ...string) bool {
68 1
	return !s.IsApplied(groups...)
69
}
70
71
// IsApplied compares scope validation groups and constraint groups. If one of the scope groups intersects with
72
// the constraint groups, the validation scope should be applied (returns true).
73
// Empty groups are treated as DefaultGroup. To set validation groups use the validator.WithGroups() method.
74
func (s Scope) IsApplied(groups ...string) bool {
75 1
	if len(s.groups) == 0 {
76 1
		if len(groups) == 0 {
77 1
			return true
78
		}
79 1
		for _, g := range groups {
80 1
			if g == DefaultGroup {
81 1
				return true
82
			}
83
		}
84
	}
85
86 1
	for _, g1 := range s.groups {
87 1
		if len(groups) == 0 {
88 1
			if g1 == DefaultGroup {
89 1
				return true
90
			}
91
		}
92 1
		for _, g2 := range groups {
93 1
			if g1 == g2 {
94 1
				return true
95
			}
96
		}
97
	}
98
99 1
	return false
100
}
101
102
func (s *Scope) applyOptions(options ...Option) error {
103 1
	for _, option := range options {
104 1
		var err error
105 1
		if o, ok := option.(internalOption); ok {
106 1
			err = o.setUpOnScope(s)
107
		} else {
108 1
			err = option.SetUp()
109
		}
110 1
		if err != nil {
111 1
			return s.describeOptionError(option, err)
112
		}
113
	}
114
115 1
	return nil
116
}
117
118
func (s *Scope) describeOptionError(option Option, err error) error {
119 1
	c, ok := option.(Constraint)
120 1
	if !ok {
121
		return fmt.Errorf(`failed to set up option: %w`, err)
122
	}
123
124 1
	if s.propertyPath == nil {
125 1
		err = fmt.Errorf(`failed to set up constraint "%s": %w`, c.Name(), err)
126
	} else {
127 1
		err = fmt.Errorf(`failed to set up constraint "%s" at path "%s": %w`, c.Name(), s.propertyPath.String(), err)
128
	}
129
130 1
	return err
131
}
132
133
func (s Scope) withContext(ctx context.Context) Scope {
134 1
	s.context = ctx
135
136 1
	return s
137
}
138
139
func (s Scope) withLanguage(tag language.Tag) Scope {
140 1
	s.language = tag
141
142 1
	return s
143
}
144
145
func (s Scope) withGroups(groups ...string) Scope {
146 1
	s.groups = groups
147 1
	return s
148
}
149
150
func newScope(
151
	translator Translator,
152
	violationFactory ViolationFactory,
153
	constraints map[string]Constraint,
154
) Scope {
155 1
	return Scope{
156
		context:          context.Background(),
157
		translator:       translator,
158
		violationFactory: violationFactory,
159
		constraints:      constraints,
160
	}
161
}
162