semver_dialects 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8b7d27d36822fd9137e04ea549b5b5db9304c24396d290fe9c5f543be9e15bcc
4
+ data.tar.gz: e7421f4c9b05ae145dc5913895eecf7a7f78d0adf22c161674354aa0449fee10
5
+ SHA512:
6
+ metadata.gz: e62253609623f4380235c1630a30840516b5ef9df3979bba3100d814358c74a8293912807f3ad0ffe9bc6c790d1e9be272165248af62a9dfe4d77f16df1d821f
7
+ data.tar.gz: 39230f2105ec31909f051ba193fa4c5e17783a33496ed861a8777c9ce66731d43b0a460a905411696f9ce5e651111e6448f51ae193f24d2951cad2ab43251629
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'semver_dialects/version'
4
+ require 'semver_dialects/semantic_version/version_translator'
5
+ require 'semver_dialects/semantic_version/version_parser'
6
+ require 'semver_dialects/semantic_version/version_range'
7
+
8
+ module SemverDialects
9
+ # Captures all errors that could be possibly raised
10
+ class Error < StandardError
11
+ def initialize(msg)
12
+ super(msg)
13
+ end
14
+ end
15
+
16
+ # A utiltity module that helps with version matching
17
+ module VersionChecker
18
+ def self.version_translate(typ, version_string)
19
+ case typ
20
+ when 'maven'
21
+ VersionTranslator.translate_maven(version_string)
22
+ when 'npm'
23
+ VersionTranslator.translate_npm(version_string)
24
+ when 'conan'
25
+ VersionTranslator.translate_conan(version_string)
26
+ when 'nuget'
27
+ VersionTranslator.translate_nuget(version_string)
28
+ when 'go'
29
+ VersionTranslator.translate_go(version_string)
30
+ when 'gem'
31
+ VersionTranslator.translate_gem(version_string)
32
+ when 'pypi'
33
+ VersionTranslator.translate_pypi(version_string)
34
+ when 'packagist'
35
+ VersionTranslator.translate_packagist(version_string)
36
+ else
37
+ raise SemverDialects::Error, 'unsupported package type'
38
+ end
39
+ end
40
+
41
+ def self.version_sat?(typ, raw_ver, raw_constraint)
42
+ version_constraint = version_translate(typ, raw_constraint)
43
+ version = VersionParser.parse('=' + raw_ver)
44
+
45
+ raise SemverDialects::Error, 'malformed constraint' if version_constraint.nil? || version_constraint.empty?
46
+ raise SemverDialects::Error, 'malformed constraint' if version.nil? || version.empty?
47
+
48
+ constraint = VersionRange.new
49
+ version_constraint.each do |version_interval_str|
50
+ constraint << VersionParser.parse(version_interval_str)
51
+ end
52
+
53
+ constraint.overlaps_with?(version)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module SemverDialects
6
+ # Handle the application command line parsing
7
+ # and the dispatch to various command objects
8
+ #
9
+ # @api public
10
+ class CLI < Thor
11
+ # Error raised by this runner
12
+ Error = Class.new(StandardError)
13
+
14
+ desc 'version', 'semver_dialects version'
15
+ def version
16
+ require_relative 'version'
17
+ puts "v#{SemverDialects::VERSION}"
18
+ end
19
+ map %w[--version -v] => :version
20
+
21
+ desc 'check_version TYPE VERSION CONSTRAINT', 'Command description...'
22
+ method_option :help, aliases: '-h', type: :boolean,
23
+ desc: 'Display usage information'
24
+ def check_version(type, version, constraint)
25
+ if options[:help]
26
+ invoke :help, ['check_version']
27
+ else
28
+ require_relative 'commands/check_version'
29
+ ecode = SemverDialects::Commands::CheckVersion.new(type, version, constraint, options).execute
30
+ exit(ecode)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module SemverDialects
6
+ # A CLI command
7
+ class Command
8
+ extend Forwardable
9
+
10
+ def_delegators :command, :run
11
+
12
+ # Execute this command
13
+ #
14
+ # @api public
15
+ def execute(*)
16
+ raise(
17
+ NotImplementedError,
18
+ "#{self.class}##{__method__} must be implemented"
19
+ )
20
+ end
21
+
22
+ # The external commands runner
23
+ #
24
+ # @see http://www.rubydoc.info/gems/tty-command
25
+ #
26
+ # @api public
27
+ def command(**options)
28
+ require 'tty-command'
29
+ TTY::Command.new(options)
30
+ end
31
+
32
+ # The cursor movement
33
+ #
34
+ # @see http://www.rubydoc.info/gems/tty-cursor
35
+ #
36
+ # @api public
37
+ def cursor
38
+ require 'tty-cursor'
39
+ TTY::Cursor
40
+ end
41
+
42
+ # Open a file or text in the user's preferred editor
43
+ #
44
+ # @see http://www.rubydoc.info/gems/tty-editor
45
+ #
46
+ # @api public
47
+ def editor
48
+ require 'tty-editor'
49
+ TTY::Editor
50
+ end
51
+
52
+ # File manipulation utility methods
53
+ #
54
+ # @see http://www.rubydoc.info/gems/tty-file
55
+ #
56
+ # @api public
57
+ def generator
58
+ require 'tty-file'
59
+ TTY::File
60
+ end
61
+
62
+ # Terminal output paging
63
+ #
64
+ # @see http://www.rubydoc.info/gems/tty-pager
65
+ #
66
+ # @api public
67
+ def pager(**options)
68
+ require 'tty-pager'
69
+ TTY::Pager.new(options)
70
+ end
71
+
72
+ # Terminal platform and OS properties
73
+ #
74
+ # @see http://www.rubydoc.info/gems/tty-pager
75
+ #
76
+ # @api public
77
+ def platform
78
+ require 'tty-platform'
79
+ TTY::Platform.new
80
+ end
81
+
82
+ # The interactive prompt
83
+ #
84
+ # @see http://www.rubydoc.info/gems/tty-prompt
85
+ #
86
+ # @api public
87
+ def prompt(**options)
88
+ require 'tty-prompt'
89
+ TTY::Prompt.new(options)
90
+ end
91
+
92
+ # Get terminal screen properties
93
+ #
94
+ # @see http://www.rubydoc.info/gems/tty-screen
95
+ #
96
+ # @api public
97
+ def screen
98
+ require 'tty-screen'
99
+ TTY::Screen
100
+ end
101
+
102
+ # The unix which utility
103
+ #
104
+ # @see http://www.rubydoc.info/gems/tty-which
105
+ #
106
+ # @api public
107
+ def which(*args)
108
+ require 'tty-which'
109
+ TTY::Which.which(*args)
110
+ end
111
+
112
+ # Check if executable exists
113
+ #
114
+ # @see http://www.rubydoc.info/gems/tty-which
115
+ #
116
+ # @api public
117
+ def exec_exist?(*args)
118
+ require 'tty-which'
119
+ TTY::Which.exist?(*args)
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative '../../semver_dialects.rb'
5
+ require_relative '../semantic_version/version_translator.rb'
6
+ require_relative '../semantic_version/version_parser.rb'
7
+ require_relative '../semantic_version/version_range.rb'
8
+
9
+ module SemverDialects
10
+ module Commands
11
+ # The check version command implementation
12
+ class CheckVersion < SemverDialects::Command
13
+ def initialize(type, version, constraint, options)
14
+ @type = type
15
+ @version = version
16
+ @constraint = constraint
17
+ @options = options
18
+ @avail_types = %w[gem npm ruby pypi php maven go]
19
+ end
20
+
21
+ def execute(_input: $stdin, output: $stdout)
22
+ typ = @type.downcase
23
+ raise SemverDialects::Error, 'wrong package type' unless @avail_types.include?(typ)
24
+
25
+ if VersionChecker.version_sat?(typ, @version, @constraint)
26
+ output.puts "#{@version} matches #{@constraint} for #{@type}"
27
+ 0
28
+ else
29
+ output.puts "#{@version} does not match #{@constraint}"
30
+ 1
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,323 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../utils.rb'
4
+
5
+ class SemanticVersion
6
+ attr_reader :version_string, :prefix_delimiter, :prefix_segments, :suffix_segments, :segments
7
+
8
+ def initialize(version_string, segments = nil)
9
+ @version_string = version_string
10
+ @prefix_segments = []
11
+ @suffix_segments = []
12
+ @prefix_delimiter = 0
13
+ version, _ = version_string.split('+')
14
+ if segments.nil?
15
+ @segments = split_version_string!(version)
16
+ else
17
+ @prefix_segments = segments
18
+ @prefix_delimiter = segments.size - 1
19
+ @segments = segments
20
+ end
21
+ end
22
+
23
+ def split_version_string!(version_string)
24
+ delim_pattern = /[\.\-]/
25
+ split_array = version_string.split(delim_pattern).map {
26
+ |grp|
27
+ grp.split(/(\d+)/).reject { |cell| cell.nil? || cell.empty? }
28
+ }.flatten
29
+
30
+ # go as far to the right as possible considering numbers and placeholders
31
+ (0..split_array.size - 1).each do
32
+ |i|
33
+ if split_array[i].number? || split_array[i] == "X" || split_array[i] == "x"
34
+ @prefix_delimiter = i
35
+ else
36
+ break
37
+ end
38
+ end
39
+
40
+ # remove redundant trailing zeros
41
+ @prefix_delimiter.downto(0).each do
42
+ |i|
43
+ if split_array[i] == "0"
44
+ split_array.delete_at(i)
45
+ @prefix_delimiter -= 1
46
+ else
47
+ break
48
+ end
49
+ end
50
+
51
+ @prefix_segments = split_array[0..@prefix_delimiter].map { |group_string| SemanticVersionSegment.new(group_string) } unless @prefix_delimiter < 0
52
+ if split_array.size - 1 >= @prefix_delimiter + 1
53
+ @suffix_segments = split_array[@prefix_delimiter + 1, split_array.size].map { |group_string| SemanticVersionSegment.new(group_string) }
54
+ end
55
+
56
+ @prefix_segments.clone().concat(@suffix_segments)
57
+ end
58
+
59
+ def _get_equalized_arrays_for(array_a, array_b)
60
+ first_array = array_a.clone()
61
+ second_array = array_b.clone()
62
+ if first_array.size < second_array.size
63
+ (second_array.size - first_array.size).times do
64
+ first_array << SemanticVersionSegment.new("0")
65
+ end
66
+ elsif first_array.size > second_array.size
67
+ (first_array.size - second_array.size).times do
68
+ second_array << SemanticVersionSegment.new("0")
69
+ end
70
+ end
71
+ [first_array, second_array]
72
+ end
73
+
74
+ def get_equalized_arrays_for(semver_a, semver_b)
75
+ first_array_prefix = semver_a.prefix_segments.clone()
76
+ second_array_prefix = semver_b.prefix_segments.clone()
77
+ first_array_suffix = semver_a.suffix_segments.clone()
78
+ second_array_suffix = semver_b.suffix_segments.clone()
79
+ first_array_prefix, second_array_prefix = _get_equalized_arrays_for(first_array_prefix, second_array_prefix)
80
+ first_array_suffix, second_array_suffix = _get_equalized_arrays_for(first_array_suffix, second_array_suffix)
81
+ [first_array_prefix.concat(first_array_suffix), second_array_prefix.concat(second_array_suffix)]
82
+ end
83
+
84
+ def get_equalized_prefix_arrays_for(semver_a, semver_b)
85
+ first_array_prefix = semver_a.prefix_segments.clone()
86
+ second_array_prefix = semver_b.prefix_segments.clone()
87
+ first_array_prefix, second_array_prefix = _get_equalized_arrays_for(first_array_prefix, second_array_prefix)
88
+ [first_array_prefix, second_array_prefix]
89
+ end
90
+
91
+ def <(other_semver)
92
+ self_array, other_array = get_equalized_arrays_for(self, other_semver)
93
+ (0..self_array.size - 1).each {
94
+ |i|
95
+ if self_array[i] < other_array[i]
96
+ return true
97
+ elsif self_array[i] > other_array[i]
98
+ return false
99
+ end
100
+ }
101
+ false
102
+ end
103
+
104
+ def is_zero?
105
+ @prefix_segments.empty? || @prefix_segments.all? { |segment| segment.is_zero? }
106
+ end
107
+
108
+ def pre_release?
109
+ @suffix_segments.any?(&:is_pre_release)
110
+ end
111
+
112
+ def post_release?
113
+ @suffix_segments.any?(&:is_post_release)
114
+ end
115
+
116
+ def >(other_semver)
117
+ self_array, other_array = get_equalized_arrays_for(self, other_semver)
118
+ (0..self_array.size - 1).each {
119
+ |i|
120
+ if self_array[i] > other_array[i]
121
+ return true
122
+ elsif self_array[i] < other_array[i]
123
+ return false
124
+ end
125
+ }
126
+ false
127
+ end
128
+
129
+ def >=(other_semver)
130
+ self == other_semver || self > other_semver || self == other_semver
131
+ end
132
+
133
+ def <=(other_semver)
134
+ self == other_semver || self < other_semver
135
+ end
136
+
137
+ def ==(other_semver)
138
+ segments_a = []
139
+ segments_b = []
140
+
141
+ segments_a += other_semver.segments
142
+ segments_b += @segments
143
+
144
+ if other_semver.segments.size < @segments.size
145
+ (@segments.size - other_semver.segments.size).times {|_| segments_a << SemanticVersionSegment.new("0")}
146
+ elsif other_semver.segments.size > @segments.size
147
+ (other_semver.segments.size - @segments.size).times {|_| segments_b << SemanticVersionSegment.new("0") }
148
+ end
149
+
150
+ (0..segments_a.size - 1).each {
151
+ |i|
152
+ if segments_a[i] != segments_b[i]
153
+ return false
154
+ end
155
+ }
156
+ true
157
+ end
158
+
159
+ def !=(other_semver)
160
+ return !(self == other_semver)
161
+ end
162
+
163
+ def diff(other_semver, abs = true)
164
+ diffs = []
165
+
166
+ self_array, other_array = get_equalized_prefix_arrays_for(self, other_semver)
167
+ (0..self_array.size - 1).each {
168
+ |i|
169
+ # diff semantic groups
170
+ diffs << (self_array[i].diff(other_array[i], abs))
171
+ }
172
+ diffs
173
+ end
174
+
175
+ def to_normalized_s
176
+ @segments.map { |group| group.to_normalized_s }.join(":").to_s
177
+ end
178
+
179
+ def to_s
180
+ @segments.map { |group| group.to_s }.join(":").to_s
181
+ end
182
+
183
+ def minor
184
+ @prefix_segments.size >= 1 ? @prefix_segments[1].to_s : "0"
185
+ end
186
+
187
+ def major
188
+ @prefix_segments.size >= 2 ? @prefix_segments[0].to_s : "0"
189
+ end
190
+
191
+ def patch
192
+ @prefix_segments.size >= 3 ? @prefix_segments[2].to_s : "0"
193
+ end
194
+
195
+ def is_successor_of?(other_semver)
196
+ filtered_segments = self.diff(other_semver, false).reject { |i| i.to_i == 0 }
197
+ filtered_segments.size == 1 && filtered_segments.last.to_i == 1
198
+ end
199
+
200
+ def cross_total()
201
+ [self.major, self.minor, self.patch].reject { |i| i.empty? }.map { |i| i.to_i }.inject(0) { |sum, x| sum + x }
202
+ end
203
+ end
204
+
205
+ class SemanticVersionSegment
206
+ attr_accessor :normalized_group_string, :original_group_string, :is_post_release, :is_pre_release
207
+
208
+ @@group_suffixes = {
209
+ # pre-releases
210
+ "PRE" => -16,
211
+ "PREVIEW" => -16,
212
+ "DEV" => -15,
213
+ "A" => -14,
214
+ "ALPHA" => -13,
215
+ "B" => -12,
216
+ "BETA" => -12,
217
+ "RC" => -11,
218
+ "M" => -10,
219
+
220
+ "RELEASE" => 0,
221
+ "FINAL" => 0,
222
+ # PHP specific
223
+ "STABLE" => 0,
224
+
225
+ # post-releases
226
+ "SP" => 1,
227
+ }
228
+
229
+ def initialize(group_string)
230
+ @is_post_release = false
231
+ @is_pre_release = false
232
+
233
+ @version_string = group_string
234
+ @original_group_string = group_string
235
+ # use x as unique placeholder
236
+ group_string_ucase = group_string.to_s.gsub(/\*/, "x").upcase
237
+
238
+ if @@group_suffixes.key?(group_string_ucase)
239
+ value = @@group_suffixes[group_string_ucase]
240
+ @is_post_release = value > 0
241
+ @is_pre_release = value < 0
242
+ @normalized_group_string = @@group_suffixes[group_string_ucase].to_s
243
+ else
244
+ @normalized_group_string = group_string_ucase
245
+ end
246
+ end
247
+
248
+ def compare(semver_a, semver_b, ret_anr_bnonr, ret_anonr_bnr, comparator)
249
+ if semver_a.number? && semver_b.number?
250
+ semver_a.to_i.send(comparator, semver_b.to_i)
251
+ elsif semver_a.number? && !semver_b.number?
252
+ if semver_b == "X"
253
+ true
254
+ else
255
+ ret_anr_bnonr
256
+ end
257
+ elsif !semver_a.number? && semver_b.number?
258
+ if semver_a == "X"
259
+ true
260
+ else
261
+ ret_anonr_bnr
262
+ end
263
+ else # !semantic_version_b.group_string.is_number? && !semantic_version_agrous_string.is_number?
264
+ if semver_a == "X" || semver_b == "X"
265
+ true
266
+ else
267
+ semver_a.send(comparator, semver_b)
268
+ end
269
+ end
270
+ end
271
+
272
+ def <(other_semver)
273
+ compare(self.normalized_group_string, other_semver.normalized_group_string, true, false, :<)
274
+ end
275
+
276
+ def >(other_semver)
277
+ compare(self.normalized_group_string, other_semver.normalized_group_string, false, true, :>)
278
+ end
279
+
280
+ def >=(other_semver)
281
+ self == other_semver || compare(self.normalized_group_string, other_semver.normalized_group_string, false, true, :>)
282
+ end
283
+
284
+ def <=(other_semver)
285
+ self == other_semver || compare(self.normalized_group_string, other_semver.normalized_group_string, true, false, :<)
286
+ end
287
+
288
+ def ==(other_semver)
289
+ self.normalized_group_string == other_semver.normalized_group_string
290
+ end
291
+
292
+ def !=(other_semver)
293
+ return !(self == other_semver)
294
+ end
295
+
296
+ def to_normalized_s
297
+ @normalized_group_string
298
+ end
299
+
300
+ def to_s
301
+ @version_string
302
+ end
303
+
304
+ def is_number?
305
+ self.normalized_group_string.number?
306
+ end
307
+
308
+ def is_zero?
309
+ is_number? ? self.normalized_group_string.to_i == 0 : false
310
+ end
311
+
312
+ def diff(other_semver, abs = true)
313
+
314
+ if other_semver.normalized_group_string == "X" || self.normalized_group_string == "X"
315
+ "0"
316
+ elsif self.normalized_group_string.number? && other_semver.normalized_group_string.number?
317
+ ret = self.normalized_group_string.to_i - other_semver.normalized_group_string.to_i
318
+ (abs && ret < 0) ? -ret : ret
319
+ else
320
+ nil
321
+ end
322
+ end
323
+ end