1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
# frozen_string_literal: true
module Bundler::PubGrub
class VersionUnion
attr_reader :ranges
def self.normalize_ranges(ranges)
ranges = ranges.flat_map do |range|
range.ranges
end
ranges.reject!(&:empty?)
return [] if ranges.empty?
mins, ranges = ranges.partition { |r| !r.min }
original_ranges = mins + ranges.sort_by { |r| [r.min, r.include_min ? 0 : 1] }
ranges = [original_ranges.shift]
original_ranges.each do |range|
if ranges.last.contiguous_to?(range)
ranges << ranges.pop.span(range)
else
ranges << range
end
end
ranges
end
def self.union(ranges, normalize: true)
ranges = normalize_ranges(ranges) if normalize
if ranges.size == 0
VersionRange.empty
elsif ranges.size == 1
ranges[0]
else
new(ranges)
end
end
def initialize(ranges)
raise ArgumentError unless ranges.all? { |r| r.instance_of?(VersionRange) }
@ranges = ranges
end
def hash
ranges.hash
end
def eql?(other)
ranges.eql?(other.ranges)
end
def include?(version)
!!ranges.bsearch {|r| r.compare_version(version) }
end
def select_versions(all_versions)
versions = []
ranges.inject(all_versions) do |acc, range|
_, matching, higher = range.partition_versions(acc)
versions.concat matching
higher
end
versions
end
def intersects?(other)
my_ranges = ranges.dup
other_ranges = other.ranges.dup
my_range = my_ranges.shift
other_range = other_ranges.shift
while my_range && other_range
if my_range.intersects?(other_range)
return true
end
if !my_range.max || other_range.empty? || (other_range.max && other_range.max < my_range.max)
other_range = other_ranges.shift
else
my_range = my_ranges.shift
end
end
end
alias_method :allows_any?, :intersects?
def allows_all?(other)
my_ranges = ranges.dup
my_range = my_ranges.shift
other.ranges.all? do |other_range|
while my_range
break if my_range.allows_all?(other_range)
my_range = my_ranges.shift
end
!!my_range
end
end
def empty?
false
end
def any?
false
end
def intersect(other)
my_ranges = ranges.dup
other_ranges = other.ranges.dup
new_ranges = []
my_range = my_ranges.shift
other_range = other_ranges.shift
while my_range && other_range
new_ranges << my_range.intersect(other_range)
if !my_range.max || other_range.empty? || (other_range.max && other_range.max < my_range.max)
other_range = other_ranges.shift
else
my_range = my_ranges.shift
end
end
new_ranges.reject!(&:empty?)
VersionUnion.union(new_ranges, normalize: false)
end
def upper_invert
ranges.last.upper_invert
end
def invert
ranges.map(&:invert).inject(:intersect)
end
def union(other)
VersionUnion.union([self, other])
end
def to_s
output = []
ranges = self.ranges.dup
while !ranges.empty?
ne = []
range = ranges.shift
while !ranges.empty? && ranges[0].min.to_s == range.max.to_s
ne << range.max
range = range.span(ranges.shift)
end
ne.map! {|x| "!= #{x}" }
if ne.empty?
output << range.to_s
elsif range.any?
output << ne.join(', ')
else
output << "#{range}, #{ne.join(', ')}"
end
end
output.join(" OR ")
end
def inspect
"#<#{self.class} #{to_s}>"
end
def ==(other)
self.class == other.class &&
self.ranges == other.ranges
end
end
end
|