podman

Форк
0
639 строк · 15.7 Кб
1
package semver
2

3
import (
4
	"bytes"
5
	"database/sql/driver"
6
	"encoding/json"
7
	"errors"
8
	"fmt"
9
	"regexp"
10
	"strconv"
11
	"strings"
12
)
13

14
// The compiled version of the regex created at init() is cached here so it
15
// only needs to be created once.
16
var versionRegex *regexp.Regexp
17

18
var (
19
	// ErrInvalidSemVer is returned a version is found to be invalid when
20
	// being parsed.
21
	ErrInvalidSemVer = errors.New("Invalid Semantic Version")
22

23
	// ErrEmptyString is returned when an empty string is passed in for parsing.
24
	ErrEmptyString = errors.New("Version string empty")
25

26
	// ErrInvalidCharacters is returned when invalid characters are found as
27
	// part of a version
28
	ErrInvalidCharacters = errors.New("Invalid characters in version")
29

30
	// ErrSegmentStartsZero is returned when a version segment starts with 0.
31
	// This is invalid in SemVer.
32
	ErrSegmentStartsZero = errors.New("Version segment starts with 0")
33

34
	// ErrInvalidMetadata is returned when the metadata is an invalid format
35
	ErrInvalidMetadata = errors.New("Invalid Metadata string")
36

37
	// ErrInvalidPrerelease is returned when the pre-release is an invalid format
38
	ErrInvalidPrerelease = errors.New("Invalid Prerelease string")
39
)
40

41
// semVerRegex is the regular expression used to parse a semantic version.
42
const semVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
43
	`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
44
	`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
45

46
// Version represents a single semantic version.
47
type Version struct {
48
	major, minor, patch uint64
49
	pre                 string
50
	metadata            string
51
	original            string
52
}
53

54
func init() {
55
	versionRegex = regexp.MustCompile("^" + semVerRegex + "$")
56
}
57

58
const (
59
	num     string = "0123456789"
60
	allowed string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + num
61
)
62

63
// StrictNewVersion parses a given version and returns an instance of Version or
64
// an error if unable to parse the version. Only parses valid semantic versions.
65
// Performs checking that can find errors within the version.
66
// If you want to coerce a version such as 1 or 1.2 and parse it as the 1.x
67
// releases of semver did, use the NewVersion() function.
68
func StrictNewVersion(v string) (*Version, error) {
69
	// Parsing here does not use RegEx in order to increase performance and reduce
70
	// allocations.
71

72
	if len(v) == 0 {
73
		return nil, ErrEmptyString
74
	}
75

76
	// Split the parts into [0]major, [1]minor, and [2]patch,prerelease,build
77
	parts := strings.SplitN(v, ".", 3)
78
	if len(parts) != 3 {
79
		return nil, ErrInvalidSemVer
80
	}
81

82
	sv := &Version{
83
		original: v,
84
	}
85

86
	// check for prerelease or build metadata
87
	var extra []string
88
	if strings.ContainsAny(parts[2], "-+") {
89
		// Start with the build metadata first as it needs to be on the right
90
		extra = strings.SplitN(parts[2], "+", 2)
91
		if len(extra) > 1 {
92
			// build metadata found
93
			sv.metadata = extra[1]
94
			parts[2] = extra[0]
95
		}
96

97
		extra = strings.SplitN(parts[2], "-", 2)
98
		if len(extra) > 1 {
99
			// prerelease found
100
			sv.pre = extra[1]
101
			parts[2] = extra[0]
102
		}
103
	}
104

105
	// Validate the number segments are valid. This includes only having positive
106
	// numbers and no leading 0's.
107
	for _, p := range parts {
108
		if !containsOnly(p, num) {
109
			return nil, ErrInvalidCharacters
110
		}
111

112
		if len(p) > 1 && p[0] == '0' {
113
			return nil, ErrSegmentStartsZero
114
		}
115
	}
116

117
	// Extract the major, minor, and patch elements onto the returned Version
118
	var err error
119
	sv.major, err = strconv.ParseUint(parts[0], 10, 64)
120
	if err != nil {
121
		return nil, err
122
	}
123

124
	sv.minor, err = strconv.ParseUint(parts[1], 10, 64)
125
	if err != nil {
126
		return nil, err
127
	}
128

129
	sv.patch, err = strconv.ParseUint(parts[2], 10, 64)
130
	if err != nil {
131
		return nil, err
132
	}
133

134
	// No prerelease or build metadata found so returning now as a fastpath.
135
	if sv.pre == "" && sv.metadata == "" {
136
		return sv, nil
137
	}
138

139
	if sv.pre != "" {
140
		if err = validatePrerelease(sv.pre); err != nil {
141
			return nil, err
142
		}
143
	}
144

145
	if sv.metadata != "" {
146
		if err = validateMetadata(sv.metadata); err != nil {
147
			return nil, err
148
		}
149
	}
150

151
	return sv, nil
152
}
153

