vers 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +29 -1
- data/CONTRIBUTING.md +2 -2
- data/README.md +27 -24
- data/lib/vers/bazel_version.rb +114 -0
- data/lib/vers/composer_version.rb +117 -0
- data/lib/vers/conan_version.rb +87 -0
- data/lib/vers/constraint.rb +7 -3
- data/lib/vers/distribution_version.rb +482 -0
- data/lib/vers/gem_version.rb +81 -0
- data/lib/vers/interval.rb +49 -33
- data/lib/vers/maven_version.rb +16 -0
- data/lib/vers/nuget_version.rb +14 -0
- data/lib/vers/parser.rb +893 -202
- data/lib/vers/pub_version.rb +59 -0
- data/lib/vers/pypi_version.rb +256 -0
- data/lib/vers/scheme.rb +52 -0
- data/lib/vers/semver_version.rb +294 -0
- data/lib/vers/special_version.rb +336 -0
- data/lib/vers/version.rb +68 -13
- data/lib/vers/version_comparison.rb +26 -0
- data/lib/vers/version_range.rb +201 -48
- data/lib/vers.rb +39 -9
- metadata +15 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e93f013566ede55047ab7d9ba9b328521e89f9eae2b9e5a21c589e6c3319ca45
|
|
4
|
+
data.tar.gz: 56e0c2ed53178935ed6548f0bc373a11343d2aa2d3de43bc51b888979c436578
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 30a8cb87c48bdb38480156560b03abcb16b9e3c1a4ff1a41726b824c7652b75782a3a6cfe2184290ed066281dcb8f478c0eb40150394e82b054314f39d4bdb86
|
|
7
|
+
data.tar.gz: f0ed8e70c9831e71fb17f3b058c2ab5b54d449352f0ec985f0c1c53962ef504e8f3d945278ad7c37af8847338078394bc9bb7e0d0317221486d1d501378bdf09
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.0.
|
|
1
|
+
4.0.6
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.0] - 2026-08-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Bazel module version comparison, validation, normalization, cleaning, stable classification, and prerelease classification.
|
|
15
|
+
- Scheme-aware comparison, validation, normalization, cleaning, stable classification, and prerelease classification across language registries, distribution packages, SemVer variants, and other version formats.
|
|
16
|
+
- Native range parsing for Composer, Pub, Conan, OpenSSL, and nginx, with expanded behavior for existing npm, Cargo, RubyGems, PyPI, Maven, and NuGet parsers.
|
|
17
|
+
- Data-driven comparison, containment, native conversion, validation, and canonical parsing tests from `vers-spec`, upstream package managers, and the Go implementation.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Version ranges retain their canonical scheme through containment and set operations, and reject algebra between incompatible schemes.
|
|
22
|
+
- VERS serialization canonicalizes scheme aliases and rejects a scheme that conflicts with the range.
|
|
23
|
+
- Canonical VERS parsing rejects uppercase types, whitespace, malformed pipes, invalid percent encoding, unencoded reserved characters, duplicate versions, and unsorted constraints when strict parsing is requested.
|
|
24
|
+
- Typed wildcard ranges reject invalid candidate versions.
|
|
25
|
+
- APK comparison and validation now follow apk-tools tokens instead of Gentoo version rules.
|
|
26
|
+
- The minimum supported Ruby version is now 3.3.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Unioning an unbounded interval with a bounded interval now remains unbounded.
|
|
31
|
+
- Range unions retain exclusions when the other operand does not contain the excluded version.
|
|
32
|
+
- VERS serialization no longer adds an empty constraint to unbounded ranges with exclusions.
|
|
33
|
+
- SemVer comparison handles surrounding whitespace consistently with validation.
|
|
34
|
+
- npm partial and prerelease ranges retain their membership after VERS serialization.
|
|
35
|
+
- Cargo and PyPI intersections follow their scheme-specific build and local version equality rules.
|
|
36
|
+
|
|
10
37
|
## [1.1.0] - 2026-02-24
|
|
11
38
|
|
|
12
39
|
### Added
|
|
@@ -126,7 +153,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
126
153
|
- **No runtime dependencies** - pure Ruby implementation
|
|
127
154
|
- **Minitest** for testing (development dependency only)
|
|
128
155
|
|
|
129
|
-
[Unreleased]: https://github.com/andrew/vers/compare/
|
|
156
|
+
[Unreleased]: https://github.com/andrew/vers/compare/v2.0.0...HEAD
|
|
157
|
+
[2.0.0]: https://github.com/andrew/vers/compare/v1.3.1...v2.0.0
|
|
130
158
|
[1.1.0]: https://github.com/andrew/vers/compare/v1.0.3...v1.1.0
|
|
131
159
|
[1.0.3]: https://github.com/andrew/vers/compare/v1.0.2...v1.0.3
|
|
132
160
|
[1.0.2]: https://github.com/andrew/vers/compare/v1.0.1...v1.0.2
|
data/CONTRIBUTING.md
CHANGED
|
@@ -41,7 +41,7 @@ Enhancement suggestions are welcome! Please:
|
|
|
41
41
|
|
|
42
42
|
### Prerequisites
|
|
43
43
|
|
|
44
|
-
- Ruby 3.
|
|
44
|
+
- Ruby 3.3 or higher
|
|
45
45
|
- Bundler gem
|
|
46
46
|
|
|
47
47
|
### Setup
|
|
@@ -191,4 +191,4 @@ Provide clear, actionable error messages:
|
|
|
191
191
|
- Suggest corrections when possible
|
|
192
192
|
- Use specific exception types
|
|
193
193
|
|
|
194
|
-
Thank you for contributing to make Vers better for everyone!
|
|
194
|
+
Thank you for contributing to make Vers better for everyone!
|
data/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# Vers - Version Range Parser for Ruby
|
|
2
2
|
|
|
3
|
-
A Ruby library for parsing
|
|
3
|
+
A Ruby library for parsing VERS ranges and applying package-manager version rules. It supports canonical VERS strings, native range syntax, scheme-aware comparison, validation, normalization, and release classification.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[](https://www.ruby-lang.org/)
|
|
5
|
+
[](https://www.ruby-lang.org/)
|
|
8
6
|
[](https://rubygems.org/gems/vers)
|
|
9
7
|
[](https://opensource.org/licenses/MIT)
|
|
10
8
|
|
|
@@ -12,14 +10,11 @@ This gem provides tools for working with version ranges across different package
|
|
|
12
10
|
|
|
13
11
|
## Features
|
|
14
12
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- **Semantic versioning features** - version increment, constraint checking, prerelease handling
|
|
21
|
-
- **Comprehensive error handling** with detailed parsing exceptions
|
|
22
|
-
- **100% test coverage** with 113 tests and 366 assertions
|
|
13
|
+
- Parse and serialize canonical VERS strings.
|
|
14
|
+
- Parse native ranges for npm, Cargo, RubyGems, PyPI, Composer, Pub, Maven, NuGet, Hex, Go, Debian, RPM, Conan, OpenSSL, and nginx.
|
|
15
|
+
- Compare, validate, normalize, clean, and classify versions with package-manager rules.
|
|
16
|
+
- Combine ranges with union, intersection, complement, and exclusions.
|
|
17
|
+
- Check containment with the range's version scheme.
|
|
23
18
|
|
|
24
19
|
## Installation
|
|
25
20
|
|
|
@@ -56,29 +51,37 @@ npm_range = Vers.parse_native("^1.2.3", "npm")
|
|
|
56
51
|
gem_range = Vers.parse_native("~> 1.0", "gem")
|
|
57
52
|
|
|
58
53
|
# Check version containment
|
|
59
|
-
Vers.satisfies?("1.5.0", ">=1.0.0,<2.0.0") # => true
|
|
54
|
+
Vers.satisfies?("1.5.0", ">=1.0.0,<2.0.0", "pypi") # => true
|
|
60
55
|
|
|
61
56
|
# Compare versions
|
|
62
57
|
Vers.compare("1.2.3", "1.2.4") # => -1
|
|
63
58
|
|
|
59
|
+
# Use Bazel's module version rules
|
|
60
|
+
Vers.compare_with_scheme("0.7.1", "0.7.1.bcr.1", "bazel") # => -1
|
|
61
|
+
Vers.valid?("35.1", "bazel") # => true
|
|
62
|
+
Vers.normalize("v01.02", "composer") # => "1.2.0"
|
|
63
|
+
Vers.clean(" v1.2 ", "npm") # => "1.2.0"
|
|
64
|
+
Vers.stable?("0.7.1.bcr.1", "bazel") # => true
|
|
65
|
+
Vers.prerelease?("36.0-rc2", "bazel") # => true
|
|
66
|
+
|
|
64
67
|
# Version operations
|
|
65
68
|
version = Vers::Version.new("1.2.3")
|
|
66
69
|
version.increment_major # => #<Vers::Version "2.0.0">
|
|
67
70
|
version.satisfies?("~> 1.2") # => true
|
|
68
71
|
```
|
|
69
72
|
|
|
70
|
-
##
|
|
73
|
+
## Version Schemes
|
|
74
|
+
|
|
75
|
+
Scheme-aware version operations support these groups:
|
|
76
|
+
|
|
77
|
+
- SemVer family: SemVer, npm, Cargo, Go modules, Hex, and nginx.
|
|
78
|
+
- Language registries: RubyGems, PyPI, Composer, Pub, Maven, and NuGet.
|
|
79
|
+
- Distribution packages: Debian, RPM, APK, Gentoo, and ALPM.
|
|
80
|
+
- Other formats: Bazel, Conan, OpenSSL, integer-dot, RFC 3339 datetime, and lexicographic versions.
|
|
71
81
|
|
|
72
|
-
|
|
73
|
-
- **RubyGems** (Ruby): Pessimistic operator (~> 1.2), standard operators (>=, <=, etc.), comma-separated constraints
|
|
74
|
-
- **PyPI** (Python): Comma-separated constraints (>=1.0,<2.0), exclusions (!=1.5.0), compatible release (~=1.4.2)
|
|
75
|
-
- **Maven** (Java): Bracket notation ([1.0,2.0], (1.0,2.0)), union ranges, open ranges
|
|
76
|
-
- **NuGet** (.NET): Bracket notation ([1.0,2.0], (1.0,2.0)), mixed brackets, open ranges
|
|
77
|
-
- **Packagist** (PHP Composer): Caret ranges (^1.2.3), tilde ranges (~1.2), stability flags (@dev, @alpha)
|
|
78
|
-
- **Debian** (apt): Standard comparison operators (>=1.0.0, <<2.0.0)
|
|
79
|
-
- **RPM** (yum/dnf): Standard comparison operators (>=1.0.0, <=2.0.0)
|
|
82
|
+
The aliases `rubygems`, `debian`, `golang`, `elixir`, and `alpine` map to their canonical schemes. Unknown schemes keep the generic version behavior. Bazel is an implementation-defined scheme in this library and remains separate from the VERS specification types.
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
Native range parsing includes npm and Cargo caret, tilde, wildcard, hyphen, AND, and OR forms; Composer stability and branch forms; Pub caret ranges; RubyGems pessimistic ranges; PyPI specifiers; Maven and NuGet bracket ranges; Conan compatible ranges; OpenSSL exact-version lists; and nginx plus ranges. Other schemes accept standard VERS comparison operators.
|
|
82
85
|
|
|
83
86
|
## Mathematical Model
|
|
84
87
|
|
|
@@ -134,7 +137,7 @@ vers_string = Vers.to_vers_string(npm_range, "npm")
|
|
|
134
137
|
puts vers_string # => "vers:npm/>=1.2.3|<2.0.0"
|
|
135
138
|
|
|
136
139
|
# Parse vers URI and use in your application
|
|
137
|
-
range = Vers.
|
|
140
|
+
range = Vers.parse_native("~>1.0", "gem")
|
|
138
141
|
puts range.contains?("1.5.0") # => true
|
|
139
142
|
```
|
|
140
143
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vers
|
|
4
|
+
module BazelVersion
|
|
5
|
+
VERSION_REGEX = /\A(?<release>[a-zA-Z0-9.]+)(?:-(?<prerelease>[a-zA-Z0-9.-]+))?(?:\+[a-zA-Z0-9.-]+)?\z/
|
|
6
|
+
MAX_NUMERIC_IDENTIFIER = (2**64) - 1
|
|
7
|
+
|
|
8
|
+
Identifier = Struct.new(:numeric, :number, :text, keyword_init: true)
|
|
9
|
+
ParsedVersion = Struct.new(:release, :prerelease, :empty, keyword_init: true)
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def compare(a, b)
|
|
14
|
+
return 0 if a == b
|
|
15
|
+
|
|
16
|
+
version_a = parse(a)
|
|
17
|
+
version_b = parse(b)
|
|
18
|
+
|
|
19
|
+
return 1 if version_a.empty && !version_b.empty
|
|
20
|
+
return -1 if !version_a.empty && version_b.empty
|
|
21
|
+
|
|
22
|
+
release_comparison = compare_identifiers(version_a.release, version_b.release)
|
|
23
|
+
return release_comparison unless release_comparison == 0
|
|
24
|
+
|
|
25
|
+
return 1 if version_a.prerelease.empty? && !version_b.prerelease.empty?
|
|
26
|
+
return -1 if !version_a.prerelease.empty? && version_b.prerelease.empty?
|
|
27
|
+
|
|
28
|
+
compare_identifiers(version_a.prerelease, version_b.prerelease)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def valid?(version)
|
|
32
|
+
return false if version == ""
|
|
33
|
+
|
|
34
|
+
parse(version)
|
|
35
|
+
true
|
|
36
|
+
rescue ArgumentError
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def stable?(version)
|
|
41
|
+
parse(version).prerelease.empty?
|
|
42
|
+
rescue ArgumentError
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def prerelease?(version)
|
|
47
|
+
!parse(version).prerelease.empty?
|
|
48
|
+
rescue ArgumentError
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def normalize(version)
|
|
53
|
+
raise ArgumentError, "Invalid Bazel version: #{version.inspect}" unless valid?(version)
|
|
54
|
+
|
|
55
|
+
version.split("+", 2).first
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def parse(version)
|
|
59
|
+
raise ArgumentError, "Invalid Bazel version: #{version.inspect}" unless version.is_a?(String)
|
|
60
|
+
|
|
61
|
+
if version.empty?
|
|
62
|
+
return ParsedVersion.new(release: [], prerelease: [], empty: true)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
match = VERSION_REGEX.match(version)
|
|
66
|
+
raise ArgumentError, "Invalid Bazel version: #{version.inspect}" unless match
|
|
67
|
+
|
|
68
|
+
ParsedVersion.new(
|
|
69
|
+
release: parse_identifiers(match[:release], version),
|
|
70
|
+
prerelease: match[:prerelease] ? parse_identifiers(match[:prerelease], version) : [],
|
|
71
|
+
empty: false
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parse_identifiers(value, version)
|
|
76
|
+
value.split(".", -1).map do |identifier|
|
|
77
|
+
raise ArgumentError, "Invalid Bazel version: #{version}" if identifier.empty?
|
|
78
|
+
|
|
79
|
+
if identifier.match?(/\A[0-9]+\z/)
|
|
80
|
+
number = identifier.to_i
|
|
81
|
+
if number > MAX_NUMERIC_IDENTIFIER
|
|
82
|
+
raise ArgumentError, "Numeric Bazel version identifier is too large: #{identifier}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
Identifier.new(numeric: true, number: number, text: identifier)
|
|
86
|
+
else
|
|
87
|
+
Identifier.new(numeric: false, number: 0, text: identifier)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def compare_identifiers(identifiers_a, identifiers_b)
|
|
93
|
+
[identifiers_a.length, identifiers_b.length].min.times do |index|
|
|
94
|
+
comparison = compare_identifier(identifiers_a[index], identifiers_b[index])
|
|
95
|
+
return comparison unless comparison == 0
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
identifiers_a.length <=> identifiers_b.length
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def compare_identifier(identifier_a, identifier_b)
|
|
102
|
+
if identifier_a.numeric != identifier_b.numeric
|
|
103
|
+
return identifier_a.numeric ? -1 : 1
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if identifier_a.numeric
|
|
107
|
+
number_comparison = identifier_a.number <=> identifier_b.number
|
|
108
|
+
return number_comparison unless number_comparison == 0
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
identifier_a.text <=> identifier_b.text
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "version_comparison"
|
|
4
|
+
|
|
5
|
+
module Vers
|
|
6
|
+
module ComposerVersion
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
PATTERN = /\Av?([0-9]+(?:\.[0-9]+){0,3})(?:[-._]?([a-z]+)(?:[.-]?([0-9]+(?:[.-][0-9]+)*))?)?(?:\+\S+)?\z/i
|
|
10
|
+
NUMERIC_BRANCH_PATTERN = /\Av?\d+(?:\.\d+)*\.x-dev\z/i
|
|
11
|
+
Parsed = Data.define(:core, :stability, :number)
|
|
12
|
+
|
|
13
|
+
STABILITIES = {
|
|
14
|
+
"dev" => 0,
|
|
15
|
+
"a" => 1,
|
|
16
|
+
"alpha" => 1,
|
|
17
|
+
"b" => 2,
|
|
18
|
+
"beta" => 2,
|
|
19
|
+
"rc" => 3,
|
|
20
|
+
"stable" => 4,
|
|
21
|
+
"p" => 5,
|
|
22
|
+
"pl" => 5,
|
|
23
|
+
"patch" => 5
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
def compare(left, right)
|
|
27
|
+
parsed_left = parse(left)
|
|
28
|
+
parsed_right = parse(right)
|
|
29
|
+
left_branch = branch?(left)
|
|
30
|
+
right_branch = branch?(right)
|
|
31
|
+
|
|
32
|
+
return -1 if left_branch && !right_branch
|
|
33
|
+
return 1 if !left_branch && right_branch
|
|
34
|
+
return left.downcase <=> right.downcase unless parsed_left && parsed_right
|
|
35
|
+
|
|
36
|
+
parsed_left.core.zip(parsed_right.core).each do |left_part, right_part|
|
|
37
|
+
comparison = VersionComparison.compare_numbers(left_part, right_part)
|
|
38
|
+
return comparison unless comparison.zero?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
comparison = parsed_left.stability <=> parsed_right.stability
|
|
42
|
+
return comparison unless comparison.zero?
|
|
43
|
+
|
|
44
|
+
compare_numbers(parsed_left.number, parsed_right.number)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def parse(value)
|
|
48
|
+
match = PATTERN.match(value.to_s.strip)
|
|
49
|
+
return nil unless match
|
|
50
|
+
|
|
51
|
+
core = match[1].split(".")
|
|
52
|
+
core << "0" while core.length < 4
|
|
53
|
+
qualifier = match[2]&.downcase
|
|
54
|
+
stability = qualifier ? STABILITIES[qualifier] : 4
|
|
55
|
+
return nil unless stability
|
|
56
|
+
|
|
57
|
+
Parsed.new(core.freeze, stability, match[3].to_s)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def branch?(value)
|
|
61
|
+
string = value.to_s.strip
|
|
62
|
+
(string.downcase.start_with?("dev-") && string.length > 4) || string.match?(NUMERIC_BRANCH_PATTERN)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def valid?(value)
|
|
66
|
+
string = value.to_s.strip
|
|
67
|
+
!string.empty? && !string.match?(/[[:space:]]/) && (!parse(string).nil? || branch?(string))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def normalize(value, implicit_stability: nil)
|
|
71
|
+
string = value.to_s.strip
|
|
72
|
+
return string if branch?(string)
|
|
73
|
+
|
|
74
|
+
match = PATTERN.match(string.sub(/\Av(?=\d)/i, ""))
|
|
75
|
+
raise ArgumentError, "Invalid Composer version: #{value}" unless match
|
|
76
|
+
|
|
77
|
+
core = match[1].split(".").map { |part| VersionComparison.normalize_number(part) }
|
|
78
|
+
core << "0" while core.length < 3
|
|
79
|
+
qualifier = match[2]&.downcase || implicit_stability
|
|
80
|
+
qualifier = {"a" => "alpha", "b" => "beta", "rc" => "RC", "p" => "patch", "pl" => "patch"}.fetch(qualifier, qualifier)
|
|
81
|
+
return core.join(".") if qualifier.nil? || qualifier == "stable"
|
|
82
|
+
|
|
83
|
+
"#{core.join(".")}-#{qualifier}#{match[3]}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def release_parts(value)
|
|
87
|
+
match = PATTERN.match(value.to_s.strip.sub(/\Av(?=\d)/i, ""))
|
|
88
|
+
return nil unless match
|
|
89
|
+
|
|
90
|
+
match[1].split(".")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def explicit_stability?(value)
|
|
94
|
+
match = PATTERN.match(value.to_s.strip.sub(/\Av(?=\d)/i, ""))
|
|
95
|
+
match && !match[2].nil?
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def prerelease?(value)
|
|
99
|
+
return true if branch?(value)
|
|
100
|
+
|
|
101
|
+
parsed = parse(value)
|
|
102
|
+
parsed && parsed.stability < STABILITIES.fetch("stable")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def compare_numbers(left, right)
|
|
106
|
+
left_parts = left.split(/[.-]/)
|
|
107
|
+
right_parts = right.split(/[.-]/)
|
|
108
|
+
|
|
109
|
+
[left_parts.length, right_parts.length].max.times do |index|
|
|
110
|
+
comparison = VersionComparison.compare_numbers(left_parts[index], right_parts[index])
|
|
111
|
+
return comparison unless comparison.zero?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
0
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "version_comparison"
|
|
4
|
+
|
|
5
|
+
module Vers
|
|
6
|
+
module ConanVersion
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
def compare(left, right)
|
|
10
|
+
left_main, left_pre, left_build = split(left)
|
|
11
|
+
right_main, right_pre, right_build = split(right)
|
|
12
|
+
|
|
13
|
+
comparison = compare_main(left_main, right_main)
|
|
14
|
+
return comparison unless comparison.zero?
|
|
15
|
+
|
|
16
|
+
comparison = compare_optional(left_pre, right_pre, prerelease: true)
|
|
17
|
+
return comparison unless comparison.zero?
|
|
18
|
+
|
|
19
|
+
compare_optional(left_build, right_build, prerelease: false)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def split(value)
|
|
23
|
+
main = value.to_s
|
|
24
|
+
build = nil
|
|
25
|
+
prerelease = nil
|
|
26
|
+
|
|
27
|
+
separator = main.rindex("+")
|
|
28
|
+
if separator
|
|
29
|
+
build = main[(separator + 1)..]
|
|
30
|
+
main = main[0...separator]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
separator = main.rindex("-")
|
|
34
|
+
if separator
|
|
35
|
+
prerelease = main[(separator + 1)..]
|
|
36
|
+
main = main[0...separator]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
[main, prerelease, build]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def valid?(value)
|
|
43
|
+
string = value.to_s.strip
|
|
44
|
+
!string.empty? && !string.match?(/[[:space:]]/)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def normalize(value)
|
|
48
|
+
value.to_s.strip
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def prerelease?(value)
|
|
52
|
+
!split(value.to_s.strip)[1].nil?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def compare_main(left, right)
|
|
56
|
+
left_parts = significant_parts(left)
|
|
57
|
+
right_parts = significant_parts(right)
|
|
58
|
+
|
|
59
|
+
left_parts.zip(right_parts).each do |left_part, right_part|
|
|
60
|
+
break unless left_part && right_part
|
|
61
|
+
|
|
62
|
+
comparison = if VersionComparison.numeric?(left_part) && VersionComparison.numeric?(right_part)
|
|
63
|
+
VersionComparison.compare_numbers(left_part, right_part)
|
|
64
|
+
else
|
|
65
|
+
left_part <=> right_part
|
|
66
|
+
end
|
|
67
|
+
return comparison unless comparison.zero?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
left_parts.length <=> right_parts.length
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def significant_parts(value)
|
|
74
|
+
parts = value.split(".", -1)
|
|
75
|
+
parts.pop while parts.last && VersionComparison.numeric?(parts.last) && VersionComparison.compare_numbers(parts.last, "0").zero?
|
|
76
|
+
parts
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def compare_optional(left, right, prerelease:)
|
|
80
|
+
return 0 if left.nil? && right.nil?
|
|
81
|
+
return prerelease ? 1 : -1 if left.nil?
|
|
82
|
+
return prerelease ? -1 : 1 if right.nil?
|
|
83
|
+
|
|
84
|
+
compare(left, right)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/vers/constraint.rb
CHANGED
|
@@ -131,8 +131,12 @@ module Vers
|
|
|
131
131
|
# @param version_string [String] The version to check
|
|
132
132
|
# @return [Boolean] true if the version satisfies the constraint
|
|
133
133
|
#
|
|
134
|
-
def satisfies?(version_string)
|
|
135
|
-
comparison =
|
|
134
|
+
def satisfies?(version_string, scheme: nil)
|
|
135
|
+
comparison = if scheme
|
|
136
|
+
Version.compare_with_scheme(version_string, version, scheme)
|
|
137
|
+
else
|
|
138
|
+
Version.compare(version_string, version)
|
|
139
|
+
end
|
|
136
140
|
|
|
137
141
|
case operator
|
|
138
142
|
when "="
|
|
@@ -167,4 +171,4 @@ module Vers
|
|
|
167
171
|
[operator, version].hash
|
|
168
172
|
end
|
|
169
173
|
end
|
|
170
|
-
end
|
|
174
|
+
end
|