1 package version
2
3 import (
4 "fmt"
5 "reflect"
6 "regexp"
7 "sort"
8 "strings"
9 )
10
11
12
13 type Constraint struct {
14 f constraintFunc
15 op operator
16 check *Version
17 original string
18 }
19
20 func (c *Constraint) Equals(con *Constraint) bool {
21 return c.op == con.op && c.check.Equal(con.check)
22 }
23
24
25
26 type Constraints []*Constraint
27
28 type constraintFunc func(v, c *Version) bool
29
30 var constraintOperators map[string]constraintOperation
31
32 type constraintOperation struct {
33 op operator
34 f constraintFunc
35 }
36
37 var constraintRegexp *regexp.Regexp
38
39 func init() {
40 constraintOperators = map[string]constraintOperation{
41 "": {op: equal, f: constraintEqual},
42 "=": {op: equal, f: constraintEqual},
43 "!=": {op: notEqual, f: constraintNotEqual},
44 ">": {op: greaterThan, f: constraintGreaterThan},
45 "<": {op: lessThan, f: constraintLessThan},
46 ">=": {op: greaterThanEqual, f: constraintGreaterThanEqual},
47 "<=": {op: lessThanEqual, f: constraintLessThanEqual},
48 "~>": {op: pessimistic, f: constraintPessimistic},
49 }
50
51 ops := make([]string, 0, len(constraintOperators))
52 for k := range constraintOperators {
53 ops = append(ops, regexp.QuoteMeta(k))
54 }
55
56 constraintRegexp = regexp.MustCompile(fmt.Sprintf(
57 `^\s*(%s)\s*(%s)\s*$`,
58 strings.Join(ops, "|"),
59 VersionRegexpRaw))
60 }
61
62
63
64
65 func NewConstraint(v string) (Constraints, error) {
66 vs := strings.Split(v, ",")
67 result := make([]*Constraint, len(vs))
68 for i, single := range vs {
69 c, err := parseSingle(single)
70 if err != nil {
71 return nil, err
72 }
73
74 result[i] = c
75 }
76
77 return Constraints(result), nil
78 }
79
80
81
82 func MustConstraints(c Constraints, err error) Constraints {
83 if err != nil {
84 panic(err)
85 }
86
87 return c
88 }
89
90
91 func (cs Constraints) Check(v *Version) bool {
92 for _, c := range cs {
93 if !c.Check(v) {
94 return false
95 }
96 }
97
98 return true
99 }
100
101
102
103
104
105
106
107
108
109 func (cs Constraints) Equals(c Constraints) bool {
110 if len(cs) != len(c) {
111 return false
112 }
113
114
115 left := make(Constraints, len(cs))
116 copy(left, cs)
117 sort.Stable(left)
118 right := make(Constraints, len(c))
119 copy(right, c)
120 sort.Stable(right)
121
122
123 for i, con := range left {
124 if !con.Equals(right[i]) {
125 return false
126 }
127 }
128
129 return true
130 }
131
132 func (cs Constraints) Len() int {
133 return len(cs)
134 }
135
136 func (cs Constraints) Less(i, j int) bool {
137 if cs[i].op < cs[j].op {
138 return true
139 }
140 if cs[i].op > cs[j].op {
141 return false
142 }
143
144 return cs[i].check.LessThan(cs[j].check)
145 }
146
147 func (cs Constraints) Swap(i, j int) {
148 cs[i], cs[j] = cs[j], cs[i]
149 }
150
151
152 func (cs Constraints) String() string {
153 csStr := make([]string, len(cs))
154 for i, c := range cs {
155 csStr[i] = c.String()
156 }
157
158 return strings.Join(csStr, ",")
159 }
160
161
162 func (c *Constraint) Check(v *Version) bool {
163 return c.f(v, c.check)
164 }
165
166
167
168 func (c *Constraint) Prerelease() bool {
169 return len(c.check.Prerelease()) > 0
170 }
171
172 func (c *Constraint) String() string {
173 return c.original
174 }
175
176 func parseSingle(v string) (*Constraint, error) {
177 matches := constraintRegexp.FindStringSubmatch(v)
178 if matches == nil {
179 return nil, fmt.Errorf("Malformed constraint: %s", v)
180 }
181
182 check, err := NewVersion(matches[2])
183 if err != nil {
184 return nil, err
185 }
186
187 cop := constraintOperators[matches[1]]
188
189 return &Constraint{
190 f: cop.f,
191 op: cop.op,
192 check: check,
193 original: v,
194 }, nil
195 }
196
197 func prereleaseCheck(v, c *Version) bool {
198 switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; {
199 case cPre && vPre:
200
201
202 return reflect.DeepEqual(c.Segments64(), v.Segments64())
203
204 case !cPre && vPre:
205
206
207 return false
208
209 case cPre && !vPre:
210
211 case !cPre && !vPre:
212
213 }
214 return true
215 }
216
217
218
219
220
221 type operator rune
222
223 const (
224 equal operator = '='
225 notEqual operator = '≠'
226 greaterThan operator = '>'
227 lessThan operator = '<'
228 greaterThanEqual operator = '≥'
229 lessThanEqual operator = '≤'
230 pessimistic operator = '~'
231 )
232
233 func constraintEqual(v, c *Version) bool {
234 return v.Equal(c)
235 }
236
237 func constraintNotEqual(v, c *Version) bool {
238 return !v.Equal(c)
239 }
240
241 func constraintGreaterThan(v, c *Version) bool {
242 return prereleaseCheck(v, c) && v.Compare(c) == 1
243 }
244
245 func constraintLessThan(v, c *Version) bool {
246 return prereleaseCheck(v, c) && v.Compare(c) == -1
247 }
248
249 func constraintGreaterThanEqual(v, c *Version) bool {
250 return prereleaseCheck(v, c) && v.Compare(c) >= 0
251 }
252
253 func constraintLessThanEqual(v, c *Version) bool {
254 return prereleaseCheck(v, c) && v.Compare(c) <= 0
255 }
256
257 func constraintPessimistic(v, c *Version) bool {
258
259 if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") {
260 return false
261 }
262
263
264
265 if v.LessThan(c) {
266 return false
267 }
268
269
270 cs := len(c.segments)
271
272
273
274 if cs > len(v.segments) {
275 return false
276 }
277
278
279
280
281 for i := 0; i < c.si-1; i++ {
282 if v.segments[i] != c.segments[i] {
283 return false
284 }
285 }
286
287
288
289
290 if c.segments[cs-1] > v.segments[cs-1] {
291 return false
292 }
293
294
295 return true
296 }
297
View as plain text