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
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vers
|
|
4
|
+
module PubVersion
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
PATTERN = /\A[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?\z/
|
|
8
|
+
|
|
9
|
+
def compare(left, right)
|
|
10
|
+
parsed_left = parse(left)
|
|
11
|
+
parsed_right = parse(right)
|
|
12
|
+
return Version.compare(left, right) unless parsed_left && parsed_right
|
|
13
|
+
|
|
14
|
+
parsed_left.core.zip(parsed_right.core).each do |left_part, right_part|
|
|
15
|
+
comparison = VersionComparison.compare_numbers(left_part, right_part)
|
|
16
|
+
return comparison unless comparison.zero?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
comparison = SemverVersion.compare_prerelease(parsed_left.prerelease, parsed_right.prerelease)
|
|
20
|
+
return comparison unless comparison.zero?
|
|
21
|
+
|
|
22
|
+
compare_build(parsed_left.build, parsed_right.build)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def parse(value)
|
|
26
|
+
return nil unless PATTERN.match?(value.to_s)
|
|
27
|
+
|
|
28
|
+
SemverVersion.parse(value)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def valid?(value)
|
|
32
|
+
!parse(value.to_s.strip).nil?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def normalize(value)
|
|
36
|
+
normalized = SemverVersion.normalize(value.to_s.strip)
|
|
37
|
+
core_and_prerelease, build = normalized.split("+", 2)
|
|
38
|
+
return core_and_prerelease unless build
|
|
39
|
+
|
|
40
|
+
normalized_build = build.split(".").map do |part|
|
|
41
|
+
VersionComparison.numeric?(part) ? VersionComparison.normalize_number(part) : part
|
|
42
|
+
end
|
|
43
|
+
"#{core_and_prerelease}+#{normalized_build.join(".")}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def prerelease?(value)
|
|
47
|
+
parsed = parse(value.to_s.strip)
|
|
48
|
+
parsed && !parsed.prerelease.empty?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def compare_build(left, right)
|
|
52
|
+
return 0 if left.empty? && right.empty?
|
|
53
|
+
return -1 if left.empty?
|
|
54
|
+
return 1 if right.empty?
|
|
55
|
+
|
|
56
|
+
SemverVersion.compare_prerelease(left, right)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "version_comparison"
|
|
4
|
+
|
|
5
|
+
module Vers
|
|
6
|
+
module PyPIVersion
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
PATTERN = /\A\s*v?
|
|
10
|
+
(?:(\d+)!)?
|
|
11
|
+
(\d+(?:\.\d+)*)
|
|
12
|
+
(?:[-_.]?(alpha|beta|preview|pre|rc|a|b|c)[-_.]?(\d*))?
|
|
13
|
+
(?:(?:[-_.]?(post|rev|r)[-_.]?(\d*))|(?:-(\d+)))?
|
|
14
|
+
(?:[-_.]?(dev)[-_.]?(\d*))?
|
|
15
|
+
(?:\+([a-z0-9]+(?:[-_.][a-z0-9]+)*))?
|
|
16
|
+
\s*\z/ix
|
|
17
|
+
|
|
18
|
+
PRE_TAGS = {
|
|
19
|
+
"a" => 0,
|
|
20
|
+
"alpha" => 0,
|
|
21
|
+
"b" => 1,
|
|
22
|
+
"beta" => 1,
|
|
23
|
+
"c" => 2,
|
|
24
|
+
"rc" => 2,
|
|
25
|
+
"pre" => 2,
|
|
26
|
+
"preview" => 2
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
LocalPart = Data.define(:value, :numeric)
|
|
30
|
+
Parsed = Data.define(
|
|
31
|
+
:epoch,
|
|
32
|
+
:release,
|
|
33
|
+
:pre_tag,
|
|
34
|
+
:pre_number,
|
|
35
|
+
:post_number,
|
|
36
|
+
:dev_number,
|
|
37
|
+
:local
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def compare(left, right)
|
|
41
|
+
parsed_left = parse(left)
|
|
42
|
+
parsed_right = parse(right)
|
|
43
|
+
comparison = compare_public_parsed(left, right, parsed_left, parsed_right)
|
|
44
|
+
return comparison unless comparison.zero?
|
|
45
|
+
return comparison unless parsed_left && parsed_right
|
|
46
|
+
|
|
47
|
+
compare_local(parsed_left.local, parsed_right.local)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def compare_public(left, right)
|
|
51
|
+
parsed_left = parse(left)
|
|
52
|
+
parsed_right = parse(right)
|
|
53
|
+
compare_public_parsed(left, right, parsed_left, parsed_right)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def compare_public_parsed(left, right, parsed_left, parsed_right)
|
|
57
|
+
return Version.compare(left, right) unless parsed_left && parsed_right
|
|
58
|
+
|
|
59
|
+
comparison = VersionComparison.compare_numbers(parsed_left.epoch, parsed_right.epoch)
|
|
60
|
+
return comparison unless comparison.zero?
|
|
61
|
+
|
|
62
|
+
comparison = compare_release(parsed_left.release, parsed_right.release)
|
|
63
|
+
return comparison unless comparison.zero?
|
|
64
|
+
|
|
65
|
+
comparison = compare_pre(parsed_left, parsed_right)
|
|
66
|
+
return comparison unless comparison.zero?
|
|
67
|
+
|
|
68
|
+
comparison = compare_post(parsed_left, parsed_right)
|
|
69
|
+
return comparison unless comparison.zero?
|
|
70
|
+
|
|
71
|
+
comparison = compare_dev(parsed_left, parsed_right)
|
|
72
|
+
return comparison unless comparison.zero?
|
|
73
|
+
|
|
74
|
+
0
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def parse(value)
|
|
78
|
+
match = PATTERN.match(value.to_s)
|
|
79
|
+
return nil unless match
|
|
80
|
+
|
|
81
|
+
pre_tag = match[3]&.downcase
|
|
82
|
+
post_number = match[7] || (match[5] ? match[6] : nil)
|
|
83
|
+
local = if match[10]
|
|
84
|
+
match[10].downcase.split(/[._-]/).map do |part|
|
|
85
|
+
LocalPart.new(part, VersionComparison.numeric?(part))
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
[]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
Parsed.new(
|
|
92
|
+
match[1].to_s,
|
|
93
|
+
match[2].split("."),
|
|
94
|
+
pre_tag && PRE_TAGS.fetch(pre_tag),
|
|
95
|
+
pre_tag ? match[4].to_s : nil,
|
|
96
|
+
post_number,
|
|
97
|
+
match[8] ? match[9].to_s : nil,
|
|
98
|
+
local.freeze
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def valid?(value)
|
|
103
|
+
!parse(value).nil?
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def prerelease?(value)
|
|
107
|
+
parsed = parse(value)
|
|
108
|
+
parsed && (!parsed.pre_tag.nil? || !parsed.dev_number.nil?)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def stable?(value)
|
|
112
|
+
parsed = parse(value)
|
|
113
|
+
parsed && parsed.pre_tag.nil? && parsed.dev_number.nil?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def normalize(value)
|
|
117
|
+
parsed = parse(value)
|
|
118
|
+
raise ArgumentError, "invalid pypi version: #{value}" unless parsed
|
|
119
|
+
|
|
120
|
+
result = +""
|
|
121
|
+
result << "#{VersionComparison.normalize_number(parsed.epoch)}!" unless VersionComparison.compare_numbers(parsed.epoch, "0").zero?
|
|
122
|
+
result << parsed.release.map { |part| VersionComparison.normalize_number(part) }.join(".")
|
|
123
|
+
|
|
124
|
+
unless parsed.pre_tag.nil?
|
|
125
|
+
result << %w[a b rc].fetch(parsed.pre_tag)
|
|
126
|
+
result << VersionComparison.normalize_number(parsed.pre_number)
|
|
127
|
+
end
|
|
128
|
+
result << ".post#{VersionComparison.normalize_number(parsed.post_number)}" unless parsed.post_number.nil?
|
|
129
|
+
result << ".dev#{VersionComparison.normalize_number(parsed.dev_number)}" unless parsed.dev_number.nil?
|
|
130
|
+
|
|
131
|
+
if parsed.local.any?
|
|
132
|
+
result << "+"
|
|
133
|
+
result << parsed.local.map { |part| part.numeric ? VersionComparison.normalize_number(part.value) : part.value }.join(".")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
result
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def specifier_equal?(version, specifier)
|
|
140
|
+
parsed_version = parse(version)
|
|
141
|
+
parsed_specifier = parse(specifier)
|
|
142
|
+
return compare(version, specifier).zero? unless parsed_version && parsed_specifier
|
|
143
|
+
versions_equal?(parsed_version, parsed_specifier, ignore_local: parsed_specifier.local.empty?)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def versions_equal?(left, right, ignore_local: false)
|
|
147
|
+
local_equal = ignore_local || compare_local(left.local, right.local).zero?
|
|
148
|
+
VersionComparison.compare_numbers(left.epoch, right.epoch).zero? &&
|
|
149
|
+
compare_release(left.release, right.release).zero? &&
|
|
150
|
+
compare_pre(left, right).zero? &&
|
|
151
|
+
compare_post(left, right).zero? &&
|
|
152
|
+
compare_dev(left, right).zero? &&
|
|
153
|
+
local_equal
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def same_release?(left, right)
|
|
157
|
+
VersionComparison.compare_numbers(left.epoch, right.epoch).zero? &&
|
|
158
|
+
compare_release(left.release, right.release).zero?
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def without_post_and_dev(version)
|
|
162
|
+
Parsed.new(
|
|
163
|
+
version.epoch,
|
|
164
|
+
version.release,
|
|
165
|
+
version.pre_tag,
|
|
166
|
+
version.pre_number,
|
|
167
|
+
nil,
|
|
168
|
+
nil,
|
|
169
|
+
[].freeze
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def without_dev(version)
|
|
174
|
+
Parsed.new(
|
|
175
|
+
version.epoch,
|
|
176
|
+
version.release,
|
|
177
|
+
version.pre_tag,
|
|
178
|
+
version.pre_number,
|
|
179
|
+
version.post_number,
|
|
180
|
+
nil,
|
|
181
|
+
[].freeze
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def compare_release(left, right)
|
|
186
|
+
[left.length, right.length].max.times do |index|
|
|
187
|
+
comparison = VersionComparison.compare_numbers(left[index], right[index])
|
|
188
|
+
return comparison unless comparison.zero?
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
0
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def compare_pre(left, right)
|
|
195
|
+
left_rank = pre_rank(left)
|
|
196
|
+
right_rank = pre_rank(right)
|
|
197
|
+
comparison = left_rank <=> right_rank
|
|
198
|
+
return comparison unless comparison.zero?
|
|
199
|
+
return 0 if left.pre_tag.nil?
|
|
200
|
+
|
|
201
|
+
comparison = left.pre_tag <=> right.pre_tag
|
|
202
|
+
return comparison unless comparison.zero?
|
|
203
|
+
|
|
204
|
+
VersionComparison.compare_numbers(left.pre_number, right.pre_number)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def pre_rank(version)
|
|
208
|
+
return -1 if version.pre_tag.nil? && version.post_number.nil? && !version.dev_number.nil?
|
|
209
|
+
return 1 if version.pre_tag.nil?
|
|
210
|
+
|
|
211
|
+
0
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def compare_post(left, right)
|
|
215
|
+
return 1 if !left.post_number.nil? && right.post_number.nil?
|
|
216
|
+
return -1 if left.post_number.nil? && !right.post_number.nil?
|
|
217
|
+
return 0 if left.post_number.nil?
|
|
218
|
+
|
|
219
|
+
VersionComparison.compare_numbers(left.post_number, right.post_number)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def compare_dev(left, right)
|
|
223
|
+
return -1 if !left.dev_number.nil? && right.dev_number.nil?
|
|
224
|
+
return 1 if left.dev_number.nil? && !right.dev_number.nil?
|
|
225
|
+
return 0 if left.dev_number.nil?
|
|
226
|
+
|
|
227
|
+
VersionComparison.compare_numbers(left.dev_number, right.dev_number)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def compare_local(left, right)
|
|
231
|
+
return 0 if left.empty? && right.empty?
|
|
232
|
+
return -1 if left.empty?
|
|
233
|
+
return 1 if right.empty?
|
|
234
|
+
|
|
235
|
+
[left.length, right.length].max.times do |index|
|
|
236
|
+
return -1 unless left[index]
|
|
237
|
+
return 1 unless right[index]
|
|
238
|
+
|
|
239
|
+
left_part = left[index]
|
|
240
|
+
right_part = right[index]
|
|
241
|
+
if left_part.numeric != right_part.numeric
|
|
242
|
+
return left_part.numeric ? 1 : -1
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
comparison = if left_part.numeric
|
|
246
|
+
VersionComparison.compare_numbers(left_part.value, right_part.value)
|
|
247
|
+
else
|
|
248
|
+
left_part.value <=> right_part.value
|
|
249
|
+
end
|
|
250
|
+
return comparison unless comparison.zero?
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
0
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
data/lib/vers/scheme.rb
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vers
|
|
4
|
+
module Scheme
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
ALIASES = {
|
|
8
|
+
"rubygems" => "gem",
|
|
9
|
+
"debian" => "deb",
|
|
10
|
+
"golang" => "go",
|
|
11
|
+
"elixir" => "hex",
|
|
12
|
+
"alpine" => "apk"
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
HANDLERS = {
|
|
16
|
+
"alpm" => ALPMVersion,
|
|
17
|
+
"apk" => APKVersion,
|
|
18
|
+
"bazel" => BazelVersion,
|
|
19
|
+
"cargo" => CargoVersion,
|
|
20
|
+
"composer" => ComposerVersion,
|
|
21
|
+
"conan" => ConanVersion,
|
|
22
|
+
"deb" => DebianVersion,
|
|
23
|
+
"datetime" => DatetimeVersion,
|
|
24
|
+
"gem" => GemVersion,
|
|
25
|
+
"gentoo" => GentooVersion,
|
|
26
|
+
"go" => GoVersion,
|
|
27
|
+
"hex" => SemverVersion,
|
|
28
|
+
"intdot" => IntDotVersion,
|
|
29
|
+
"lexicographic" => LexicographicVersion,
|
|
30
|
+
"maven" => MavenVersion,
|
|
31
|
+
"nginx" => SemverVersion,
|
|
32
|
+
"npm" => NpmVersion,
|
|
33
|
+
"nuget" => NuGetVersion,
|
|
34
|
+
"openssl" => OpenSSLVersion,
|
|
35
|
+
"pub" => PubVersion,
|
|
36
|
+
"pypi" => PyPIVersion,
|
|
37
|
+
"rpm" => RPMVersion,
|
|
38
|
+
"semver" => SemverVersion
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
def canonical(value)
|
|
42
|
+
return nil if value.nil?
|
|
43
|
+
|
|
44
|
+
scheme = value.to_s.downcase
|
|
45
|
+
ALIASES.fetch(scheme, scheme)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def handler(value)
|
|
49
|
+
HANDLERS[canonical(value)]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "version_comparison"
|
|
4
|
+
|
|
5
|
+
module Vers
|
|
6
|
+
module SemverVersion
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
Parsed = Data.define(:core, :prerelease, :build)
|
|
10
|
+
|
|
11
|
+
def compare(left, right)
|
|
12
|
+
parsed_left = parse(left)
|
|
13
|
+
parsed_right = parse(right)
|
|
14
|
+
return left <=> right unless parsed_left && parsed_right
|
|
15
|
+
|
|
16
|
+
parsed_left.core.zip(parsed_right.core).each do |left_part, right_part|
|
|
17
|
+
comparison = VersionComparison.compare_numbers(left_part, right_part)
|
|
18
|
+
return comparison unless comparison.zero?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
compare_prerelease(parsed_left.prerelease, parsed_right.prerelease)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def parse(value)
|
|
25
|
+
string = value.to_s.strip
|
|
26
|
+
index = string.start_with?("v") ? 1 : 0
|
|
27
|
+
core = []
|
|
28
|
+
|
|
29
|
+
3.times do |part|
|
|
30
|
+
start = index
|
|
31
|
+
index += 1 while index < string.length && string.getbyte(index).between?(48, 57)
|
|
32
|
+
return nil if index == start
|
|
33
|
+
|
|
34
|
+
core << string[start...index]
|
|
35
|
+
break unless index < string.length && string.getbyte(index) == 46
|
|
36
|
+
return nil if part == 2
|
|
37
|
+
|
|
38
|
+
index += 1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
core << "0" while core.length < 3
|
|
42
|
+
prerelease = ""
|
|
43
|
+
build = ""
|
|
44
|
+
|
|
45
|
+
if index < string.length && string.getbyte(index) == 45
|
|
46
|
+
start = index + 1
|
|
47
|
+
index = start
|
|
48
|
+
index += 1 while index < string.length && string.getbyte(index) != 43
|
|
49
|
+
return nil if index == start
|
|
50
|
+
|
|
51
|
+
prerelease = string[start...index]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if index < string.length && string.getbyte(index) == 43
|
|
55
|
+
index += 1
|
|
56
|
+
return nil if index == string.length || string[index..].include?("\n")
|
|
57
|
+
|
|
58
|
+
build = string[index..]
|
|
59
|
+
index = string.length
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
return nil unless index == string.length
|
|
63
|
+
|
|
64
|
+
Parsed.new(core.freeze, prerelease, build)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def valid?(value)
|
|
68
|
+
parsed = parse(value.to_s.strip)
|
|
69
|
+
return false unless parsed
|
|
70
|
+
|
|
71
|
+
[parsed.prerelease, parsed.build].all? do |field|
|
|
72
|
+
field.empty? || field.split(".", -1).all? { |part| part.match?(/\A[0-9A-Za-z-]+\z/) }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def normalize(value, preserve_v: false)
|
|
77
|
+
string = value.to_s.strip
|
|
78
|
+
parsed = parse(string)
|
|
79
|
+
raise ArgumentError, "Invalid semantic version: #{value}" unless parsed && valid?(string)
|
|
80
|
+
|
|
81
|
+
result = parsed.core.map { |part| VersionComparison.normalize_number(part) }.join(".")
|
|
82
|
+
result = "v#{result}" if preserve_v && string.start_with?("v")
|
|
83
|
+
unless parsed.prerelease.empty?
|
|
84
|
+
prerelease = parsed.prerelease.split(".").map do |part|
|
|
85
|
+
VersionComparison.numeric?(part) ? VersionComparison.normalize_number(part) : part
|
|
86
|
+
end
|
|
87
|
+
result += "-#{prerelease.join(".")}"
|
|
88
|
+
end
|
|
89
|
+
result += "+#{parsed.build}" unless parsed.build.empty?
|
|
90
|
+
result
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def prerelease?(value)
|
|
94
|
+
parsed = parse(value.to_s.strip)
|
|
95
|
+
parsed && !parsed.prerelease.empty?
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def compare_prerelease(left, right)
|
|
99
|
+
return 0 if left.empty? && right.empty?
|
|
100
|
+
return 1 if left.empty?
|
|
101
|
+
return -1 if right.empty?
|
|
102
|
+
|
|
103
|
+
left_parts = left.split(".", -1)
|
|
104
|
+
right_parts = right.split(".", -1)
|
|
105
|
+
left_parts.zip(right_parts).each do |left_part, right_part|
|
|
106
|
+
return 1 if right_part.nil?
|
|
107
|
+
|
|
108
|
+
comparison = compare_identifier(left_part, right_part)
|
|
109
|
+
return comparison unless comparison.zero?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
left_parts.length <=> right_parts.length
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def compare_identifier(left, right)
|
|
116
|
+
left_numeric = VersionComparison.numeric?(left)
|
|
117
|
+
right_numeric = VersionComparison.numeric?(right)
|
|
118
|
+
return -1 if left_numeric && !right_numeric
|
|
119
|
+
return 1 if !left_numeric && right_numeric
|
|
120
|
+
|
|
121
|
+
if left_numeric
|
|
122
|
+
VersionComparison.compare_numbers(left, right)
|
|
123
|
+
else
|
|
124
|
+
left <=> right
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
module NpmVersion
|
|
130
|
+
extend self
|
|
131
|
+
|
|
132
|
+
def compare(left, right)
|
|
133
|
+
SemverVersion.compare(left.strip, right.strip)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def valid?(value)
|
|
137
|
+
SemverVersion.valid?(value.to_s.strip)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def normalize(value)
|
|
141
|
+
SemverVersion.normalize(value.to_s.strip)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def prerelease?(value)
|
|
145
|
+
SemverVersion.prerelease?(value.to_s.strip)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
module CargoVersion
|
|
150
|
+
extend self
|
|
151
|
+
|
|
152
|
+
def compare(left, right)
|
|
153
|
+
comparison = SemverVersion.compare(left, right)
|
|
154
|
+
return comparison unless comparison.zero?
|
|
155
|
+
|
|
156
|
+
parsed_left = SemverVersion.parse(left)
|
|
157
|
+
parsed_right = SemverVersion.parse(right)
|
|
158
|
+
return comparison unless parsed_left && parsed_right
|
|
159
|
+
|
|
160
|
+
compare_build(parsed_left.build, parsed_right.build)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def compare_build(left, right)
|
|
164
|
+
return 0 if left == right
|
|
165
|
+
return -1 if left.empty?
|
|
166
|
+
return 1 if right.empty?
|
|
167
|
+
|
|
168
|
+
left_parts = left.split(".", -1)
|
|
169
|
+
right_parts = right.split(".", -1)
|
|
170
|
+
left_parts.zip(right_parts).each do |left_part, right_part|
|
|
171
|
+
return 1 if right_part.nil?
|
|
172
|
+
|
|
173
|
+
comparison = compare_build_identifier(left_part, right_part)
|
|
174
|
+
return comparison unless comparison.zero?
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
left_parts.length <=> right_parts.length
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def compare_build_identifier(left, right)
|
|
181
|
+
left_numeric = VersionComparison.numeric?(left)
|
|
182
|
+
right_numeric = VersionComparison.numeric?(right)
|
|
183
|
+
return -1 if left_numeric && !right_numeric
|
|
184
|
+
return 1 if !left_numeric && right_numeric
|
|
185
|
+
return left <=> right unless left_numeric
|
|
186
|
+
|
|
187
|
+
normalized_left = VersionComparison.normalize_number(left)
|
|
188
|
+
normalized_right = VersionComparison.normalize_number(right)
|
|
189
|
+
comparison = normalized_left.length <=> normalized_right.length
|
|
190
|
+
return comparison unless comparison.zero?
|
|
191
|
+
|
|
192
|
+
comparison = normalized_left <=> normalized_right
|
|
193
|
+
return comparison unless comparison.zero?
|
|
194
|
+
|
|
195
|
+
left.length <=> right.length
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def valid?(value)
|
|
199
|
+
SemverVersion.valid?(value.to_s.strip)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def normalize(value)
|
|
203
|
+
SemverVersion.normalize(value.to_s.strip)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def prerelease?(value)
|
|
207
|
+
SemverVersion.prerelease?(value.to_s.strip)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
module GoVersion
|
|
212
|
+
extend self
|
|
213
|
+
|
|
214
|
+
def compare(left, right)
|
|
215
|
+
return SemverVersion.compare(left, right) unless left.start_with?("v") || right.start_with?("v")
|
|
216
|
+
|
|
217
|
+
parsed_left = parse(left)
|
|
218
|
+
parsed_right = parse(right)
|
|
219
|
+
return 1 if parsed_left && !parsed_right
|
|
220
|
+
return -1 if !parsed_left && parsed_right
|
|
221
|
+
return 0 unless parsed_left && parsed_right
|
|
222
|
+
|
|
223
|
+
parsed_left.core.zip(parsed_right.core).each do |left_part, right_part|
|
|
224
|
+
comparison = VersionComparison.compare_numbers(left_part, right_part)
|
|
225
|
+
return comparison unless comparison.zero?
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
SemverVersion.compare_prerelease(parsed_left.prerelease, parsed_right.prerelease)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def parse(value)
|
|
232
|
+
return nil unless value.start_with?("v")
|
|
233
|
+
|
|
234
|
+
string = value.delete_prefix("v")
|
|
235
|
+
core_end = string.index(/[-+]/) || string.length
|
|
236
|
+
core = string[0...core_end].split(".", -1)
|
|
237
|
+
return nil if core.empty? || core.length > 3
|
|
238
|
+
return nil unless core.all? { |part| valid_core_part?(part) }
|
|
239
|
+
return nil if core.length < 3 && core_end != string.length
|
|
240
|
+
|
|
241
|
+
core << "0" while core.length < 3
|
|
242
|
+
remainder = string[core_end..]
|
|
243
|
+
prerelease = ""
|
|
244
|
+
build = ""
|
|
245
|
+
|
|
246
|
+
if remainder.start_with?("-")
|
|
247
|
+
value = remainder.delete_prefix("-")
|
|
248
|
+
prerelease_end = value.index("+") || value.length
|
|
249
|
+
prerelease = value[0...prerelease_end]
|
|
250
|
+
return nil unless valid_identifiers?(prerelease, build: false)
|
|
251
|
+
|
|
252
|
+
remainder = value[prerelease_end..]
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
if remainder.start_with?("+")
|
|
256
|
+
build = remainder.delete_prefix("+")
|
|
257
|
+
return nil unless valid_identifiers?(build, build: true)
|
|
258
|
+
|
|
259
|
+
remainder = ""
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
return nil unless remainder.empty?
|
|
263
|
+
|
|
264
|
+
SemverVersion::Parsed.new(core.freeze, prerelease, build)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def valid_core_part?(part)
|
|
268
|
+
VersionComparison.numeric?(part) && (part == "0" || !part.start_with?("0"))
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def valid_identifiers?(value, build:)
|
|
272
|
+
return false if value.empty?
|
|
273
|
+
|
|
274
|
+
value.split(".", -1).all? do |identifier|
|
|
275
|
+
next false unless identifier.match?(/\A[0-9A-Za-z-]+\z/)
|
|
276
|
+
|
|
277
|
+
build || identifier == "0" || !VersionComparison.numeric?(identifier) || !identifier.start_with?("0")
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def valid?(value)
|
|
282
|
+
string = value.to_s.strip
|
|
283
|
+
string.start_with?("v") ? !parse(string).nil? : SemverVersion.valid?(string)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def normalize(value)
|
|
287
|
+
SemverVersion.normalize(value.to_s.strip, preserve_v: true)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def prerelease?(value)
|
|
291
|
+
SemverVersion.prerelease?(value.to_s.strip)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|