154
// NewVersion parses a given version and returns an instance of Version or
155
// an error if unable to parse the version. If the version is SemVer-ish it
156
// attempts to convert it to SemVer. If you want  to validate it was a strict
157
// semantic version at parse time see StrictNewVersion().
158
func NewVersion(v string) (*Version, error) {
159
	m := versionRegex.FindStringSubmatch(v)
160
	if m == nil {
161
		return nil, ErrInvalidSemVer
162
	}
163

164
	sv := &Version{
165
		metadata: m[8],
166
		pre:      m[5],
167
		original: v,
168
	}
169

170
	var err error
171
	sv.major, err = strconv.ParseUint(m[1], 10, 64)
172
	if err != nil {
173
		return nil, fmt.Errorf("Error parsing version segment: %s", err)
174
	}
175

176
	if m[2] != "" {
177
		sv.minor, err = strconv.ParseUint(strings.TrimPrefix(m[2], "."), 10, 64)
178
		if err != nil {
179
			return nil, fmt.Errorf("Error parsing version segment: %s", err)
180
		}
181
	} else {
182
		sv.minor = 0
183
	}
184

185
	if m[3] != "" {
186
		sv.patch, err = strconv.ParseUint(strings.TrimPrefix(m[3], "."), 10, 64)
187
		if err != nil {
188
			return nil, fmt.Errorf("Error parsing version segment: %s", err)
189
		}
190
	} else {
191
		sv.patch = 0
192
	}
193

194
	// Perform some basic due diligence on the extra parts to ensure they are
195
	// valid.
196

197
	if sv.pre != "" {
198
		if err = validatePrerelease(sv.pre); err != nil {
199
			return nil, err
200
		}
201
	}
202

203
	if sv.metadata != "" {
204
		if err = validateMetadata(sv.metadata); err != nil {
205
			return nil, err
206
		}
207
	}
208

209
	return sv, nil
210
}
211

212
// New creates a new instance of Version with each of the parts passed in as
213
// arguments instead of parsing a version string.
214
func New(major, minor, patch uint64, pre, metadata string) *Version {
215
	v := Version{
216
		major:    major,
217
		minor:    minor,
218
		patch:    patch,
219
		pre:      pre,
220
		metadata: metadata,
221
		original: "",
222
	}
223

224
	v.original = v.String()
225

226
	return &v
227
}
228

229
// MustParse parses a given version and panics on error.
230
func MustParse(v string) *Version {
231
	sv, err := NewVersion(v)
232
	if err != nil {
233
		panic(err)
234
	}
235
	return sv
236
}
237

238
// String converts a Version object to a string.
239
// Note, if the original version contained a leading v this version will not.
240
// See the Original() method to retrieve the original value. Semantic Versions
241
// don't contain a leading v per the spec. Instead it's optional on
242
// implementation.
243
func (v Version) String() string {
244
	var buf bytes.Buffer
245

246
	fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch)
247
	if v.pre != "" {
248
		fmt.Fprintf(&buf, "-%s", v.pre)
249
	}
