1 package version
2
3 import (
4 "bytes"
5 "fmt"
6 "reflect"
7 "regexp"
8 "strconv"
9 "strings"
10 )
11
12
13 var (
14 versionRegexp *regexp.Regexp
15 semverRegexp *regexp.Regexp
16 )
17
18
19
20 const (
21 VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
22 `(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
23 `(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
24 `?`
25
26
27 SemverRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
28 `(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
29 `(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
30 `?`
31 )
32
33
34 type Version struct {
35 metadata string
36 pre string
37 segments []int64
38 si int
39 original string
40 }
41
42 func init() {
43 versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
44 semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
45 }
46
47
48
49 func NewVersion(v string) (*Version, error) {
50 return newVersion(v, versionRegexp)
51 }
52
53
54
55
56 func NewSemver(v string) (*Version, error) {
57 return newVersion(v, semverRegexp)
58 }
59
60 func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
61 matches := pattern.FindStringSubmatch(v)
62 if matches == nil {
63 return nil, fmt.Errorf("Malformed version: %s", v)
64 }
65 segmentsStr := strings.Split(matches[1], ".")
66 segments := make([]int64, len(segmentsStr))
67 for i, str := range segmentsStr {
68 val, err := strconv.ParseInt(str, 10, 64)
69 if err != nil {
70 return nil, fmt.Errorf(
71 "Error parsing version: %s", err)
72 }
73
74 segments[i] = val
75 }
76
77
78
79
80 for i := len(segments); i < 3; i++ {
81 segments = append(segments, 0)
82 }
83
84 pre := matches[7]
85 if pre == "" {
86 pre = matches[4]
87 }
88
89 return &Version{
90 metadata: matches[10],
91 pre: pre,
92 segments: segments,
93 si: len(segmentsStr),
94 original: v,
95 }, nil
96 }
97
98
99
100 func Must(v *Version, err error) *Version {
101 if err != nil {
102 panic(err)
103 }
104
105 return v
106 }
107
108
109
110
111
112
113
114 func (v *Version) Compare(other *Version) int {
115
116 if v.String() == other.String() {
117 return 0
118 }
119
120 segmentsSelf := v.Segments64()
121 segmentsOther := other.Segments64()
122
123
124 if reflect.DeepEqual(segmentsSelf, segmentsOther) {
125 preSelf := v.Prerelease()
126 preOther := other.Prerelease()
127 if preSelf == "" && preOther == "" {
128 return 0
129 }
130 if preSelf == "" {
131 return 1
132 }
133 if preOther == "" {
134 return -1
135 }
136
137 return comparePrereleases(preSelf, preOther)
138 }
139
140
141 lenSelf := len(segmentsSelf)
142 lenOther := len(segmentsOther)
143 hS := lenSelf
144 if lenSelf < lenOther {
145 hS = lenOther
146 }
147
148
149
150 for i := 0; i < hS; i++ {
151 if i > lenSelf-1 {
152
153
154 if !allZero(segmentsOther[i:]) {
155
156 return -1
157 }
158 break
159 } else if i > lenOther-1 {
160
161
162 if !allZero(segmentsSelf[i:]) {
163
164 return 1
165 }
166 break
167 }
168 lhs := segmentsSelf[i]
169 rhs := segmentsOther[i]
170 if lhs == rhs {
171 continue
172 } else if lhs < rhs {
173 return -1
174 }
175
176 return 1
177 }
178
179
180 return 0
181 }
182
183 func allZero(segs []int64) bool {
184 for _, s := range segs {
185 if s != 0 {
186 return false
187 }
188 }
189 return true
190 }
191
192 func comparePart(preSelf string, preOther string) int {
193 if preSelf == preOther {
194 return 0
195 }
196
197 var selfInt int64
198 selfNumeric := true
199 selfInt, err := strconv.ParseInt(preSelf, 10, 64)
200 if err != nil {
201 selfNumeric = false
202 }
203
204 var otherInt int64
205 otherNumeric := true
206 otherInt, err = strconv.ParseInt(preOther, 10, 64)
207 if err != nil {
208 otherNumeric = false
209 }
210
211
212 if preSelf == "" {
213 if otherNumeric {
214 return -1
215 }
216 return 1
217 }
218
219 if preOther == "" {
220 if selfNumeric {
221 return 1
222 }
223 return -1
224 }
225
226 if selfNumeric && !otherNumeric {
227 return -1
228 } else if !selfNumeric && otherNumeric {
229 return 1
230 } else if !selfNumeric && !otherNumeric && preSelf > preOther {
231 return 1
232 } else if selfInt > otherInt {
233 return 1
234 }
235
236 return -1
237 }
238
239 func comparePrereleases(v string, other string) int {
240
241 if v == other {
242 return 0
243 }
244
245
246 selfPreReleaseMeta := strings.Split(v, ".")
247 otherPreReleaseMeta := strings.Split(other, ".")
248
249 selfPreReleaseLen := len(selfPreReleaseMeta)
250 otherPreReleaseLen := len(otherPreReleaseMeta)
251
252 biggestLen := otherPreReleaseLen
253 if selfPreReleaseLen > otherPreReleaseLen {
254 biggestLen = selfPreReleaseLen
255 }
256
257
258 for i := 0; i < biggestLen; i = i + 1 {
259 partSelfPre := ""
260 if i < selfPreReleaseLen {
261 partSelfPre = selfPreReleaseMeta[i]
262 }
263
264 partOtherPre := ""
265 if i < otherPreReleaseLen {
266 partOtherPre = otherPreReleaseMeta[i]
267 }
268
269 compare := comparePart(partSelfPre, partOtherPre)
270
271 if compare != 0 {
272 return compare
273 }
274 }
275
276 return 0
277 }
278
279
280
281 func (v *Version) Core() *Version {
282 segments := v.Segments64()
283 segmentsOnly := fmt.Sprintf("%d.%d.%d", segments[0], segments[1], segments[2])
284 return Must(NewVersion(segmentsOnly))
285 }
286
287
288 func (v *Version) Equal(o *Version) bool {
289 if v == nil || o == nil {
290 return v == o
291 }
292
293 return v.Compare(o) == 0
294 }
295
296
297 func (v *Version) GreaterThan(o *Version) bool {
298 return v.Compare(o) > 0
299 }
300
301
302 func (v *Version) GreaterThanOrEqual(o *Version) bool {
303 return v.Compare(o) >= 0
304 }
305
306
307 func (v *Version) LessThan(o *Version) bool {
308 return v.Compare(o) < 0
309 }
310
311
312 func (v *Version) LessThanOrEqual(o *Version) bool {
313 return v.Compare(o) <= 0
314 }
315
316
317
318
319
320
321 func (v *Version) Metadata() string {
322 return v.metadata
323 }
324
325
326
327
328
329
330
331 func (v *Version) Prerelease() string {
332 return v.pre
333 }
334
335
336
337
338
339
340 func (v *Version) Segments() []int {
341 segmentSlice := make([]int, len(v.segments))
342 for i, v := range v.segments {
343 segmentSlice[i] = int(v)
344 }
345 return segmentSlice
346 }
347
348
349
350
351
352
353 func (v *Version) Segments64() []int64 {
354 result := make([]int64, len(v.segments))
355 copy(result, v.segments)
356 return result
357 }
358
359
360
361
362
363
364
365
366
367 func (v *Version) String() string {
368 var buf bytes.Buffer
369 fmtParts := make([]string, len(v.segments))
370 for i, s := range v.segments {
371
372 str := strconv.FormatInt(s, 10)
373 fmtParts[i] = str
374 }
375 fmt.Fprintf(&buf, strings.Join(fmtParts, "."))
376 if v.pre != "" {
377 fmt.Fprintf(&buf, "-%s", v.pre)
378 }
379 if v.metadata != "" {
380 fmt.Fprintf(&buf, "+%s", v.metadata)
381 }
382
383 return buf.String()
384 }
385
386
387
388 func (v *Version) Original() string {
389 return v.original
390 }
391
392
393 func (v *Version) UnmarshalText(b []byte) error {
394 temp, err := NewVersion(string(b))
395 if err != nil {
396 return err
397 }
398
399 *v = *temp
400
401 return nil
402 }
403
404
405 func (v *Version) MarshalText() ([]byte, error) {
406 return []byte(v.String()), nil
407 }
408
View as plain text