semver_dialects 1.0.1
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 +7 -0
- data/lib/semver_dialects.rb +56 -0
- 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/utils.rb +41 -0
- metadata +163 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
require_relative "semantic_version"
|
2
|
+
|
3
|
+
class VersionCut
|
4
|
+
attr_accessor :semver, :value
|
5
|
+
|
6
|
+
def initialize(value, segments = nil)
|
7
|
+
@value = value.to_s
|
8
|
+
if segments.nil?
|
9
|
+
@semver = SemanticVersion.new(@value)
|
10
|
+
else
|
11
|
+
@semver = SemanticVersion.new(@value, segments)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@value.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def diff(other_cut, abs = true)
|
20
|
+
if other_cut.instance_of?(BelowAll)
|
21
|
+
AboveAll.new()
|
22
|
+
elsif other_cut.instance_of?(AboveAll)
|
23
|
+
BelowAll.new()
|
24
|
+
else
|
25
|
+
diff = self.semver.diff(other_cut.semver, abs)
|
26
|
+
if diff.nil?
|
27
|
+
EmptyInterval.new()
|
28
|
+
else
|
29
|
+
VersionCut.new(diff.join("."), diff)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def is_successor_of?(other_cut)
|
35
|
+
return true if other_cut.instance_of?(BelowAll)
|
36
|
+
return false if other_cut.instance_of?(AboveAll)
|
37
|
+
|
38
|
+
self.semver.is_successor_of?(other_cut.semver)
|
39
|
+
end
|
40
|
+
|
41
|
+
def <(other_cut)
|
42
|
+
other_cut.instance_of?(BelowAll) ? false : other_cut.instance_of?(AboveAll) ? true : @semver < other_cut.semver
|
43
|
+
end
|
44
|
+
|
45
|
+
def >(other_cut)
|
46
|
+
other_cut.instance_of?(BelowAll) ? true : other_cut.instance_of?(AboveAll) ? false : @semver > other_cut.semver
|
47
|
+
end
|
48
|
+
|
49
|
+
def <=(other_cut)
|
50
|
+
self < other_cut || self == other_cut
|
51
|
+
end
|
52
|
+
|
53
|
+
def >=(other_cut)
|
54
|
+
self > other_cut || self == other_cut
|
55
|
+
end
|
56
|
+
|
57
|
+
def ==(other_cut)
|
58
|
+
# self cannot be BelowAll or AboveAll
|
59
|
+
if other_cut.instance_of?(BelowAll) || other_cut.instance_of?(AboveAll)
|
60
|
+
false
|
61
|
+
else
|
62
|
+
@semver == other_cut.semver
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def !=(other_cut)
|
67
|
+
# self cannot be BelowAll or AboveAll
|
68
|
+
if other_cut.instance_of?(BelowAll) || other_cut.instance_of?(AboveAll)
|
69
|
+
false
|
70
|
+
else
|
71
|
+
@semver != other_cut.semver
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def is_initial_version?
|
76
|
+
@semver.is_zero?
|
77
|
+
end
|
78
|
+
|
79
|
+
def major
|
80
|
+
@semver.major
|
81
|
+
end
|
82
|
+
|
83
|
+
def minor
|
84
|
+
@semver.minor
|
85
|
+
end
|
86
|
+
|
87
|
+
def patch
|
88
|
+
@semver.patch
|
89
|
+
end
|
90
|
+
|
91
|
+
def cross_total
|
92
|
+
[minor, major, patch].reject { |i| i.empty? }.map { |i| i.to_i }.inject(0) { |sum, x| sum + x }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class BelowAll < VersionCut
|
97
|
+
def initialize()
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_s
|
101
|
+
"-inf"
|
102
|
+
end
|
103
|
+
|
104
|
+
def is_initial_version?
|
105
|
+
false
|
106
|
+
end
|
107
|
+
|
108
|
+
def <(other_cut)
|
109
|
+
other_cut.instance_of?(BelowAll) ? false : true
|
110
|
+
end
|
111
|
+
|
112
|
+
def >(other_cut)
|
113
|
+
false
|
114
|
+
end
|
115
|
+
|
116
|
+
def <=(other_cut)
|
117
|
+
self < other_cut || self == other_cut
|
118
|
+
end
|
119
|
+
|
120
|
+
def >=(other_cut)
|
121
|
+
self > other_cut || self == other_cut
|
122
|
+
end
|
123
|
+
|
124
|
+
def ==(other_cut)
|
125
|
+
(other_cut.instance_of? BelowAll) ? true : false
|
126
|
+
end
|
127
|
+
|
128
|
+
def !=(other_cut)
|
129
|
+
!(self == other_cut)
|
130
|
+
end
|
131
|
+
|
132
|
+
def is_successor_of?(other_cut)
|
133
|
+
false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class AboveAll < VersionCut
|
138
|
+
def initialize()
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_s
|
142
|
+
"+inf"
|
143
|
+
end
|
144
|
+
|
145
|
+
def is_initial_version?
|
146
|
+
false
|
147
|
+
end
|
148
|
+
|
149
|
+
def <(other_cut)
|
150
|
+
false
|
151
|
+
end
|
152
|
+
|
153
|
+
def >(other_cut)
|
154
|
+
other_cut.instance_of?(AboveAll) ? false : true
|
155
|
+
end
|
156
|
+
|
157
|
+
def <=(other_cut)
|
158
|
+
self < other_cut || self == other_cut
|
159
|
+
end
|
160
|
+
|
161
|
+
def >=(other_cut)
|
162
|
+
self > other_cut || self == other_cut
|
163
|
+
end
|
164
|
+
|
165
|
+
def ==(other_cut)
|
166
|
+
(other_cut.instance_of? AboveAll) ? true : false
|
167
|
+
end
|
168
|
+
|
169
|
+
def !=(other_cut)
|
170
|
+
!(self == other_cut)
|
171
|
+
end
|
172
|
+
|
173
|
+
def is_successor_of?(other_cut)
|
174
|
+
!other_cut.instance_of?(AboveAll)
|
175
|
+
end
|
176
|
+
end
|
@@ -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
|