-
Notifications
You must be signed in to change notification settings - Fork 4
/
reference.go
153 lines (132 loc) · 3.54 KB
/
reference.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package validator
import (
"errors"
"sync"
"github.com/lestrrat/go-pdebug"
)
// RefResolver is a mandatory object that you must pass to a
// ReferenceConstraint upon its creation. This is responsible
// for resolving the reference to an actual constraint.
type RefResolver interface {
GetReference(string) (Constraint, error)
}
// ConstraintMap is an implementation of RefResolver
type ConstraintMap struct {
lock sync.Mutex
refs map[string]Constraint
}
// Len returns the number of references stored in this ConstraintMap
func (cm ConstraintMap) Len() int {
return len(cm.refmap())
}
func (cm *ConstraintMap) refmap() map[string]Constraint {
if cm.refs == nil {
cm.refs = make(map[string]Constraint)
}
return cm.refs
}
// SetReference registeres a new Constraint to a name
func (cm *ConstraintMap) SetReference(name string, c Constraint) {
cm.lock.Lock()
defer cm.lock.Unlock()
refs := cm.refmap()
refs[name] = c
}
// GetReference fetches the Constraint associated with the given name
func (cm *ConstraintMap) GetReference(name string) (Constraint, error) {
cm.lock.Lock()
defer cm.lock.Unlock()
refs := cm.refmap()
c, ok := refs[name]
if !ok {
return nil, errors.New("reference '" + name + "' not found")
}
return c, nil
}
// ReferenceConstraint is a constraint where its actual definition
// is stored elsewhere.
type ReferenceConstraint struct {
resolver RefResolver
lock sync.Mutex
resolved Constraint
reference string
}
// Reference creates a new ReferenceConstraint object
func Reference(resolver RefResolver) *ReferenceConstraint {
return &ReferenceConstraint{
resolver: resolver,
}
}
// Resolved returns the Constraint obtained by resolving
// the reference.
func (r *ReferenceConstraint) Resolved() (c Constraint, err error) {
if pdebug.Enabled {
g := pdebug.IPrintf("START ReferenceConstraint.Resolved '%s'", r.reference)
defer func() {
if err == nil {
g.IRelease("END ReferenceConstraint.Resolved '%s' (OK)", r.reference)
} else {
g.IRelease("END ReferenceConstraint.Resolved '%s' (FAIL): %s", r.reference, err)
}
}()
}
r.lock.Lock()
defer r.lock.Unlock()
if r.resolved != nil {
if pdebug.Enabled {
pdebug.Printf("Reference is already resolved")
}
return r.resolved, nil
}
c, err = r.resolver.GetReference(r.reference)
if err != nil {
return nil, err
}
r.resolved = c
return c, nil
}
// RefersTo specifies the reference string that this constraint points to
func (r *ReferenceConstraint) RefersTo(s string) *ReferenceConstraint {
r.reference = s
return r
}
// Default is a no op for this type
func (r *ReferenceConstraint) Default(_ interface{}) {
}
// DefaultValue returns the default value from the constraint pointed
// by the reference
func (r *ReferenceConstraint) DefaultValue() interface{} {
c, err := r.Resolved()
if err != nil {
return nil
}
return c.DefaultValue()
}
// HasDefault returns true if the constraint pointed by the reference
// has defaults
func (r *ReferenceConstraint) HasDefault() bool {
c, err := r.Resolved()
if err != nil {
return false
}
return c.HasDefault()
}
// Validate validates the value against the constraint pointed to
// by the reference.
func (r *ReferenceConstraint) Validate(v interface{}) (err error) {
if pdebug.Enabled {
g := pdebug.IPrintf("START ReferenceConstraint.Validate")
defer func() {
if err == nil {
g.IRelease("END ReferenceConstraint.Validate (PASS)")
} else {
g.IRelease("END ReferenceConstraint.Validate (FAIL): %s", err)
}
}()
}
c, err := r.Resolved()
if err != nil {
return err
}
return c.Validate(v)
}