]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/blang/semver/semver.go
vendor: github.com/hashicorp/terraform/...@v0.10.0
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / blang / semver / semver.go
1 package semver
2
3 import (
4 "errors"
5 "fmt"
6 "strconv"
7 "strings"
8 )
9
10 const (
11 numbers string = "0123456789"
12 alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
13 alphanum = alphas + numbers
14 )
15
16 // SpecVersion is the latest fully supported spec version of semver
17 var SpecVersion = Version{
18 Major: 2,
19 Minor: 0,
20 Patch: 0,
21 }
22
23 // Version represents a semver compatible version
24 type Version struct {
25 Major uint64
26 Minor uint64
27 Patch uint64
28 Pre []PRVersion
29 Build []string //No Precendence
30 }
31
32 // Version to string
33 func (v Version) String() string {
34 b := make([]byte, 0, 5)
35 b = strconv.AppendUint(b, v.Major, 10)
36 b = append(b, '.')
37 b = strconv.AppendUint(b, v.Minor, 10)
38 b = append(b, '.')
39 b = strconv.AppendUint(b, v.Patch, 10)
40
41 if len(v.Pre) > 0 {
42 b = append(b, '-')
43 b = append(b, v.Pre[0].String()...)
44
45 for _, pre := range v.Pre[1:] {
46 b = append(b, '.')
47 b = append(b, pre.String()...)
48 }
49 }
50
51 if len(v.Build) > 0 {
52 b = append(b, '+')
53 b = append(b, v.Build[0]...)
54
55 for _, build := range v.Build[1:] {
56 b = append(b, '.')
57 b = append(b, build...)
58 }
59 }
60
61 return string(b)
62 }
63
64 // Equals checks if v is equal to o.
65 func (v Version) Equals(o Version) bool {
66 return (v.Compare(o) == 0)
67 }
68
69 // EQ checks if v is equal to o.
70 func (v Version) EQ(o Version) bool {
71 return (v.Compare(o) == 0)
72 }
73
74 // NE checks if v is not equal to o.
75 func (v Version) NE(o Version) bool {
76 return (v.Compare(o) != 0)
77 }
78
79 // GT checks if v is greater than o.
80 func (v Version) GT(o Version) bool {
81 return (v.Compare(o) == 1)
82 }
83
84 // GTE checks if v is greater than or equal to o.
85 func (v Version) GTE(o Version) bool {
86 return (v.Compare(o) >= 0)
87 }
88
89 // GE checks if v is greater than or equal to o.
90 func (v Version) GE(o Version) bool {
91 return (v.Compare(o) >= 0)
92 }
93
94 // LT checks if v is less than o.
95 func (v Version) LT(o Version) bool {
96 return (v.Compare(o) == -1)
97 }
98
99 // LTE checks if v is less than or equal to o.
100 func (v Version) LTE(o Version) bool {
101 return (v.Compare(o) <= 0)
102 }
103
104 // LE checks if v is less than or equal to o.
105 func (v Version) LE(o Version) bool {
106 return (v.Compare(o) <= 0)
107 }
108
109 // Compare compares Versions v to o:
110 // -1 == v is less than o
111 // 0 == v is equal to o
112 // 1 == v is greater than o
113 func (v Version) Compare(o Version) int {
114 if v.Major != o.Major {
115 if v.Major > o.Major {
116 return 1
117 }
118 return -1
119 }
120 if v.Minor != o.Minor {
121 if v.Minor > o.Minor {
122 return 1
123 }
124 return -1
125 }
126 if v.Patch != o.Patch {
127 if v.Patch > o.Patch {
128 return 1
129 }
130 return -1
131 }
132
133 // Quick comparison if a version has no prerelease versions
134 if len(v.Pre) == 0 && len(o.Pre) == 0 {
135 return 0
136 } else if len(v.Pre) == 0 && len(o.Pre) > 0 {
137 return 1
138 } else if len(v.Pre) > 0 && len(o.Pre) == 0 {
139 return -1
140 }
141
142 i := 0
143 for ; i < len(v.Pre) && i < len(o.Pre); i++ {
144 if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
145 continue
146 } else if comp == 1 {
147 return 1
148 } else {
149 return -1
150 }
151 }
152
153 // If all pr versions are the equal but one has further prversion, this one greater
154 if i == len(v.Pre) && i == len(o.Pre) {
155 return 0
156 } else if i == len(v.Pre) && i < len(o.Pre) {
157 return -1
158 } else {
159 return 1
160 }
161
162 }
163
164 // Validate validates v and returns error in case
165 func (v Version) Validate() error {
166 // Major, Minor, Patch already validated using uint64
167
168 for _, pre := range v.Pre {
169 if !pre.IsNum { //Numeric prerelease versions already uint64
170 if len(pre.VersionStr) == 0 {
171 return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
172 }
173 if !containsOnly(pre.VersionStr, alphanum) {
174 return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
175 }
176 }
177 }
178
179 for _, build := range v.Build {
180 if len(build) == 0 {
181 return fmt.Errorf("Build meta data can not be empty %q", build)
182 }
183 if !containsOnly(build, alphanum) {
184 return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
185 }
186 }
187
188 return nil
189 }
190
191 // New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
192 func New(s string) (vp *Version, err error) {
193 v, err := Parse(s)
194 vp = &v
195 return
196 }
197
198 // Make is an alias for Parse, parses version string and returns a validated Version or error
199 func Make(s string) (Version, error) {
200 return Parse(s)
201 }
202
203 // ParseTolerant allows for certain version specifications that do not strictly adhere to semver
204 // specs to be parsed by this library. It does so by normalizing versions before passing them to
205 // Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
206 // with only major and minor components specified
207 func ParseTolerant(s string) (Version, error) {
208 s = strings.TrimSpace(s)
209 s = strings.TrimPrefix(s, "v")
210
211 // Split into major.minor.(patch+pr+meta)
212 parts := strings.SplitN(s, ".", 3)
213 if len(parts) < 3 {
214 if strings.ContainsAny(parts[len(parts)-1], "+-") {
215 return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
216 }
217 for len(parts) < 3 {
218 parts = append(parts, "0")
219 }
220 s = strings.Join(parts, ".")
221 }
222
223 return Parse(s)
224 }
225
226 // Parse parses version string and returns a validated Version or error
227 func Parse(s string) (Version, error) {
228 if len(s) == 0 {
229 return Version{}, errors.New("Version string empty")
230 }
231
232 // Split into major.minor.(patch+pr+meta)
233 parts := strings.SplitN(s, ".", 3)
234 if len(parts) != 3 {
235 return Version{}, errors.New("No Major.Minor.Patch elements found")
236 }
237
238 // Major
239 if !containsOnly(parts[0], numbers) {
240 return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
241 }
242 if hasLeadingZeroes(parts[0]) {
243 return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
244 }
245 major, err := strconv.ParseUint(parts[0], 10, 64)
246 if err != nil {
247 return Version{}, err
248 }
249
250 // Minor
251 if !containsOnly(parts[1], numbers) {
252 return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
253 }
254 if hasLeadingZeroes(parts[1]) {
255 return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
256 }
257 minor, err := strconv.ParseUint(parts[1], 10, 64)
258 if err != nil {
259 return Version{}, err
260 }
261
262 v := Version{}
263 v.Major = major
264 v.Minor = minor
265
266 var build, prerelease []string
267 patchStr := parts[2]
268
269 if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
270 build = strings.Split(patchStr[buildIndex+1:], ".")
271 patchStr = patchStr[:buildIndex]
272 }
273
274 if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
275 prerelease = strings.Split(patchStr[preIndex+1:], ".")
276 patchStr = patchStr[:preIndex]
277 }
278
279 if !containsOnly(patchStr, numbers) {
280 return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
281 }
282 if hasLeadingZeroes(patchStr) {
283 return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
284 }
285 patch, err := strconv.ParseUint(patchStr, 10, 64)
286 if err != nil {
287 return Version{}, err
288 }
289
290 v.Patch = patch
291
292 // Prerelease
293 for _, prstr := range prerelease {
294 parsedPR, err := NewPRVersion(prstr)
295 if err != nil {
296 return Version{}, err
297 }
298 v.Pre = append(v.Pre, parsedPR)
299 }
300
301 // Build meta data
302 for _, str := range build {
303 if len(str) == 0 {
304 return Version{}, errors.New("Build meta data is empty")
305 }
306 if !containsOnly(str, alphanum) {
307 return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
308 }
309 v.Build = append(v.Build, str)
310 }
311
312 return v, nil
313 }
314
315 // MustParse is like Parse but panics if the version cannot be parsed.
316 func MustParse(s string) Version {
317 v, err := Parse(s)
318 if err != nil {
319 panic(`semver: Parse(` + s + `): ` + err.Error())
320 }
321 return v
322 }
323
324 // PRVersion represents a PreRelease Version
325 type PRVersion struct {
326 VersionStr string
327 VersionNum uint64
328 IsNum bool
329 }
330
331 // NewPRVersion creates a new valid prerelease version
332 func NewPRVersion(s string) (PRVersion, error) {
333 if len(s) == 0 {
334 return PRVersion{}, errors.New("Prerelease is empty")
335 }
336 v := PRVersion{}
337 if containsOnly(s, numbers) {
338 if hasLeadingZeroes(s) {
339 return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
340 }
341 num, err := strconv.ParseUint(s, 10, 64)
342
343 // Might never be hit, but just in case
344 if err != nil {
345 return PRVersion{}, err
346 }
347 v.VersionNum = num
348 v.IsNum = true
349 } else if containsOnly(s, alphanum) {
350 v.VersionStr = s
351 v.IsNum = false
352 } else {
353 return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
354 }
355 return v, nil
356 }
357
358 // IsNumeric checks if prerelease-version is numeric
359 func (v PRVersion) IsNumeric() bool {
360 return v.IsNum
361 }
362
363 // Compare compares two PreRelease Versions v and o:
364 // -1 == v is less than o
365 // 0 == v is equal to o
366 // 1 == v is greater than o
367 func (v PRVersion) Compare(o PRVersion) int {
368 if v.IsNum && !o.IsNum {
369 return -1
370 } else if !v.IsNum && o.IsNum {
371 return 1
372 } else if v.IsNum && o.IsNum {
373 if v.VersionNum == o.VersionNum {
374 return 0
375 } else if v.VersionNum > o.VersionNum {
376 return 1
377 } else {
378 return -1
379 }
380 } else { // both are Alphas
381 if v.VersionStr == o.VersionStr {
382 return 0
383 } else if v.VersionStr > o.VersionStr {
384 return 1
385 } else {
386 return -1
387 }
388 }
389 }
390
391 // PreRelease version to string
392 func (v PRVersion) String() string {
393 if v.IsNum {
394 return strconv.FormatUint(v.VersionNum, 10)
395 }
396 return v.VersionStr
397 }
398
399 func containsOnly(s string, set string) bool {
400 return strings.IndexFunc(s, func(r rune) bool {
401 return !strings.ContainsRune(set, r)
402 }) == -1
403 }
404
405 func hasLeadingZeroes(s string) bool {
406 return len(s) > 1 && s[0] == '0'
407 }
408
409 // NewBuildVersion creates a new valid build version
410 func NewBuildVersion(s string) (string, error) {
411 if len(s) == 0 {
412 return "", errors.New("Buildversion is empty")
413 }
414 if !containsOnly(s, alphanum) {
415 return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
416 }
417 return s, nil
418 }