if_test_foo 0.0.4 → 0.0.5
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/lib/semver_dialects/cli.rb +34 -0
- data/lib/semver_dialects/command.rb +122 -0
- data/lib/semver_dialects/commands/check_version.rb +35 -0
- data/lib/semver_dialects/semantic_version/semantic_version.rb +323 -0
- data/lib/semver_dialects/semantic_version/version_cut.rb +176 -0
- data/lib/semver_dialects/semantic_version/version_interval.rb +286 -0
- data/lib/semver_dialects/semantic_version/version_parser.rb +37 -0
- data/lib/semver_dialects/semantic_version/version_range.rb +191 -0
- data/lib/semver_dialects/semantic_version/version_translator.rb +172 -0
- data/lib/semver_dialects/version.rb +5 -0
- data/lib/semver_dialects.rb +56 -0
- data/lib/utils.rb +41 -0
- metadata +18 -7
- data/lib/foo.rb +0 -5
@@ -0,0 +1,286 @@
|
|
1
|
+
module IntervalType
|
2
|
+
UNKNOWN = 0
|
3
|
+
LEFT_OPEN = 1
|
4
|
+
LEFT_CLOSED = 2
|
5
|
+
RIGHT_OPEN = 4
|
6
|
+
RIGHT_CLOSED = 8
|
7
|
+
end
|
8
|
+
|
9
|
+
class VersionInterval
|
10
|
+
attr_accessor :type, :start_cut, :end_cut
|
11
|
+
|
12
|
+
def initialize(type, start_cut, end_cut)
|
13
|
+
@type = type
|
14
|
+
@start_cut = start_cut
|
15
|
+
@end_cut = end_cut
|
16
|
+
end
|
17
|
+
|
18
|
+
def intersect(other_interval)
|
19
|
+
# this look odd -- we have to use it here though, because it may be that placeholders are present inside
|
20
|
+
# the version for which > and < would yield true
|
21
|
+
return EmptyInterval.new if !(@start_cut <= other_interval.end_cut) || !(other_interval.start_cut <= @end_cut)
|
22
|
+
|
23
|
+
start_cut_new = max(@start_cut, other_interval.start_cut)
|
24
|
+
end_cut_new = min(@end_cut, other_interval.end_cut)
|
25
|
+
|
26
|
+
# compute the boundaries for the intersection
|
27
|
+
type = compute_intersection_boundary(self, other_interval, start_cut_new, end_cut_new)
|
28
|
+
interval = VersionInterval.new(type, start_cut_new, end_cut_new)
|
29
|
+
half_open = !(interval.bit_set?(IntervalType::RIGHT_CLOSED) && interval.bit_set?(IntervalType::LEFT_CLOSED))
|
30
|
+
|
31
|
+
interval.singleton? && half_open ? EmptyInterval.new : interval
|
32
|
+
end
|
33
|
+
|
34
|
+
def union(other_interval)
|
35
|
+
return EmptyInterval.new if self.intersect(other_interval).instance_of?(EmptyInterval)
|
36
|
+
|
37
|
+
start_cut_new = min(@start_cut, other_interval.start_cut)
|
38
|
+
end_cut_new = max(@end_cut, other_interval.end_cut)
|
39
|
+
|
40
|
+
# compute the boundaries for the union
|
41
|
+
type = compute_union_boundary(self, other_interval, start_cut_new, end_cut_new)
|
42
|
+
VersionInterval.new(type, start_cut_new, end_cut_new)
|
43
|
+
end
|
44
|
+
|
45
|
+
def collapse(other_interval)
|
46
|
+
return EmptyInterval.new if self.intersect(other_interval).instance_of?(EmptyInterval)
|
47
|
+
|
48
|
+
frame = [@start_cut, other_interval.start_cut, @end_cut, other_interval.end_cut].reject { |cut| special(cut) }
|
49
|
+
min_cut = frame.reduce { |smallest, current| smallest < current ? smallest : current }
|
50
|
+
max_cut = frame.reduce { |biggest, current| biggest > current ? biggest : current }
|
51
|
+
|
52
|
+
# compute the boundaries for the union
|
53
|
+
type = compute_union_boundary(self, other_interval, min_cut, max_cut)
|
54
|
+
VersionInterval.new(type, min_cut, max_cut)
|
55
|
+
end
|
56
|
+
|
57
|
+
def special(cut)
|
58
|
+
cut.instance_of?(AboveAll) || cut.instance_of?(BelowAll)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
s = ""
|
63
|
+
s += bit_set?(IntervalType::LEFT_CLOSED) ? "[" : ""
|
64
|
+
s += bit_set?(IntervalType::LEFT_OPEN) ? "(" : ""
|
65
|
+
s += [@start_cut, @end_cut].join(",")
|
66
|
+
s += bit_set?(IntervalType::RIGHT_CLOSED) ? "]" : ""
|
67
|
+
s += bit_set?(IntervalType::RIGHT_OPEN) ? ")" : ""
|
68
|
+
s
|
69
|
+
end
|
70
|
+
|
71
|
+
# this function returns a human-readable descriptions of the version strings
|
72
|
+
def to_description_s
|
73
|
+
s = ""
|
74
|
+
if self.distinct?
|
75
|
+
s = "version #{@start_cut.to_s}"
|
76
|
+
elsif self.universal?
|
77
|
+
s = "all versions "
|
78
|
+
else
|
79
|
+
s = "all versions "
|
80
|
+
s += start_cut.instance_of?(BelowAll) ? "" : bit_set?(IntervalType::LEFT_OPEN) ? "after #{@start_cut.to_s} " : bit_set?(IntervalType::LEFT_CLOSED) ? "starting from #{@start_cut.to_s} " : ""
|
81
|
+
s += end_cut.instance_of?(AboveAll) ? "" : bit_set?(IntervalType::RIGHT_OPEN) ? "before #{@end_cut.to_s}" : bit_set?(IntervalType::RIGHT_CLOSED) ? "up to #{@end_cut.to_s}" : ""
|
82
|
+
end
|
83
|
+
s.strip
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_nuget_s
|
87
|
+
to_maven_s
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_maven_s
|
91
|
+
s = ""
|
92
|
+
# special case -- distinct version
|
93
|
+
if self.distinct?
|
94
|
+
s += "[#{@start_cut.to_s}]"
|
95
|
+
else
|
96
|
+
s += start_cut.instance_of?(BelowAll) ? "(," : bit_set?(IntervalType::LEFT_OPEN) ? "[#{@start_cut.to_s}," : bit_set?(IntervalType::LEFT_CLOSED) ? "[#{@start_cut.to_s}," : ""
|
97
|
+
s += end_cut.instance_of?(AboveAll) ? ")" : bit_set?(IntervalType::RIGHT_OPEN) ? "#{@end_cut.to_s})" : bit_set?(IntervalType::RIGHT_CLOSED) ? "#{@end_cut.to_s}]" : ""
|
98
|
+
end
|
99
|
+
s
|
100
|
+
end
|
101
|
+
|
102
|
+
def distinct?
|
103
|
+
bit_set?(IntervalType::LEFT_CLOSED) && bit_set?(IntervalType::RIGHT_CLOSED) && @start_cut == @end_cut
|
104
|
+
end
|
105
|
+
|
106
|
+
def subsumes?(other)
|
107
|
+
@start_cut <= other.start_cut && @end_cut >= other.end_cut
|
108
|
+
end
|
109
|
+
|
110
|
+
def universal?
|
111
|
+
(bit_set?(IntervalType::LEFT_OPEN) && bit_set?(IntervalType::RIGHT_OPEN) && @start_cut.instance_of?(BelowAll) && @end_cut.instance_of?(AboveAll)) || @start_cut.is_initial_version? && @end_cut.instance_of?(AboveAll)
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_gem_s
|
115
|
+
get_canoncial_s
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_ruby_s
|
119
|
+
get_canoncial_s
|
120
|
+
end
|
121
|
+
|
122
|
+
def to_npm_s
|
123
|
+
get_canoncial_s
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_conan_s
|
127
|
+
get_canoncial_s
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_go_s
|
131
|
+
get_canoncial_s
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_pypi_s
|
135
|
+
get_canoncial_s(',', '==')
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_packagist_s
|
139
|
+
get_canoncial_s(',')
|
140
|
+
end
|
141
|
+
|
142
|
+
def empty?
|
143
|
+
self.instance_of?(EmptyInterval)
|
144
|
+
end
|
145
|
+
|
146
|
+
def singleton?
|
147
|
+
@start_cut == @end_cut && @start_cut.value == @end_cut.value
|
148
|
+
end
|
149
|
+
|
150
|
+
def diff(other_interval, abs = true)
|
151
|
+
if self.distinct? && other_interval.distinct?
|
152
|
+
self.start_cut.diff(other_interval.start_cut, abs)
|
153
|
+
else
|
154
|
+
EmptyInterval.new()
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def joinable?(other_interval)
|
159
|
+
other_interval.start_cut.is_successor_of?(self.end_cut) || universal?
|
160
|
+
end
|
161
|
+
|
162
|
+
def join(other_interval)
|
163
|
+
if self.joinable?(other_interval)
|
164
|
+
_join(self, other_interval)
|
165
|
+
elsif other_interval.joinable?(self)
|
166
|
+
_join(other_interval, self)
|
167
|
+
else
|
168
|
+
EmptyInterval.new()
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def cross_total
|
173
|
+
if distinct?
|
174
|
+
@start_cut.cross_total
|
175
|
+
else
|
176
|
+
@start_cut.cross_total + @end_cut.cross_total
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def ==(other_interval)
|
181
|
+
@start_cut == other_interval.start_cut && @end_cut == other_interval.end_cut && @type == other_interval.type
|
182
|
+
end
|
183
|
+
|
184
|
+
# inverts the given version interval -- note that this function amy return two version intervals
|
185
|
+
# e.g., (2,10] -> (-inf, 2], (10, +inf)
|
186
|
+
# left right
|
187
|
+
def invert
|
188
|
+
intervals = []
|
189
|
+
left_type = bit_set?(IntervalType::LEFT_OPEN) ? IntervalType::RIGHT_CLOSED : IntervalType::RIGHT_OPEN
|
190
|
+
left_type = left_type | IntervalType::LEFT_OPEN
|
191
|
+
right_type = bit_set?(IntervalType::RIGHT_OPEN) ? IntervalType::LEFT_CLOSED : IntervalType::LEFT_OPEN
|
192
|
+
right_type = right_type | IntervalType::RIGHT_OPEN
|
193
|
+
|
194
|
+
intervals << VersionInterval.new(left_type, BelowAll.new, @start_cut) unless @start_cut.instance_of?(BelowAll)
|
195
|
+
intervals << VersionInterval.new(right_type, @end_cut, AboveAll.new) unless @end_cut.instance_of?(AboveAll)
|
196
|
+
intervals
|
197
|
+
end
|
198
|
+
|
199
|
+
def bit_set?(interval_type)
|
200
|
+
@type & interval_type != 0
|
201
|
+
end
|
202
|
+
|
203
|
+
protected
|
204
|
+
|
205
|
+
# computes the boundary type for union operation
|
206
|
+
def compute_union_boundary(interval_a, interval_b, start_cut_new, end_cut_new)
|
207
|
+
compute_boundary(interval_a, interval_b, start_cut_new, end_cut_new, IntervalType::LEFT_CLOSED, IntervalType::RIGHT_CLOSED)
|
208
|
+
end
|
209
|
+
|
210
|
+
def compute_intersection_boundary(interval_a, interval_b, start_cut_new, end_cut_new)
|
211
|
+
compute_boundary(interval_a, interval_b,start_cut_new, end_cut_new, IntervalType::LEFT_OPEN, IntervalType::RIGHT_OPEN)
|
212
|
+
end
|
213
|
+
|
214
|
+
def compute_boundary(interval_a, interval_b, start_cut_new, end_cut_new, left_check, right_check)
|
215
|
+
start_cut_a = interval_a.start_cut
|
216
|
+
end_cut_a = interval_a.end_cut
|
217
|
+
type_a = interval_a.type
|
218
|
+
|
219
|
+
start_cut_b = interval_b.start_cut
|
220
|
+
end_cut_b = interval_b.end_cut
|
221
|
+
type_b = interval_b.type
|
222
|
+
|
223
|
+
left_fill = left_check == IntervalType::LEFT_OPEN ? IntervalType::LEFT_CLOSED : IntervalType::LEFT_OPEN
|
224
|
+
right_fill = right_check == IntervalType::RIGHT_OPEN ? IntervalType::RIGHT_CLOSED : IntervalType::RIGHT_OPEN
|
225
|
+
|
226
|
+
# compute the boundaries for the union
|
227
|
+
if start_cut_b == start_cut_a && start_cut_b == start_cut_b
|
228
|
+
one_left_closed = left_type(type_a) == left_check || left_type(type_b) == left_check
|
229
|
+
left_type = one_left_closed ? left_check : left_fill
|
230
|
+
else
|
231
|
+
left_type = start_cut_new == start_cut_a ? left_type(type_a) : left_type(type_b)
|
232
|
+
end
|
233
|
+
|
234
|
+
if end_cut_b == end_cut_a && end_cut_b == end_cut_b
|
235
|
+
one_right_closed = right_type(type_a) == right_check || right_type(type_b) == right_check
|
236
|
+
right_type = one_right_closed ? right_check : right_fill
|
237
|
+
else
|
238
|
+
right_type = end_cut_new == end_cut_a ? right_type(type_a) : right_type(type_b)
|
239
|
+
end
|
240
|
+
|
241
|
+
left_type | right_type
|
242
|
+
end
|
243
|
+
|
244
|
+
def _join(first_interval, second_interval)
|
245
|
+
if first_interval.joinable?(second_interval)
|
246
|
+
VersionInterval.new(first_interval.type, first_interval.start_cut.dup, second_interval.end_cut.dup)
|
247
|
+
else
|
248
|
+
EmptyInterval.new()
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def get_canoncial_s(delimiter = " ", eq = '=')
|
253
|
+
if self.distinct?
|
254
|
+
"#{eq}#{@start_cut.to_s}"
|
255
|
+
else
|
256
|
+
first = start_cut.instance_of?(BelowAll) ? "" : bit_set?(IntervalType::LEFT_OPEN) ? ">#{@start_cut.to_s}" : bit_set?(IntervalType::LEFT_CLOSED) ? ">=#{@start_cut.to_s}" : ""
|
257
|
+
second = end_cut.instance_of?(AboveAll) ? "" : bit_set?(IntervalType::RIGHT_OPEN) ? "<#{@end_cut.to_s}" : bit_set?(IntervalType::RIGHT_CLOSED) ? "<=#{@end_cut.to_s}" : ""
|
258
|
+
!first.empty? && !second.empty? ? "#{first}#{delimiter}#{second}" : first + second
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def max(cut_a, cut_b)
|
263
|
+
cut_a > cut_b ? cut_a : cut_b
|
264
|
+
end
|
265
|
+
|
266
|
+
def min(cut_a, cut_b)
|
267
|
+
cut_a < cut_b ? cut_a : cut_b
|
268
|
+
end
|
269
|
+
|
270
|
+
def right_type(type)
|
271
|
+
(IntervalType::RIGHT_OPEN | IntervalType::RIGHT_CLOSED) & type
|
272
|
+
end
|
273
|
+
|
274
|
+
def left_type(type)
|
275
|
+
(IntervalType::LEFT_OPEN | IntervalType::LEFT_CLOSED) & type
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
class EmptyInterval < VersionInterval
|
280
|
+
def initialize()
|
281
|
+
end
|
282
|
+
|
283
|
+
def to_s
|
284
|
+
"empty"
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative "version_cut"
|
2
|
+
require_relative "version_interval"
|
3
|
+
|
4
|
+
module VersionParser
|
5
|
+
def self.parse(versionstring)
|
6
|
+
if (versionstring == "=*")
|
7
|
+
# special case = All Versions
|
8
|
+
return VersionInterval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_OPEN, BelowAll.new(), AboveAll.new())
|
9
|
+
end
|
10
|
+
|
11
|
+
version_items = versionstring.split(" ")
|
12
|
+
interval = VersionInterval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_OPEN, BelowAll.new(), AboveAll.new())
|
13
|
+
version_items.each do
|
14
|
+
|version_item|
|
15
|
+
matches = version_item.match /(?<op>[><=]+) *(?<version>[a-zA-Z0-9\-_\.\*]+)/
|
16
|
+
version_string = matches[:version]
|
17
|
+
case matches[:op]
|
18
|
+
when ">="
|
19
|
+
new_interval = VersionInterval.new(IntervalType::LEFT_CLOSED | IntervalType::RIGHT_OPEN, VersionCut.new(version_string), AboveAll.new())
|
20
|
+
interval = interval.intersect(new_interval)
|
21
|
+
when "<="
|
22
|
+
new_interval = VersionInterval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_CLOSED, BelowAll.new(), VersionCut.new(version_string))
|
23
|
+
interval = interval.intersect(new_interval)
|
24
|
+
when "<"
|
25
|
+
new_interval = VersionInterval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_OPEN, BelowAll.new(), VersionCut.new(version_string))
|
26
|
+
interval = interval.intersect(new_interval)
|
27
|
+
when ">"
|
28
|
+
new_interval = VersionInterval.new(IntervalType::LEFT_OPEN | IntervalType::RIGHT_OPEN, VersionCut.new(version_string), AboveAll.new())
|
29
|
+
interval = interval.intersect(new_interval)
|
30
|
+
when "=", "=="
|
31
|
+
new_interval = VersionInterval.new(IntervalType::LEFT_CLOSED | IntervalType::RIGHT_CLOSED, VersionCut.new(version_string), VersionCut.new(version_string))
|
32
|
+
interval = interval.intersect(new_interval)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
interval
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'version_parser'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
# VersionRange is a utility class that helps managing consecutive version ranges automatically
|
7
|
+
# given that they are added in-order
|
8
|
+
# Note that join_if_possible should be only activated in case the ranges are added in consecutive order!!
|
9
|
+
class VersionRange
|
10
|
+
attr_reader :version_intervals, :join_if_possible
|
11
|
+
|
12
|
+
def initialize(join_if_possible = true)
|
13
|
+
@version_intervals = []
|
14
|
+
@version_interval_set = Set.new
|
15
|
+
@join_if_possible = join_if_possible
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_all(version_range)
|
19
|
+
version_range.version_intervals.each { |interval| add(interval) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(version_interval)
|
23
|
+
return if @version_interval_set.include?(version_interval)
|
24
|
+
|
25
|
+
if @join_if_possible
|
26
|
+
if @version_intervals.empty?
|
27
|
+
@version_intervals << version_interval
|
28
|
+
@version_interval_set.add(version_interval)
|
29
|
+
else
|
30
|
+
last = @version_intervals.last
|
31
|
+
# nothing to do
|
32
|
+
return if last.end_cut == version_interval.start_cut && last.end_cut.value == version_interval.start_cut.value
|
33
|
+
|
34
|
+
if last.joinable?(version_interval)
|
35
|
+
@version_intervals[@version_intervals.size - 1] = last.join(version_interval)
|
36
|
+
else
|
37
|
+
@version_intervals << version_interval
|
38
|
+
@version_interval_set.add(version_interval)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
else
|
42
|
+
@version_intervals << version_interval
|
43
|
+
@version_interval_set.add(version_interval)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def <<(item)
|
48
|
+
add(item)
|
49
|
+
end
|
50
|
+
|
51
|
+
def size
|
52
|
+
@version_intervals.size
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
@version_intervals.map(&:to_s).join(',')
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_description_s
|
60
|
+
@version_intervals.map(&:to_description_s).join(', ').capitalize
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_npm_s
|
64
|
+
@version_intervals.map(&:to_npm_s).join('||')
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_conan_s
|
68
|
+
to_npm_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_nuget_s
|
72
|
+
to_maven_s
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_maven_s
|
76
|
+
@version_intervals.map(&:to_maven_s).join(',')
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_gem_s
|
80
|
+
@version_intervals.map(&:to_gem_s).join('||')
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_pypi_s
|
84
|
+
@version_intervals.map(&:to_pypi_s).join('||')
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_go_s
|
88
|
+
@version_intervals.map(&:to_go_s).join('||')
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_packagist_s
|
92
|
+
@version_intervals.map(&:to_packagist_s).join('||')
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_version_s(package_type)
|
96
|
+
case package_type
|
97
|
+
when 'npm'
|
98
|
+
to_npm_s
|
99
|
+
when 'nuget'
|
100
|
+
to_nuget_s
|
101
|
+
when 'maven'
|
102
|
+
to_maven_s
|
103
|
+
when 'gem'
|
104
|
+
to_gem_s
|
105
|
+
when 'pypi'
|
106
|
+
to_pypi_s
|
107
|
+
when 'packagist'
|
108
|
+
to_packagist_s
|
109
|
+
when 'go'
|
110
|
+
to_go_s
|
111
|
+
when 'conan'
|
112
|
+
to_conan_s
|
113
|
+
else
|
114
|
+
''
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# inverts the given version interval -- note that this function amy return two version intervals
|
119
|
+
# e.g., (2,10], (12, 13], [15, +inf)
|
120
|
+
# 1) invert: (-inf, 2], (10, +inf), (-inf, 12], (13, +inf), (15)
|
121
|
+
# 2) collapse (-inf, 2], (10, 12], (13, 15)
|
122
|
+
#
|
123
|
+
# the collapsed inverted ranges can potentially contain fixed versions
|
124
|
+
def invert
|
125
|
+
inverted = @version_intervals.map(&:invert).flatten
|
126
|
+
version_intervals = collapse_intervals(inverted)
|
127
|
+
version_intervals_to_range(version_intervals)
|
128
|
+
end
|
129
|
+
|
130
|
+
def collapse
|
131
|
+
version_intervals = collapse_intervals(@version_intervals)
|
132
|
+
version_intervals_to_range(version_intervals)
|
133
|
+
end
|
134
|
+
|
135
|
+
def includes?(version_interval)
|
136
|
+
@version_interval_set.include?(version_interval)
|
137
|
+
end
|
138
|
+
|
139
|
+
def overlaps_with?(version_interval)
|
140
|
+
@version_interval_set.each do |interval|
|
141
|
+
return true unless interval.intersect(version_interval).instance_of? EmptyInterval
|
142
|
+
end
|
143
|
+
false
|
144
|
+
end
|
145
|
+
|
146
|
+
def first
|
147
|
+
@version_intervals.first
|
148
|
+
end
|
149
|
+
|
150
|
+
def empty?
|
151
|
+
@version_intervals.empty?
|
152
|
+
end
|
153
|
+
|
154
|
+
def any?
|
155
|
+
@version_intervals.any?
|
156
|
+
end
|
157
|
+
|
158
|
+
def universal?
|
159
|
+
@version_intervals.each do |version_interval|
|
160
|
+
return true if version_interval.universal?
|
161
|
+
end
|
162
|
+
false
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def version_intervals_to_range(version_intervals)
|
168
|
+
ret = VersionRange.new(false)
|
169
|
+
version_intervals.each do |version_interval|
|
170
|
+
ret << version_interval
|
171
|
+
end
|
172
|
+
ret
|
173
|
+
end
|
174
|
+
|
175
|
+
def collapse_intervals(version_intervals)
|
176
|
+
interval_cp = []
|
177
|
+
interval_cp += version_intervals
|
178
|
+
ret = [interval_cp.shift]
|
179
|
+
|
180
|
+
interval_cp.each do |item|
|
181
|
+
last = ret.last
|
182
|
+
if last.intersect(item).instance_of?(EmptyInterval)
|
183
|
+
ret << item
|
184
|
+
else
|
185
|
+
ret.pop
|
186
|
+
ret << last.collapse(item)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
ret
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../utils.rb'
|
4
|
+
|
5
|
+
# A prepropcessor -- convert different version strings to something that can be digested by the
|
6
|
+
# version parser
|
7
|
+
module VersionTranslator
|
8
|
+
def self.translate_npm(version_string)
|
9
|
+
version_string.split('||').map do |item|
|
10
|
+
add_missing_operator(single_space_after_operator(item.strip.gsub(/&&/, ' ')))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.translate_conan(version_string)
|
15
|
+
translate_npm(version_string)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.translate_go(version_string)
|
19
|
+
translate_gem(version_string)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.translate_gem(version_string)
|
23
|
+
version_string.split('||').map do |item|
|
24
|
+
add_missing_operator(single_space_after_operator(item.strip.gsub(/\s+/, ' ')))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.translate_packagist(version_string)
|
29
|
+
translate_pypi(version_string)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.translate_pypi(version_string)
|
33
|
+
version_string.split('||').map do |item|
|
34
|
+
add_missing_operator(single_space_after_operator(comma_to_space(item)))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.translate_nuget(version_string)
|
39
|
+
translate_maven(version_string)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.translate_maven(version_string)
|
43
|
+
lexing_maven_version_string(version_string).map { |item| translate_mvn_version_item(item) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.add_missing_operator(version_string)
|
47
|
+
starts_with_operator?(version_string) ? version_string : "=#{version_string}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.single_space_after_operator(version_string)
|
51
|
+
version_string.gsub(/([>=<]+) +/, '\1').gsub(/\s+/, ' ')
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.starts_with_operator?(version_item)
|
55
|
+
version_item.match(/^[=><]/) ? true : false
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.comma_to_space(version_string)
|
59
|
+
version_string.strip.gsub(/,/, ' ')
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.lexing_maven_version_string(version_string)
|
63
|
+
open = false
|
64
|
+
substring = ''
|
65
|
+
ret = []
|
66
|
+
version_string.each_char do |c|
|
67
|
+
case c
|
68
|
+
when '(', '['
|
69
|
+
if open
|
70
|
+
puts "malformed maven version string #{version_string}"
|
71
|
+
exit(-1)
|
72
|
+
else
|
73
|
+
unless substring.empty?
|
74
|
+
ret << substring
|
75
|
+
substring = ''
|
76
|
+
end
|
77
|
+
open = true
|
78
|
+
substring += c
|
79
|
+
end
|
80
|
+
when ')', ']'
|
81
|
+
if !open
|
82
|
+
puts "malformed maven version string #{version_string}"
|
83
|
+
exit(-1)
|
84
|
+
else
|
85
|
+
open = false
|
86
|
+
substring += c
|
87
|
+
ret << substring
|
88
|
+
substring = ''
|
89
|
+
end
|
90
|
+
when ','
|
91
|
+
substring += c if open
|
92
|
+
when ' '
|
93
|
+
# nothing to do
|
94
|
+
substring += ''
|
95
|
+
else
|
96
|
+
substring += c
|
97
|
+
end
|
98
|
+
end
|
99
|
+
if open
|
100
|
+
puts "malformed maven version string #{version_string}"
|
101
|
+
exit(-1)
|
102
|
+
end
|
103
|
+
ret << substring unless substring.empty?
|
104
|
+
ret
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.parenthesized?(version_item)
|
108
|
+
version_item.match(/^[\(\[]/) && version_item.match(/[\]\)]$/) ? true : false
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.translate_mvn_version_item(version_item)
|
112
|
+
content = ''
|
113
|
+
parens_pattern = ''
|
114
|
+
if parenthesized?(version_item)
|
115
|
+
content = version_item[1, version_item.size - 2]
|
116
|
+
parens_pattern = version_item[0] + version_item[version_item.size - 1]
|
117
|
+
# special case -- unversal version range
|
118
|
+
return '=*' if content.strip == ','
|
119
|
+
else
|
120
|
+
# according to the doc, if there is a plain version string in maven, it means 'starting from version x'
|
121
|
+
# https://docs.oracle.com/middleware/1212/core/MAVEN/maven_version.htm#MAVEN8903
|
122
|
+
content = "#{version_item},"
|
123
|
+
parens_pattern = '[)'
|
124
|
+
end
|
125
|
+
|
126
|
+
args = content.split(',')
|
127
|
+
first_non_empty_arg = args.find(&:range_present?)
|
128
|
+
|
129
|
+
if content.start_with?(',')
|
130
|
+
# {,y}
|
131
|
+
case parens_pattern
|
132
|
+
when '[]'
|
133
|
+
"<=#{first_non_empty_arg}"
|
134
|
+
when '()'
|
135
|
+
"<#{first_non_empty_arg}"
|
136
|
+
when '[)'
|
137
|
+
"<#{first_non_empty_arg}"
|
138
|
+
else
|
139
|
+
# par_pattern == "(]"
|
140
|
+
"<=#{first_non_empty_arg}"
|
141
|
+
end
|
142
|
+
elsif content.end_with?(',')
|
143
|
+
# {x,}
|
144
|
+
case parens_pattern
|
145
|
+
when '[]'
|
146
|
+
">=#{first_non_empty_arg}"
|
147
|
+
when '()'
|
148
|
+
">#{first_non_empty_arg}"
|
149
|
+
when '[)'
|
150
|
+
">=#{first_non_empty_arg}"
|
151
|
+
else
|
152
|
+
# par_pattern == "(]"
|
153
|
+
">#{first_non_empty_arg}"
|
154
|
+
end
|
155
|
+
elsif content[','].nil?
|
156
|
+
# [x,x]
|
157
|
+
"=#{content}"
|
158
|
+
else
|
159
|
+
case parens_pattern
|
160
|
+
when '[]'
|
161
|
+
">=#{args[0]} <=#{args[1]}"
|
162
|
+
when '()'
|
163
|
+
">#{args[0]} <#{args[1]}"
|
164
|
+
when '[)'
|
165
|
+
">=#{args[0]} <#{args[1]}"
|
166
|
+
else
|
167
|
+
# par_pattern == "(]"
|
168
|
+
">#{args[0]} <=#{args[1]}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|