250
	if v.metadata != "" {
251
		fmt.Fprintf(&buf, "+%s", v.metadata)
252
	}
253

254
	return buf.String()
255
}
256

257
// Original returns the original value passed in to be parsed.
258
func (v *Version) Original() string {
259
	return v.original
260
}
261

262
// Major returns the major version.
263
func (v Version) Major() uint64 {
264
	return v.major
265
}
266

267
// Minor returns the minor version.
268
func (v Version) Minor() uint64 {
269
	return v.minor
270
}
271

272
// Patch returns the patch version.
273
func (v Version) Patch() uint64 {
274
	return v.patch
275
}
276

277
// Prerelease returns the pre-release version.
278
func (v Version) Prerelease() string {
279
	return v.pre
280
}
281

282
// Metadata returns the metadata on the version.
283
func (v Version) Metadata() string {
284
	return v.metadata
285
}
286

287
// originalVPrefix returns the original 'v' prefix if any.
288
func (v Version) originalVPrefix() string {
289
	// Note, only lowercase v is supported as a prefix by the parser.
290
	if v.original != "" && v.original[:1] == "v" {
291
		return v.original[:1]
292
	}
293
	return ""
294
}
295

296
// IncPatch produces the next patch version.
297
// If the current version does not have prerelease/metadata information,
298
// it unsets metadata and prerelease values, increments patch number.
299
// If the current version has any of prerelease or metadata information,
300
// it unsets both values and keeps current patch value
301
func (v Version) IncPatch() Version {
302
	vNext := v
303
	// according to http://semver.org/#spec-item-9
304
	// Pre-release versions have a lower precedence than the associated normal version.
305
	// according to http://semver.org/#spec-item-10
306
	// Build metadata SHOULD be ignored when determining version precedence.
307
	if v.pre != "" {
308
		vNext.metadata = ""
309
		vNext.pre = ""
310
	} else {
311
		vNext.metadata = ""
312
		vNext.pre = ""
313
		vNext.patch = v.patch + 1
314
	}
315
	vNext.original = v.originalVPrefix() + "" + vNext.String()
316
	return vNext
317
}
318

319
// IncMinor produces the next minor version.
320
// Sets patch to 0.
321
// Increments minor number.
322
// Unsets metadata.
323
// Unsets prerelease status.
324
func (v Version) IncMinor() Version {
325
	vNext := v
326
	vNext.metadata = ""
327
	vNext.pre = ""
328
	vNext.patch = 0
329
	vNext.minor = v.minor + 1
330
	vNext.original = v.originalVPrefix() + "" + vNext.String()
331
	return vNext
332
}
333

334
// IncMajor produces the next major version.
335
// Sets patch to 0.
336
// Sets minor to 0.
337
// Increments major number.
338
// Unsets metadata.
339
// Unsets prerelease status.
340
func (v Version) IncMajor() Version {
341
	vNext := v
342
	vNext.metadata = ""
343
	vNext.pre = ""
344
	vNext.patch = 0
345
	vNext.minor = 0
346
	vNext.major = v.major + 1
347
	vNext.original = v.originalVPrefix() + "" + vNext.String()
348
	return vNext
349
}
350

351
// SetPrerelease defines the prerelease value.
352
// Value must not include the required 'hyphen' prefix.
353
func (v Version) SetPrerelease(prerelease string) (Version, error) {
354
	vNext := v
355
	if len(prerelease) > 0 {
356
		if err := validatePrerelease(prerelease); err != nil {
357
			return vNext, err
358
		}
359
	}
360
	vNext.pre = prerelease
361
	vNext.original = v.originalVPrefix() + "" + vNext.String()
362
	return vNext, nil
363
}
364

365
// SetMetadata defines metadata value.
366
// Value must not include the required 'plus' prefix.
367
func (v Version) SetMetadata(metadata string) (Version, error) {
368
	vNext := v
369
	if len(metadata) > 0 {
370
		if err := validateMetadata(metadata); err != nil {
371
			return vNext, err
372
		}
373
	}
374
	vNext.metadata = metadata
375
	vNext.original = v.originalVPrefix() + "" + vNext.String()
376
	return vNext, nil
377
}
378

