OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / hashicorp / go-version / constraint.go
1 package version
2
3 import (
4         "fmt"
5         "reflect"
6         "regexp"
7         "strings"
8 )
9
10 // Constraint represents a single constraint for a version, such as
11 // ">= 1.0".
12 type Constraint struct {
13         f        constraintFunc
14         check    *Version
15         original string
16 }
17
18 // Constraints is a slice of constraints. We make a custom type so that
19 // we can add methods to it.
20 type Constraints []*Constraint
21
22 type constraintFunc func(v, c *Version) bool
23
24 var constraintOperators map[string]constraintFunc
25
26 var constraintRegexp *regexp.Regexp
27
28 func init() {
29         constraintOperators = map[string]constraintFunc{
30                 "":   constraintEqual,
31                 "=":  constraintEqual,
32                 "!=": constraintNotEqual,
33                 ">":  constraintGreaterThan,
34                 "<":  constraintLessThan,
35                 ">=": constraintGreaterThanEqual,
36                 "<=": constraintLessThanEqual,
37                 "~>": constraintPessimistic,
38         }
39
40         ops := make([]string, 0, len(constraintOperators))
41         for k := range constraintOperators {
42                 ops = append(ops, regexp.QuoteMeta(k))
43         }
44
45         constraintRegexp = regexp.MustCompile(fmt.Sprintf(
46                 `^\s*(%s)\s*(%s)\s*$`,
47                 strings.Join(ops, "|"),
48                 VersionRegexpRaw))
49 }
50
51 // NewConstraint will parse one or more constraints from the given
52 // constraint string. The string must be a comma-separated list of
53 // constraints.
54 func NewConstraint(v string) (Constraints, error) {
55         vs := strings.Split(v, ",")
56         result := make([]*Constraint, len(vs))
57         for i, single := range vs {
58                 c, err := parseSingle(single)
59                 if err != nil {
60                         return nil, err
61                 }
62
63                 result[i] = c
64         }
65
66         return Constraints(result), nil
67 }
68
69 // Check tests if a version satisfies all the constraints.
70 func (cs Constraints) Check(v *Version) bool {
71         for _, c := range cs {
72                 if !c.Check(v) {
73                         return false
74                 }
75         }
76
77         return true
78 }
79
80 // Returns the string format of the constraints
81 func (cs Constraints) String() string {
82         csStr := make([]string, len(cs))
83         for i, c := range cs {
84                 csStr[i] = c.String()
85         }
86
87         return strings.Join(csStr, ",")
88 }
89
90 // Check tests if a constraint is validated by the given version.
91 func (c *Constraint) Check(v *Version) bool {
92         return c.f(v, c.check)
93 }
94
95 func (c *Constraint) String() string {
96         return c.original
97 }
98
99 func parseSingle(v string) (*Constraint, error) {
100         matches := constraintRegexp.FindStringSubmatch(v)
101         if matches == nil {
102                 return nil, fmt.Errorf("Malformed constraint: %s", v)
103         }
104
105         check, err := NewVersion(matches[2])
106         if err != nil {
107                 return nil, err
108         }
109
110         return &Constraint{
111                 f:        constraintOperators[matches[1]],
112                 check:    check,
113                 original: v,
114         }, nil
115 }
116
117 func prereleaseCheck(v, c *Version) bool {
118         switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; {
119         case cPre && vPre:
120                 // A constraint with a pre-release can only match a pre-release version
121                 // with the same base segments.
122                 return reflect.DeepEqual(c.Segments64(), v.Segments64())
123
124         case !cPre && vPre:
125                 // A constraint without a pre-release can only match a version without a
126                 // pre-release.
127                 return false
128
129         case cPre && !vPre:
130                 // OK, except with the pessimistic operator
131         case !cPre && !vPre:
132                 // OK
133         }
134         return true
135 }
136
137 //-------------------------------------------------------------------
138 // Constraint functions
139 //-------------------------------------------------------------------
140
141 func constraintEqual(v, c *Version) bool {
142         return v.Equal(c)
143 }
144
145 func constraintNotEqual(v, c *Version) bool {
146         return !v.Equal(c)
147 }
148
149 func constraintGreaterThan(v, c *Version) bool {
150         return prereleaseCheck(v, c) && v.Compare(c) == 1
151 }
152
153 func constraintLessThan(v, c *Version) bool {
154         return prereleaseCheck(v, c) && v.Compare(c) == -1
155 }
156
157 func constraintGreaterThanEqual(v, c *Version) bool {
158         return prereleaseCheck(v, c) && v.Compare(c) >= 0
159 }
160
161 func constraintLessThanEqual(v, c *Version) bool {
162         return prereleaseCheck(v, c) && v.Compare(c) <= 0
163 }
164
165 func constraintPessimistic(v, c *Version) bool {
166         // Using a pessimistic constraint with a pre-release, restricts versions to pre-releases
167         if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") {
168                 return false
169         }
170
171         // If the version being checked is naturally less than the constraint, then there
172         // is no way for the version to be valid against the constraint
173         if v.LessThan(c) {
174                 return false
175         }
176         // We'll use this more than once, so grab the length now so it's a little cleaner
177         // to write the later checks
178         cs := len(c.segments)
179
180         // If the version being checked has less specificity than the constraint, then there
181         // is no way for the version to be valid against the constraint
182         if cs > len(v.segments) {
183                 return false
184         }
185
186         // Check the segments in the constraint against those in the version. If the version
187         // being checked, at any point, does not have the same values in each index of the
188         // constraints segments, then it cannot be valid against the constraint.
189         for i := 0; i < c.si-1; i++ {
190                 if v.segments[i] != c.segments[i] {
191                         return false
192                 }
193         }
194
195         // Check the last part of the segment in the constraint. If the version segment at
196         // this index is less than the constraints segment at this index, then it cannot
197         // be valid against the constraint
198         if c.segments[cs-1] > v.segments[cs-1] {
199                 return false
200         }
201
202         // If nothing has rejected the version by now, it's valid
203         return true
204 }