semver_dialects 2.0.2 → 3.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/lib/semver_dialects/base_version.rb +79 -0
- data/lib/semver_dialects/boundary.rb +142 -0
- data/lib/semver_dialects/commands/check_version.rb +2 -5
- data/lib/semver_dialects/interval.rb +256 -0
- data/lib/semver_dialects/interval_parser.rb +60 -0
- data/lib/semver_dialects/interval_set.rb +121 -0
- data/lib/semver_dialects/interval_set_parser.rb +205 -0
- data/lib/semver_dialects/maven.rb +197 -0
- data/lib/semver_dialects/semantic_version.rb +294 -0
- data/lib/semver_dialects/semver2.rb +159 -0
- data/lib/semver_dialects/version.rb +1 -1
- data/lib/semver_dialects.rb +152 -63
- metadata +57 -12
- data/lib/semver_dialects/semantic_version/semantic_version.rb +0 -337
- data/lib/semver_dialects/semantic_version/version_cut.rb +0 -176
- data/lib/semver_dialects/semantic_version/version_interval.rb +0 -288
- data/lib/semver_dialects/semantic_version/version_parser.rb +0 -40
- data/lib/semver_dialects/semantic_version/version_range.rb +0 -191
- data/lib/semver_dialects/semantic_version/version_translator.rb +0 -172
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# IntervalSetParser parses a string that represents an interval set
|
4
|
+
# in a syntax that's specific to a package type.
|
5
|
+
module SemverDialects
|
6
|
+
module IntervalSetParser
|
7
|
+
# parse parses a string and returns an IntervalSet.
|
8
|
+
# The string is expected to be in a syntax that's specific the given package type.
|
9
|
+
def self.parse(typ, interval_set_string)
|
10
|
+
IntervalSet.new.tap do |set|
|
11
|
+
translate(typ, interval_set_string).each do |interval_str|
|
12
|
+
set << IntervalParser.parse(typ, interval_str)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.translate(typ, interval_set_string)
|
18
|
+
case typ
|
19
|
+
when 'maven'
|
20
|
+
translate_maven(interval_set_string)
|
21
|
+
when 'npm'
|
22
|
+
translate_npm(interval_set_string)
|
23
|
+
when 'conan'
|
24
|
+
translate_conan(interval_set_string)
|
25
|
+
when 'nuget'
|
26
|
+
translate_nuget(interval_set_string)
|
27
|
+
when 'go'
|
28
|
+
translate_go(interval_set_string)
|
29
|
+
when 'gem'
|
30
|
+
translate_gem(interval_set_string)
|
31
|
+
when 'pypi'
|
32
|
+
translate_pypi(interval_set_string)
|
33
|
+
when 'packagist'
|
34
|
+
translate_packagist(interval_set_string)
|
35
|
+
else
|
36
|
+
raise UnsupportedPackageTypeError, typ
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.translate_npm(interval_set_string)
|
41
|
+
interval_set_string.split('||').map do |item|
|
42
|
+
add_missing_operator(single_space_after_operator(item.strip.gsub(/&&/, ' ')))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.translate_conan(interval_set_string)
|
47
|
+
translate_npm(interval_set_string)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.translate_go(interval_set_string)
|
51
|
+
translate_gem(interval_set_string)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.translate_gem(interval_set_string)
|
55
|
+
interval_set_string.split('||').map do |item|
|
56
|
+
add_missing_operator(single_space_after_operator(item.strip.gsub(/\s+/, ' ')))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.translate_packagist(interval_set_string)
|
61
|
+
translate_pypi(interval_set_string)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.translate_pypi(interval_set_string)
|
65
|
+
interval_set_string.split('||').map do |item|
|
66
|
+
add_missing_operator(single_space_after_operator(comma_to_space(item)))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.translate_nuget(interval_set_string)
|
71
|
+
translate_maven(interval_set_string)
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.translate_maven(interval_set_string)
|
75
|
+
lexing_maven_interval_set_string(interval_set_string).map { |item| translate_mvn_version_item(item) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.add_missing_operator(interval_set_string)
|
79
|
+
starts_with_operator?(interval_set_string) ? interval_set_string : "=#{interval_set_string}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.single_space_after_operator(interval_set_string)
|
83
|
+
interval_set_string.gsub(/([>=<]+) +/, '\1').gsub(/\s+/, ' ')
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.starts_with_operator?(version_item)
|
87
|
+
version_item.match(/^[=><]/) ? true : false
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.comma_to_space(interval_set_string)
|
91
|
+
interval_set_string.strip.gsub(/,/, ' ')
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.lexing_maven_interval_set_string(interval_set_string)
|
95
|
+
open = false
|
96
|
+
substring = ''
|
97
|
+
ret = []
|
98
|
+
interval_set_string.each_char do |c|
|
99
|
+
case c
|
100
|
+
when '(', '['
|
101
|
+
if open
|
102
|
+
puts "malformed maven version string #{interval_set_string}"
|
103
|
+
exit(-1)
|
104
|
+
else
|
105
|
+
unless substring.empty?
|
106
|
+
ret << substring
|
107
|
+
substring = ''
|
108
|
+
end
|
109
|
+
open = true
|
110
|
+
substring += c
|
111
|
+
end
|
112
|
+
when ')', ']'
|
113
|
+
if !open
|
114
|
+
puts "malformed maven version string #{interval_set_string}"
|
115
|
+
exit(-1)
|
116
|
+
else
|
117
|
+
open = false
|
118
|
+
substring += c
|
119
|
+
ret << substring
|
120
|
+
substring = ''
|
121
|
+
end
|
122
|
+
when ','
|
123
|
+
substring += c if open
|
124
|
+
when ' '
|
125
|
+
# nothing to do
|
126
|
+
substring += ''
|
127
|
+
else
|
128
|
+
substring += c
|
129
|
+
end
|
130
|
+
end
|
131
|
+
if open
|
132
|
+
puts "malformed maven version string #{interval_set_string}"
|
133
|
+
exit(-1)
|
134
|
+
end
|
135
|
+
ret << substring unless substring.empty?
|
136
|
+
ret
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.parenthesized?(version_item)
|
140
|
+
version_item.match(/^[(\[]/) && version_item.match(/[\])]$/)
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.translate_mvn_version_item(version_item)
|
144
|
+
content = ''
|
145
|
+
parens_pattern = ''
|
146
|
+
if parenthesized?(version_item)
|
147
|
+
content = version_item[1, version_item.size - 2]
|
148
|
+
parens_pattern = version_item[0] + version_item[version_item.size - 1]
|
149
|
+
# special case -- unversal version range
|
150
|
+
return '=*' if content.strip == ','
|
151
|
+
else
|
152
|
+
# according to the doc, if there is a plain version string in maven, it means 'starting from version x'
|
153
|
+
# https://docs.oracle.com/middleware/1212/core/MAVEN/maven_version.htm#MAVEN8903
|
154
|
+
content = "#{version_item},"
|
155
|
+
parens_pattern = '[)'
|
156
|
+
end
|
157
|
+
|
158
|
+
args = content.split(',')
|
159
|
+
first_non_empty_arg = args.find(&:range_present?)
|
160
|
+
|
161
|
+
if content.start_with?(',')
|
162
|
+
# {,y}
|
163
|
+
case parens_pattern
|
164
|
+
when '[]'
|
165
|
+
"<=#{first_non_empty_arg}"
|
166
|
+
when '()'
|
167
|
+
"<#{first_non_empty_arg}"
|
168
|
+
when '[)'
|
169
|
+
"<#{first_non_empty_arg}"
|
170
|
+
else
|
171
|
+
# par_pattern == "(]"
|
172
|
+
"<=#{first_non_empty_arg}"
|
173
|
+
end
|
174
|
+
elsif content.end_with?(',')
|
175
|
+
# {x,}
|
176
|
+
case parens_pattern
|
177
|
+
when '[]'
|
178
|
+
">=#{first_non_empty_arg}"
|
179
|
+
when '()'
|
180
|
+
">#{first_non_empty_arg}"
|
181
|
+
when '[)'
|
182
|
+
">=#{first_non_empty_arg}"
|
183
|
+
else
|
184
|
+
# par_pattern == "(]"
|
185
|
+
">#{first_non_empty_arg}"
|
186
|
+
end
|
187
|
+
elsif content[','].nil?
|
188
|
+
# [x,x]
|
189
|
+
"=#{content}"
|
190
|
+
else
|
191
|
+
case parens_pattern
|
192
|
+
when '[]'
|
193
|
+
">=#{args[0]} <=#{args[1]}"
|
194
|
+
when '()'
|
195
|
+
">#{args[0]} <#{args[1]}"
|
196
|
+
when '[)'
|
197
|
+
">=#{args[0]} <#{args[1]}"
|
198
|
+
else
|
199
|
+
# par_pattern == "(]"
|
200
|
+
">#{args[0]} <=#{args[1]}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'strscan'
|
4
|
+
|
5
|
+
module SemverDialects
|
6
|
+
module Maven
|
7
|
+
ALPHA = -5
|
8
|
+
BETA = -4
|
9
|
+
MILESTONE = -3
|
10
|
+
RC = -2
|
11
|
+
SNAPSHOT = -1
|
12
|
+
SP = 'sp'
|
13
|
+
|
14
|
+
class Version < BaseVersion
|
15
|
+
attr_accessor :addition
|
16
|
+
|
17
|
+
# Return an array similar to the one Maven generates when parsing versions.
|
18
|
+
#
|
19
|
+
# $ java -jar ${MAVEN_HOME}/lib/maven-artifact-3.9.6.jar 1a1
|
20
|
+
# Display parameters as parsed by Maven (in canonical form and as a list of tokens) and comparison result:
|
21
|
+
# 1. 1a1 -> 1-alpha-1; tokens: [1, [alpha, [1]]]
|
22
|
+
#
|
23
|
+
def to_a
|
24
|
+
return tokens if addition.nil?
|
25
|
+
|
26
|
+
tokens.clone.append(addition.to_a)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s(as_addition = false)
|
30
|
+
s = ''
|
31
|
+
if tokens.any?
|
32
|
+
s += '-' if as_addition
|
33
|
+
s += tokens.map do |token|
|
34
|
+
case token
|
35
|
+
when String
|
36
|
+
token
|
37
|
+
when Integer
|
38
|
+
case token
|
39
|
+
when ALPHA
|
40
|
+
'alpha'
|
41
|
+
when BETA
|
42
|
+
'beta'
|
43
|
+
when MILESTONE
|
44
|
+
'milestone'
|
45
|
+
when RC
|
46
|
+
'rc'
|
47
|
+
when SNAPSHOT
|
48
|
+
'snapshot'
|
49
|
+
else
|
50
|
+
token.to_s
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end.join('.')
|
54
|
+
end
|
55
|
+
s += addition.to_s(true) if addition
|
56
|
+
s
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Compare tokens as specified in https://maven.apache.org/pom.html#version-order-specification.
|
62
|
+
# Negative integers are alpha, beta, milestone, rc, and snapshot qualifiers.
|
63
|
+
# Special qualifier "sp" is right after GA and before any lexical or numeric token.
|
64
|
+
# Strings should be converted to lower case before being compared by this method.
|
65
|
+
# 1-a0 == 1-alpha < 1-0 == 1 == 1final == 1 ga < 1sp < 1-a < 1-1
|
66
|
+
def compare_token_pair(a = 0, b = 0)
|
67
|
+
a ||= 0
|
68
|
+
b ||= 0
|
69
|
+
|
70
|
+
if a.is_a?(Integer) && b.is_a?(String)
|
71
|
+
return a <= 0 ? -1 : 1
|
72
|
+
end
|
73
|
+
|
74
|
+
if a.is_a?(String) && b.is_a?(Integer)
|
75
|
+
return b <= 0 ? 1 : -1
|
76
|
+
end
|
77
|
+
|
78
|
+
return -1 if a == SP && b.is_a?(String) && b != SP
|
79
|
+
|
80
|
+
return 1 if b == SP && a.is_a?(String) && a != SP
|
81
|
+
|
82
|
+
# Identifiers have both the same type.
|
83
|
+
# This returns nil if the identifiers can't be compared.
|
84
|
+
a <=> b
|
85
|
+
end
|
86
|
+
|
87
|
+
def empty_addition
|
88
|
+
Version.new([])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class VersionParser
|
93
|
+
def self.parse(input)
|
94
|
+
new(input).parse
|
95
|
+
end
|
96
|
+
|
97
|
+
attr_reader :input
|
98
|
+
|
99
|
+
def initialize(input)
|
100
|
+
@input = input
|
101
|
+
end
|
102
|
+
|
103
|
+
def parse
|
104
|
+
@scanner = StringScanner.new(input.downcase)
|
105
|
+
@version = Version.new([])
|
106
|
+
@result = @version
|
107
|
+
parse_version(false)
|
108
|
+
result
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
attr_reader :scanner, :version, :result
|
114
|
+
|
115
|
+
# Parse a version and all its additions recursively.
|
116
|
+
# It automatically creates a new partition for numbers
|
117
|
+
# if number_begins_partition is true.
|
118
|
+
def parse_version(number_begins_partition)
|
119
|
+
# skip leading v if any
|
120
|
+
scanner.skip(/v/)
|
121
|
+
|
122
|
+
until scanner.eos?
|
123
|
+
if (s = scanner.scan(/\d+/))
|
124
|
+
if number_begins_partition
|
125
|
+
parse_addition(s.to_i)
|
126
|
+
else
|
127
|
+
version.tokens << s.to_i
|
128
|
+
end
|
129
|
+
|
130
|
+
elsif (s = scanner.match?(/a\d+/))
|
131
|
+
# aN is equivalent to alpha-N
|
132
|
+
scanner.skip('a')
|
133
|
+
parse_addition(ALPHA)
|
134
|
+
|
135
|
+
elsif (s = scanner.match?(/b\d+/))
|
136
|
+
# bN is equivalent to beta-N
|
137
|
+
scanner.skip('b')
|
138
|
+
parse_addition(BETA)
|
139
|
+
|
140
|
+
elsif (s = scanner.match?(/m\d+/))
|
141
|
+
# mN is equivalent to milestone-N
|
142
|
+
scanner.skip('m')
|
143
|
+
parse_addition(MILESTONE)
|
144
|
+
|
145
|
+
elsif (s = scanner.scan(/(alpha|beta|milestone|rc|cr|sp|ga|final|release|snapshot)[a-z]+/))
|
146
|
+
# process "alpha" and others as normal lexical tokens if they're followed by a letter
|
147
|
+
parse_addition(s)
|
148
|
+
|
149
|
+
elsif (s = scanner.scan('alpha'))
|
150
|
+
# handle alphaN, alpha-X, alpha.X, or ending alpha
|
151
|
+
parse_addition(ALPHA)
|
152
|
+
|
153
|
+
elsif (s = scanner.scan('beta'))
|
154
|
+
parse_addition(BETA)
|
155
|
+
|
156
|
+
elsif (s = scanner.scan('milestone'))
|
157
|
+
parse_addition(MILESTONE)
|
158
|
+
|
159
|
+
elsif (s = scanner.scan(/(rc|cr)/))
|
160
|
+
parse_addition(RC)
|
161
|
+
|
162
|
+
elsif (s = scanner.scan('snapshot'))
|
163
|
+
parse_addition(SNAPSHOT)
|
164
|
+
|
165
|
+
elsif (s = scanner.scan(/ga|final|release/))
|
166
|
+
parse_addition
|
167
|
+
|
168
|
+
elsif (s = scanner.scan('sp'))
|
169
|
+
parse_addition(SP)
|
170
|
+
|
171
|
+
elsif (s = scanner.scan(/[a-z]+/))
|
172
|
+
parse_addition(s)
|
173
|
+
|
174
|
+
elsif (s = scanner.scan('.'))
|
175
|
+
number_begins_partition = false
|
176
|
+
|
177
|
+
elsif (s = scanner.scan('-'))
|
178
|
+
number_begins_partition = true
|
179
|
+
|
180
|
+
else
|
181
|
+
raise IncompleteScanError, scanner.rest
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Create an addition for the current version, make it the current version, and parse it.
|
187
|
+
# Numbers start a new partition.
|
188
|
+
def parse_addition(token = nil)
|
189
|
+
version.addition = Version.new([token].compact)
|
190
|
+
@version = version.addition
|
191
|
+
|
192
|
+
scanner.skip(/-+/)
|
193
|
+
parse_version(true)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,294 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../utils'
|
4
|
+
|
5
|
+
# SemanticVersion represents a Semver 2 version.
|
6
|
+
# Comparison rules are the ones of Semver 2.
|
7
|
+
module SemverDialects
|
8
|
+
class SemanticVersion
|
9
|
+
attr_reader :version_string, :prefix_segments, :suffix_segments, :segments
|
10
|
+
|
11
|
+
# String to build a regexp that matches a version.
|
12
|
+
#
|
13
|
+
# A version might start with a leading "v", then it must have a digit,
|
14
|
+
# then it might have any sequence made of alphanumerical characters,
|
15
|
+
# underscores, dots, dashes, and wildcards.
|
16
|
+
VERSION_PATTERN = 'v?[0-9][a-zA-Z0-9_.*+-]*'
|
17
|
+
|
18
|
+
# Regexp for a string that only contains a single version string.
|
19
|
+
VERSION_ONLY_REGEXP = Regexp.new("\\A#{VERSION_PATTERN}\\z").freeze
|
20
|
+
|
21
|
+
def initialize(version_string)
|
22
|
+
raise InvalidVersionError, version_string unless VERSION_ONLY_REGEXP.match version_string
|
23
|
+
|
24
|
+
@version_string = version_string
|
25
|
+
@prefix_segments = []
|
26
|
+
@suffix_segments = []
|
27
|
+
version, = version_string.delete_prefix('v').split('+')
|
28
|
+
@segments = split_version_string!(version)
|
29
|
+
end
|
30
|
+
|
31
|
+
def split_version_string!(version_string)
|
32
|
+
delim_pattern = /[.-]/
|
33
|
+
split_array = version_string.split(delim_pattern).map do |grp|
|
34
|
+
grp.split(/(\d+)/).reject { |cell| cell.nil? || cell.empty? }
|
35
|
+
end.flatten
|
36
|
+
|
37
|
+
# go as far to the right as possible considering numbers and placeholders
|
38
|
+
prefix_delimiter = 0
|
39
|
+
(0..split_array.size - 1).each do |i|
|
40
|
+
break unless split_array[i].number? || split_array[i] == 'X' || split_array[i] == 'x'
|
41
|
+
|
42
|
+
prefix_delimiter = i
|
43
|
+
end
|
44
|
+
|
45
|
+
# remove redundant trailing zeros
|
46
|
+
prefix_delimiter.downto(0).each do |i|
|
47
|
+
break unless split_array[i] == '0'
|
48
|
+
|
49
|
+
split_array.delete_at(i)
|
50
|
+
prefix_delimiter -= 1
|
51
|
+
end
|
52
|
+
|
53
|
+
unless prefix_delimiter < 0
|
54
|
+
@prefix_segments = split_array[0..prefix_delimiter].map do |group_string|
|
55
|
+
SemanticVersionSegment.new(group_string)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
if split_array.size - 1 >= prefix_delimiter + 1
|
59
|
+
@suffix_segments = split_array[prefix_delimiter + 1, split_array.size].map do |group_string|
|
60
|
+
SemanticVersionSegment.new(group_string)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
@prefix_segments.clone.concat(@suffix_segments)
|
65
|
+
end
|
66
|
+
|
67
|
+
def _get_equalized_arrays_for(array_a, array_b)
|
68
|
+
first_array = array_a.clone
|
69
|
+
second_array = array_b.clone
|
70
|
+
if first_array.size < second_array.size
|
71
|
+
(second_array.size - first_array.size).times do
|
72
|
+
first_array << SemanticVersionSegment.new('0')
|
73
|
+
end
|
74
|
+
elsif first_array.size > second_array.size
|
75
|
+
(first_array.size - second_array.size).times do
|
76
|
+
second_array << SemanticVersionSegment.new('0')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
[first_array, second_array]
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_equalized_arrays_for(semver_a, semver_b)
|
83
|
+
first_array_prefix = semver_a.prefix_segments.clone
|
84
|
+
second_array_prefix = semver_b.prefix_segments.clone
|
85
|
+
first_array_suffix = semver_a.suffix_segments.clone
|
86
|
+
second_array_suffix = semver_b.suffix_segments.clone
|
87
|
+
first_array_prefix, second_array_prefix = _get_equalized_arrays_for(first_array_prefix, second_array_prefix)
|
88
|
+
first_array_suffix, second_array_suffix = _get_equalized_arrays_for(first_array_suffix, second_array_suffix)
|
89
|
+
[first_array_prefix.concat(first_array_suffix), second_array_prefix.concat(second_array_suffix)]
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_equalized_prefix_arrays_for(semver_a, semver_b)
|
93
|
+
first_array_prefix = semver_a.prefix_segments.clone
|
94
|
+
second_array_prefix = semver_b.prefix_segments.clone
|
95
|
+
first_array_prefix, second_array_prefix = _get_equalized_arrays_for(first_array_prefix, second_array_prefix)
|
96
|
+
[first_array_prefix, second_array_prefix]
|
97
|
+
end
|
98
|
+
|
99
|
+
def <(other)
|
100
|
+
self_array, other_array = get_equalized_arrays_for(self, other)
|
101
|
+
(0..self_array.size - 1).each do |i|
|
102
|
+
if self_array[i] < other_array[i]
|
103
|
+
return true
|
104
|
+
elsif self_array[i] > other_array[i]
|
105
|
+
return false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
false
|
109
|
+
end
|
110
|
+
|
111
|
+
def is_zero?
|
112
|
+
@prefix_segments.empty? || @prefix_segments.all?(&:is_zero?)
|
113
|
+
end
|
114
|
+
|
115
|
+
def pre_release?
|
116
|
+
@suffix_segments.any?(&:is_pre_release)
|
117
|
+
end
|
118
|
+
|
119
|
+
def post_release?
|
120
|
+
@suffix_segments.any?(&:is_post_release)
|
121
|
+
end
|
122
|
+
|
123
|
+
def >(other)
|
124
|
+
self_array, other_array = get_equalized_arrays_for(self, other)
|
125
|
+
(0..self_array.size - 1).each do |i|
|
126
|
+
if self_array[i] > other_array[i]
|
127
|
+
return true
|
128
|
+
elsif self_array[i] < other_array[i]
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
false
|
133
|
+
end
|
134
|
+
|
135
|
+
def >=(other)
|
136
|
+
self == other || self > other || self == other
|
137
|
+
end
|
138
|
+
|
139
|
+
def <=(other)
|
140
|
+
self == other || self < other
|
141
|
+
end
|
142
|
+
|
143
|
+
def ==(other)
|
144
|
+
segments_a = []
|
145
|
+
segments_b = []
|
146
|
+
|
147
|
+
segments_a += other.segments
|
148
|
+
segments_b += @segments
|
149
|
+
|
150
|
+
if other.segments.size < @segments.size
|
151
|
+
(@segments.size - other.segments.size).times { |_| segments_a << SemanticVersionSegment.new('0') }
|
152
|
+
elsif other.segments.size > @segments.size
|
153
|
+
(other.segments.size - @segments.size).times { |_| segments_b << SemanticVersionSegment.new('0') }
|
154
|
+
end
|
155
|
+
|
156
|
+
(0..segments_a.size - 1).each do |i|
|
157
|
+
return false if segments_a[i] != segments_b[i]
|
158
|
+
end
|
159
|
+
true
|
160
|
+
end
|
161
|
+
|
162
|
+
def !=(other)
|
163
|
+
!(self == other)
|
164
|
+
end
|
165
|
+
|
166
|
+
def to_normalized_s
|
167
|
+
@segments.map(&:to_normalized_s).join(':')
|
168
|
+
end
|
169
|
+
|
170
|
+
def to_s
|
171
|
+
@version_string
|
172
|
+
end
|
173
|
+
|
174
|
+
def minor
|
175
|
+
@prefix_segments.size >= 1 ? @prefix_segments[1].to_s : '0'
|
176
|
+
end
|
177
|
+
|
178
|
+
def major
|
179
|
+
@prefix_segments.size >= 2 ? @prefix_segments[0].to_s : '0'
|
180
|
+
end
|
181
|
+
|
182
|
+
def patch
|
183
|
+
@prefix_segments.size >= 3 ? @prefix_segments[2].to_s : '0'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class SemanticVersionSegment
|
188
|
+
attr_accessor :normalized_group_string, :original_group_string, :is_post_release, :is_pre_release
|
189
|
+
|
190
|
+
@@group_suffixes = {
|
191
|
+
# pre-releases
|
192
|
+
'PRE' => -16,
|
193
|
+
'PREVIEW' => -16,
|
194
|
+
'DEV' => -15,
|
195
|
+
'A' => -14,
|
196
|
+
'ALPHA' => -13,
|
197
|
+
'B' => -12,
|
198
|
+
'BETA' => -12,
|
199
|
+
'RC' => -11,
|
200
|
+
'M' => -10,
|
201
|
+
|
202
|
+
'RELEASE' => 0,
|
203
|
+
'FINAL' => 0,
|
204
|
+
# PHP specific
|
205
|
+
'STABLE' => 0,
|
206
|
+
|
207
|
+
# post-releases
|
208
|
+
'SP' => 1
|
209
|
+
}
|
210
|
+
|
211
|
+
def initialize(group_string)
|
212
|
+
@is_post_release = false
|
213
|
+
@is_pre_release = false
|
214
|
+
|
215
|
+
@version_string = group_string
|
216
|
+
@original_group_string = group_string
|
217
|
+
# use x as unique placeholder
|
218
|
+
group_string_ucase = group_string.to_s.gsub(/\*/, 'x').upcase
|
219
|
+
|
220
|
+
if @@group_suffixes.key?(group_string_ucase)
|
221
|
+
value = @@group_suffixes[group_string_ucase]
|
222
|
+
@is_post_release = value > 0
|
223
|
+
@is_pre_release = value < 0
|
224
|
+
@normalized_group_string = @@group_suffixes[group_string_ucase].to_s
|
225
|
+
else
|
226
|
+
@normalized_group_string = group_string_ucase
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def compare(semver_a, semver_b, ret_anr_bnonr, ret_anonr_bnr, comparator)
|
231
|
+
if semver_a.number? && semver_b.number?
|
232
|
+
semver_a.to_i.send(comparator, semver_b.to_i)
|
233
|
+
elsif semver_a.number? && !semver_b.number?
|
234
|
+
if semver_b == 'X'
|
235
|
+
true
|
236
|
+
else
|
237
|
+
ret_anr_bnonr
|
238
|
+
end
|
239
|
+
elsif !semver_a.number? && semver_b.number?
|
240
|
+
if semver_a == 'X'
|
241
|
+
true
|
242
|
+
else
|
243
|
+
ret_anonr_bnr
|
244
|
+
end
|
245
|
+
elsif semver_a == 'X' || semver_b == 'X' # !semantic_version_b.group_string.is_number? && !semantic_version_agrous_string.is_number?
|
246
|
+
true
|
247
|
+
else
|
248
|
+
semver_a.send(comparator, semver_b)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def <(other)
|
253
|
+
compare(normalized_group_string, other.normalized_group_string, true, false, :<)
|
254
|
+
end
|
255
|
+
|
256
|
+
def >(other)
|
257
|
+
compare(normalized_group_string, other.normalized_group_string, false, true, :>)
|
258
|
+
end
|
259
|
+
|
260
|
+
def >=(other)
|
261
|
+
self == other || compare(normalized_group_string, other.normalized_group_string, false, true,
|
262
|
+
:>)
|
263
|
+
end
|
264
|
+
|
265
|
+
def <=(other)
|
266
|
+
self == other || compare(normalized_group_string, other.normalized_group_string, true, false,
|
267
|
+
:<)
|
268
|
+
end
|
269
|
+
|
270
|
+
def ==(other)
|
271
|
+
normalized_group_string == other.normalized_group_string
|
272
|
+
end
|
273
|
+
|
274
|
+
def !=(other)
|
275
|
+
!(self == other)
|
276
|
+
end
|
277
|
+
|
278
|
+
def to_normalized_s
|
279
|
+
@normalized_group_string
|
280
|
+
end
|
281
|
+
|
282
|
+
def to_s
|
283
|
+
@version_string
|
284
|
+
end
|
285
|
+
|
286
|
+
def is_number?
|
287
|
+
normalized_group_string.number?
|
288
|
+
end
|
289
|
+
|
290
|
+
def is_zero?
|
291
|
+
is_number? ? normalized_group_string.to_i == 0 : false
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|