Passed
Pull Request — main (#48)
by Igor
02:21
created

scope.go   A

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Test Coverage

Coverage 97.06%

Importance

Changes 0
Metric Value
cc 17
eloc 63
dl 0
loc 112
ccs 33
cts 34
cp 0.9706
crap 17.0073
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A validation.Scope.BuildViolation 0 11 3
A validation.Scope.Context 0 2 1
A validation.Scope.withContext 0 4 1
A validation.*Scope.applyOptions 0 14 4
A validation.*Scope.describeOptionError 0 13 3
A validation.Scope.atProperty 0 4 1
A validation.Scope.Validator 0 2 1
A validation.Scope.withLanguage 0 4 1
A validation.newScope 0 8 1
A validation.Scope.atIndex 0 4 1
1
package validation
2
3
import (
4
	"context"
5
	"fmt"
6
7
	languagepkg "github.com/muonsoft/language"
8
	"golang.org/x/text/language"
9
)
10
11
// Scope holds the current state of validation. On the client-side of the package,
12
// it can be used to build violations.
13
type Scope struct {
14
	context          context.Context
15
	propertyPath     *PropertyPath
16
	language         language.Tag
17
	translator       *Translator
18
	violationFactory ViolationFactory
19
	constraints      map[string]Constraint
20
}
21
22
// Context returns context value that was passed to the validator by Context argument or
23
// by creating scoped validator with the validator.WithContext method.
24
func (s Scope) Context() context.Context {
25 1
	return s.context
26
}
27
28
// BuildViolation is used to create violations in validation methods of constraints.
29
// This method automatically injects the property path and language of the current validation scope.
30
func (s Scope) BuildViolation(code, message string) *ViolationBuilder {
31 1
	b := NewViolationBuilder(s.violationFactory).BuildViolation(code, message)
32 1
	b.SetPropertyPath(s.propertyPath)
33
34 1
	if s.language != language.Und {
35 1
		b.SetLanguage(s.language)
36 1
	} else if s.context != nil {
37 1
		b.SetLanguage(languagepkg.FromContext(s.context))
38
	}
39
40 1
	return b
41
}
42
43
// Validator creates a new validation for the given scope. This validator can be used
44
// to perform complex validation on a custom constraint by using the
45
// already existing constraints.
46
func (s Scope) Validator() *Validator {
47 1
	return newScopedValidator(s)
48
}
49
50
func (s *Scope) applyOptions(options ...Option) error {
51 1
	for _, option := range options {
52 1
		var err error
53 1
		if o, ok := option.(internalOption); ok {
54 1
			err = o.setUpOnScope(s)
55
		} else {
56 1
			err = option.SetUp()
57
		}
58 1
		if err != nil {
59 1
			return s.describeOptionError(option, err)
60
		}
61
	}
62
63 1
	return nil
64
}
65
66
func (s *Scope) describeOptionError(option Option, err error) error {
67 1
	c, ok := option.(Constraint)
68 1
	if !ok {
69
		return fmt.Errorf(`failed to set up option: %w`, err)
70
	}
71
72 1
	if s.propertyPath == nil {
73 1
		err = fmt.Errorf(`failed to set up constraint "%s": %w`, c.Name(), err)
74
	} else {
75 1
		err = fmt.Errorf(`failed to set up constraint "%s" at path "%s": %w`, c.Name(), s.propertyPath.String(), err)
76
	}
77
78 1
	return err
79
}
80
81
func (s Scope) withContext(ctx context.Context) Scope {
82 1
	s.context = ctx
83
84 1
	return s
85
}
86
87
func (s Scope) withLanguage(tag language.Tag) Scope {
88 1
	s.language = tag
89
90 1
	return s
91
}
92
93
func (s Scope) atProperty(name string) Scope {
94 1
	s.propertyPath = s.propertyPath.WithProperty(name)
95
96 1
	return s
97
}
98
99
func (s Scope) atIndex(index int) Scope {
100 1
	s.propertyPath = s.propertyPath.WithIndex(index)
101
102 1
	return s
103
}
104
105
func newScope() Scope {
106 1
	translator := newTranslator()
107
108 1
	return Scope{
109
		context:          context.Background(),
110
		translator:       translator,
111
		violationFactory: newViolationFactory(translator),
112
		constraints:      make(map[string]Constraint),
113
	}
114
}
115