-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathconstraint.go
157 lines (137 loc) · 3.73 KB
/
constraint.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
154
155
156
package version
import (
"errors"
"fmt"
"regexp"
"strings"
)
var constraintRegex = regexp.MustCompile(`^(?:(>=|>|<=|<|!=|==?)\s*)?(.+)$`)
type constraintFunc func(a, b *Version) bool
type constraint struct {
f constraintFunc
b *Version
original string
}
// Constraints is a collection of version constraint rules that can be checked against a version.
type Constraints []constraint
// NewConstraint parses a string into a Constraints object that can be used to check
// if a given version satisfies the constraint.
func NewConstraint(cs string) (Constraints, error) {
parts := strings.Split(cs, ",")
newC := make(Constraints, len(parts))
for i, p := range parts {
parts[i] = strings.TrimSpace(p)
}
for i, p := range parts {
c, err := newConstraint(p)
if err != nil {
return Constraints{}, err
}
newC[i] = c
}
return newC, nil
}
// MustConstraint is like NewConstraint but panics if the constraint is invalid.
func MustConstraint(cs string) Constraints {
c, err := NewConstraint(cs)
if err != nil {
panic("github.com/k0sproject/version: NewConstraint: " + err.Error())
}
return c
}
// String returns the constraint as a string.
func (cs Constraints) String() string {
s := make([]string, len(cs))
for i, c := range cs {
s[i] = c.String()
}
return strings.Join(s, ", ")
}
// Check returns true if the given version satisfies all of the constraints.
func (cs Constraints) Check(v *Version) bool {
for _, c := range cs {
if c.b.Prerelease() == "" && v.Prerelease() != "" {
return false
}
if !c.f(c.b, v) {
return false
}
}
return true
}
// CheckString is like Check but takes a string version. If the version is invalid,
// it returns false.
func (cs Constraints) CheckString(v string) bool {
vv, err := NewVersion(v)
if err != nil {
return false
}
return cs.Check(vv)
}
// String returns the original constraint string.
func (c *constraint) String() string {
return c.original
}
func newConstraint(s string) (constraint, error) {
match := constraintRegex.FindStringSubmatch(s)
if len(match) != 3 {
return constraint{}, errors.New("invalid constraint: " + s)
}
op := match[1]
f, err := opfunc(op)
if err != nil {
return constraint{}, err
}
// convert one or two digit constraints to threes digit unless it's an equality operation
if op != "" && op != "=" && op != "==" {
segments := strings.Split(match[2], ".")
if len(segments) < 3 {
lastSegment := segments[len(segments)-1]
var pre string
if strings.Contains(lastSegment, "-") {
parts := strings.Split(lastSegment, "-")
segments[len(segments)-1] = parts[0]
pre = "-" + parts[1]
}
switch len(segments) {
case 1:
// >= 1 becomes >= 1.0.0
// >= 1-rc.1 becomes >= 1.0.0-rc.1
return newConstraint(fmt.Sprintf("%s %s.0.0%s", op, segments[0], pre))
case 2:
// >= 1.1 becomes >= 1.1.0
// >= 1.1-rc.1 becomes >= 1.1.0-rc.1
return newConstraint(fmt.Sprintf("%s %s.%s.0%s", op, segments[0], segments[1], pre))
}
}
}
target, err := NewVersion(match[2])
if err != nil {
return constraint{}, err
}
return constraint{f: f, b: target, original: s}, nil
}
func opfunc(s string) (constraintFunc, error) {
switch s {
case "", "=", "==":
return eq, nil
case ">":
return gt, nil
case ">=":
return gte, nil
case "<":
return lt, nil
case "<=":
return lte, nil
case "!=":
return neq, nil
default:
return nil, errors.New("invalid operator: " + s)
}
}
func gt(a, b *Version) bool { return b.GreaterThan(a) }
func lt(a, b *Version) bool { return b.LessThan(a) }
func gte(a, b *Version) bool { return b.GreaterThanOrEqual(a) }
func lte(a, b *Version) bool { return b.LessThanOrEqual(a) }
func eq(a, b *Version) bool { return b.Equal(a) }
func neq(a, b *Version) bool { return !b.Equal(a) }