OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / hashicorp / go-version / version.go
1 package version
2
3 import (
4         "bytes"
5         "fmt"
6         "reflect"
7         "regexp"
8         "strconv"
9         "strings"
10 )
11
12 // The compiled regular expression used to test the validity of a version.
13 var versionRegexp *regexp.Regexp
14
15 // The raw regular expression string used for testing the validity
16 // of a version.
17 const VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
18         `(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
19         `(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
20         `?`
21
22 // Version represents a single version.
23 type Version struct {
24         metadata string
25         pre      string
26         segments []int64
27         si       int
28         original string
29 }
30
31 func init() {
32         versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
33 }
34
35 // NewVersion parses the given version and returns a new
36 // Version.
37 func NewVersion(v string) (*Version, error) {
38         matches := versionRegexp.FindStringSubmatch(v)
39         if matches == nil {
40                 return nil, fmt.Errorf("Malformed version: %s", v)
41         }
42         segmentsStr := strings.Split(matches[1], ".")
43         segments := make([]int64, len(segmentsStr))
44         si := 0
45         for i, str := range segmentsStr {
46                 val, err := strconv.ParseInt(str, 10, 64)
47                 if err != nil {
48                         return nil, fmt.Errorf(
49                                 "Error parsing version: %s", err)
50                 }
51
52                 segments[i] = int64(val)
53                 si++
54         }
55
56         // Even though we could support more than three segments, if we
57         // got less than three, pad it with 0s. This is to cover the basic
58         // default usecase of semver, which is MAJOR.MINOR.PATCH at the minimum
59         for i := len(segments); i < 3; i++ {
60                 segments = append(segments, 0)
61         }
62
63         pre := matches[7]
64         if pre == "" {
65                 pre = matches[4]
66         }
67
68         return &Version{
69                 metadata: matches[10],
70                 pre:      pre,
71                 segments: segments,
72                 si:       si,
73                 original: v,
74         }, nil
75 }
76
77 // Must is a helper that wraps a call to a function returning (*Version, error)
78 // and panics if error is non-nil.
79 func Must(v *Version, err error) *Version {
80         if err != nil {
81                 panic(err)
82         }
83
84         return v
85 }
86
87 // Compare compares this version to another version. This
88 // returns -1, 0, or 1 if this version is smaller, equal,
89 // or larger than the other version, respectively.
90 //
91 // If you want boolean results, use the LessThan, Equal,
92 // or GreaterThan methods.
93 func (v *Version) Compare(other *Version) int {
94         // A quick, efficient equality check
95         if v.String() == other.String() {
96                 return 0
97         }
98
99         segmentsSelf := v.Segments64()
100         segmentsOther := other.Segments64()
101
102         // If the segments are the same, we must compare on prerelease info
103         if reflect.DeepEqual(segmentsSelf, segmentsOther) {
104                 preSelf := v.Prerelease()
105                 preOther := other.Prerelease()
106                 if preSelf == "" && preOther == "" {
107                         return 0
108                 }
109                 if preSelf == "" {
110                         return 1
111                 }
112                 if preOther == "" {
113                         return -1
114                 }
115
116                 return comparePrereleases(preSelf, preOther)
117         }
118
119         // Get the highest specificity (hS), or if they're equal, just use segmentSelf length
120         lenSelf := len(segmentsSelf)
121         lenOther := len(segmentsOther)
122         hS := lenSelf
123         if lenSelf < lenOther {
124                 hS = lenOther
125         }
126         // Compare the segments
127         // Because a constraint could have more/less specificity than the version it's
128         // checking, we need to account for a lopsided or jagged comparison
129         for i := 0; i < hS; i++ {
130                 if i > lenSelf-1 {
131                         // This means Self had the lower specificity
132                         // Check to see if the remaining segments in Other are all zeros
133                         if !allZero(segmentsOther[i:]) {
134                                 // if not, it means that Other has to be greater than Self
135                                 return -1
136                         }
137                         break
138                 } else if i > lenOther-1 {
139                         // this means Other had the lower specificity
140                         // Check to see if the remaining segments in Self are all zeros -
141                         if !allZero(segmentsSelf[i:]) {
142                                 //if not, it means that Self has to be greater than Other
143                                 return 1
144                         }
145                         break
146                 }
147                 lhs := segmentsSelf[i]
148                 rhs := segmentsOther[i]
149                 if lhs == rhs {
150                         continue
151                 } else if lhs < rhs {
152                         return -1
153                 }
154                 // Otherwis, rhs was > lhs, they're not equal
155                 return 1
156         }
157
158         // if we got this far, they're equal
159         return 0
160 }
161
162 func allZero(segs []int64) bool {
163         for _, s := range segs {
164                 if s != 0 {
165                         return false
166                 }
167         }
168         return true
169 }
170
171 func comparePart(preSelf string, preOther string) int {
172         if preSelf == preOther {
173                 return 0
174         }
175
176         var selfInt int64
177         selfNumeric := true
178         selfInt, err := strconv.ParseInt(preSelf, 10, 64)
179         if err != nil {
180                 selfNumeric = false
181         }
182
183         var otherInt int64
184         otherNumeric := true
185         otherInt, err = strconv.ParseInt(preOther, 10, 64)
186         if err != nil {
187                 otherNumeric = false
188         }
189
190         // if a part is empty, we use the other to decide
191         if preSelf == "" {
192                 if otherNumeric {
193                         return -1
194                 }
195                 return 1
196         }
197
198         if preOther == "" {
199                 if selfNumeric {
200                         return 1
201                 }
202                 return -1
203         }
204
205         if selfNumeric && !otherNumeric {
206                 return -1
207         } else if !selfNumeric && otherNumeric {
208                 return 1
209         } else if !selfNumeric && !otherNumeric && preSelf > preOther {
210                 return 1
211         } else if selfInt > otherInt {
212                 return 1
213         }
214
215         return -1
216 }
217
218 func comparePrereleases(v string, other string) int {
219         // the same pre release!
220         if v == other {
221                 return 0
222         }
223
224         // split both pre releases for analyse their parts
225         selfPreReleaseMeta := strings.Split(v, ".")
226         otherPreReleaseMeta := strings.Split(other, ".")
227
228         selfPreReleaseLen := len(selfPreReleaseMeta)
229         otherPreReleaseLen := len(otherPreReleaseMeta)
230
231         biggestLen := otherPreReleaseLen
232         if selfPreReleaseLen > otherPreReleaseLen {
233                 biggestLen = selfPreReleaseLen
234         }
235
236         // loop for parts to find the first difference
237         for i := 0; i < biggestLen; i = i + 1 {
238                 partSelfPre := ""
239                 if i < selfPreReleaseLen {
240                         partSelfPre = selfPreReleaseMeta[i]
241                 }
242
243                 partOtherPre := ""
244                 if i < otherPreReleaseLen {
245                         partOtherPre = otherPreReleaseMeta[i]
246                 }
247
248                 compare := comparePart(partSelfPre, partOtherPre)
249                 // if parts are equals, continue the loop
250                 if compare != 0 {
251                         return compare
252                 }
253         }
254
255         return 0
256 }
257
258 // Equal tests if two versions are equal.
259 func (v *Version) Equal(o *Version) bool {
260         return v.Compare(o) == 0
261 }
262
263 // GreaterThan tests if this version is greater than another version.
264 func (v *Version) GreaterThan(o *Version) bool {
265         return v.Compare(o) > 0
266 }
267
268 // LessThan tests if this version is less than another version.
269 func (v *Version) LessThan(o *Version) bool {
270         return v.Compare(o) < 0
271 }
272
273 // Metadata returns any metadata that was part of the version
274 // string.
275 //
276 // Metadata is anything that comes after the "+" in the version.
277 // For example, with "1.2.3+beta", the metadata is "beta".
278 func (v *Version) Metadata() string {
279         return v.metadata
280 }
281
282 // Prerelease returns any prerelease data that is part of the version,
283 // or blank if there is no prerelease data.
284 //
285 // Prerelease information is anything that comes after the "-" in the
286 // version (but before any metadata). For example, with "1.2.3-beta",
287 // the prerelease information is "beta".
288 func (v *Version) Prerelease() string {
289         return v.pre
290 }
291
292 // Segments returns the numeric segments of the version as a slice of ints.
293 //
294 // This excludes any metadata or pre-release information. For example,
295 // for a version "1.2.3-beta", segments will return a slice of
296 // 1, 2, 3.
297 func (v *Version) Segments() []int {
298         segmentSlice := make([]int, len(v.segments))
299         for i, v := range v.segments {
300                 segmentSlice[i] = int(v)
301         }
302         return segmentSlice
303 }
304
305 // Segments64 returns the numeric segments of the version as a slice of int64s.
306 //
307 // This excludes any metadata or pre-release information. For example,
308 // for a version "1.2.3-beta", segments will return a slice of
309 // 1, 2, 3.
310 func (v *Version) Segments64() []int64 {
311         result := make([]int64, len(v.segments))
312         copy(result, v.segments)
313         return result
314 }
315
316 // String returns the full version string included pre-release
317 // and metadata information.
318 //
319 // This value is rebuilt according to the parsed segments and other
320 // information. Therefore, ambiguities in the version string such as
321 // prefixed zeroes (1.04.0 => 1.4.0), `v` prefix (v1.0.0 => 1.0.0), and
322 // missing parts (1.0 => 1.0.0) will be made into a canonicalized form
323 // as shown in the parenthesized examples.
324 func (v *Version) String() string {
325         var buf bytes.Buffer
326         fmtParts := make([]string, len(v.segments))
327         for i, s := range v.segments {
328                 // We can ignore err here since we've pre-parsed the values in segments
329                 str := strconv.FormatInt(s, 10)
330                 fmtParts[i] = str
331         }
332         fmt.Fprintf(&buf, strings.Join(fmtParts, "."))
333         if v.pre != "" {
334                 fmt.Fprintf(&buf, "-%s", v.pre)
335         }
336         if v.metadata != "" {
337                 fmt.Fprintf(&buf, "+%s", v.metadata)
338         }
339
340         return buf.String()
341 }
342
343 // Original returns the original parsed version as-is, including any
344 // potential whitespace, `v` prefix, etc.
345 func (v *Version) Original() string {
346         return v.original
347 }