379
// LessThan tests if one version is less than another one.
380
func (v *Version) LessThan(o *Version) bool {
381
	return v.Compare(o) < 0
382
}
383

384
// GreaterThan tests if one version is greater than another one.
385
func (v *Version) GreaterThan(o *Version) bool {
386
	return v.Compare(o) > 0
387
}
388

389
// Equal tests if two versions are equal to each other.
390
// Note, versions can be equal with different metadata since metadata
391
// is not considered part of the comparable version.
392
func (v *Version) Equal(o *Version) bool {
393
	return v.Compare(o) == 0
394
}
395

396
// Compare compares this version to another one. It returns -1, 0, or 1 if
397
// the version smaller, equal, or larger than the other version.
398
//
399
// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is
400
// lower than the version without a prerelease. Compare always takes into account
401
// prereleases. If you want to work with ranges using typical range syntaxes that
402
// skip prereleases if the range is not looking for them use constraints.
403
func (v *Version) Compare(o *Version) int {
404
	// Compare the major, minor, and patch version for differences. If a
405
	// difference is found return the comparison.
406
	if d := compareSegment(v.Major(), o.Major()); d != 0 {
407
		return d
408
	}
409
	if d := compareSegment(v.Minor(), o.Minor()); d != 0 {
410
		return d
411
	}
412
	if d := compareSegment(v.Patch(), o.Patch()); d != 0 {
413
		return d
414
	}
415

416
	// At this point the major, minor, and patch versions are the same.
417
	ps := v.pre
418
	po := o.Prerelease()
419

420
	if ps == "" && po == "" {
421
		return 0
422
	}
423
	if ps == "" {
424
		return 1
425
	}
426
	if po == "" {
427
		return -1
428
	}
429

430
	return comparePrerelease(ps, po)
431
}
432

433
// UnmarshalJSON implements JSON.Unmarshaler interface.
434
func (v *Version) UnmarshalJSON(b []byte) error {
435
	var s string
436
	if err := json.Unmarshal(b, &s); err != nil {
437
		return err
438
	}
439
	temp, err := NewVersion(s)
440
	if err != nil {
441
		return err
442
	}
443
	v.major = temp.major
444
	v.minor = temp.minor
445
	v.patch = temp.patch
446
	v.pre = temp.pre
447
	v.metadata = temp.metadata
448
	v.original = temp.original
449
	return nil
450
}
451

452
// MarshalJSON implements JSON.Marshaler interface.
453
func (v Version) MarshalJSON() ([]byte, error) {
454
	return json.Marshal(v.String())
455
}
456

457
// UnmarshalText implements the encoding.TextUnmarshaler interface.
458
func (v *Version) UnmarshalText(text []byte) error {
459
	temp, err := NewVersion(string(text))
460
	if err != nil {
461
		return err
462
	}
463

464
	*v = *temp
465

466
	return nil
467
}
468

469
// MarshalText implements the encoding.TextMarshaler interface.
470
func (v Version) MarshalText() ([]byte, error) {
471
	return []byte(v.String()), nil
472
}
473

474
// Scan implements the SQL.Scanner interface.
475
func (v *Version) Scan(value interface{}) error {
476
	var s string
477
	s, _ = value.(string)
478
	temp, err := NewVersion(s)
479
	if err != nil {
480
		return err
481
	}
482
	v.major = temp.major
483
	v.minor = temp.minor
484
	v.patch = temp.patch
485
	v.pre = temp.pre
486
	v.metadata = temp.metadata
487
	v.original = temp.original
488
	return nil
489
}
490

491
// Value implements the Driver.Valuer interface.
492
func (v Version) Value() (driver.Value, error) {
493
	return v.String(), nil
494
}
495

496
func compareSegment(v, o uint64) int {
497
	if v < o {
498
		return -1
499
	}
500
	if v > o {
501
		return 1
502
	}
503

504
	return 0
505
}
506

