if_test_foo 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SemverDialects
4
+ VERSION = '1.3.0'
5
+ end