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