507
func comparePrerelease(v, o string) int {
508
	// split the prelease versions by their part. The separator, per the spec,
509
	// is a .
510
	sparts := strings.Split(v, ".")
511
	oparts := strings.Split(o, ".")
512

513
	// Find the longer length of the parts to know how many loop iterations to
514
	// go through.
515
	slen := len(sparts)
516
	olen := len(oparts)
517

518
	l := slen
519
	if olen > slen {
520
		l = olen
521
	}
522

523
	// Iterate over each part of the prereleases to compare the differences.
524
	for i := 0; i < l; i++ {
525
		// Since the lentgh of the parts can be different we need to create
526
		// a placeholder. This is to avoid out of bounds issues.
527
		stemp := ""
528
		if i < slen {
529
			stemp = sparts[i]
530
		}
531

532
		otemp := ""
533
		if i < olen {
534
			otemp = oparts[i]
535
		}
536

537
		d := comparePrePart(stemp, otemp)
538
		if d != 0 {
539
			return d
540
		}
541
	}
542

543
	// Reaching here means two versions are of equal value but have different
544
	// metadata (the part following a +). They are not identical in string form
545
	// but the version comparison finds them to be equal.
546
	return 0
547
}
548

549
func comparePrePart(s, o string) int {
550
	// Fastpath if they are equal
551
	if s == o {
552
		return 0
553
	}
554

555
	// When s or o are empty we can use the other in an attempt to determine
556
	// the response.
557
	if s == "" {
558
		if o != "" {
559
			return -1
560
		}
561
		return 1
562
	}
563

564
	if o == "" {
565
		if s != "" {
566
			return 1
567
		}
568
		return -1
569
	}
570

571
	// When comparing strings "99" is greater than "103". To handle
572
	// cases like this we need to detect numbers and compare them. According
573
	// to the semver spec, numbers are always positive. If there is a - at the
574
	// start like -99 this is to be evaluated as an alphanum. numbers always
575
	// have precedence over alphanum. Parsing as Uints because negative numbers
576
	// are ignored.
577

578
	oi, n1 := strconv.ParseUint(o, 10, 64)
579
	si, n2 := strconv.ParseUint(s, 10, 64)
580

581
	// The case where both are strings compare the strings
582
	if n1 != nil && n2 != nil {
583
		if s > o {
584
			return 1
585
		}
586
		return -1
587
	} else if n1 != nil {
588
		// o is a string and s is a number
589
		return -1
590
	} else if n2 != nil {
591
		// s is a string and o is a number
592
		return 1
593
	}
594
	// Both are numbers
595
	if si > oi {
596
		return 1
597
	}
598
	return -1
599
}
600

601
// Like strings.ContainsAny but does an only instead of any.
602
func containsOnly(s string, comp string) bool {
603
	return strings.IndexFunc(s, func(r rune) bool {
604
		return !strings.ContainsRune(comp, r)
605
	}) == -1
606
}
607

608
// From the spec, "Identifiers MUST comprise only
609
// ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty.
610
// Numeric identifiers MUST NOT include leading zeroes.". These segments can
611
// be dot separated.
612
func validatePrerelease(p string) error {
613
	eparts := strings.Split(p, ".")
614
	for _, p := range eparts {
615
		if containsOnly(p, num) {
616
			if len(p) > 1 && p[0] == '0' {
617
				return ErrSegmentStartsZero
618
			}
619
		} else if !containsOnly(p, allowed) {
620
			return ErrInvalidPrerelease
621
		}
622
	}
623

624
	return nil
625
}
626

627
// From the spec, "Build metadata MAY be denoted by
628
// appending a plus sign and a series of dot separated identifiers immediately
629
// following the patch or pre-release version. Identifiers MUST comprise only
630
// ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty."
631
func validateMetadata(m string) error {
632
	eparts := strings.Split(m, ".")
633
	for _, p := range eparts {
634
		if !containsOnly(p, allowed) {
635
			return ErrInvalidMetadata
636
		}
637
	}
638
	return nil
639
}
640

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.