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