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
|
# frozen_string_literal: true
module Packages
module Nuget
module VersionHelpers
private
def sort_versions(versions)
versions.sort { |a, b| compare_versions(a, b) }
end
# NuGet version sorting algorithm as per https://semver.org/spec/v2.0.0.html#spec-item-11
def compare_versions(version_a, version_b)
return 0 if version_a == version_b
return 1 if version_b.nil?
return -1 if version_a.nil?
a_without_build_meta, a_build_meta = version_a.split('+', 2)
b_without_build_meta, b_build_meta = version_b.split('+', 2)
a_core, a_pre = a_without_build_meta.split(/-/, 2)
b_core, b_pre = b_without_build_meta.split(/-/, 2)
a_core_parts = a_core.split('.')
b_core_parts = b_core.split('.')
compare_core_parts(a_core_parts, b_core_parts) ||
compare_pre_release_parts(a_pre, b_pre) ||
pick_non_nil(a_pre, b_pre) ||
compare_build_meta_parts(a_build_meta, b_build_meta)
end
def compare_core_parts(a_core_parts, b_core_parts)
while a_core_parts.any? || b_core_parts.any?
a_part = a_core_parts.shift&.to_i || 0
b_part = b_core_parts.shift&.to_i || 0
return a_part <=> b_part if a_part != b_part
end
end
def compare_pre_release_parts(a_pre, b_pre)
return unless a_pre && b_pre
a_pre_parts = a_pre.split('.').map(&:downcase)
b_pre_parts = b_pre.split('.').map(&:downcase)
while a_pre_parts.any? || b_pre_parts.any?
a_pre_part = a_pre_parts.shift
b_pre_part = b_pre_parts.shift
# Empty parts are considered lower
return -1 if a_pre_part.nil?
return 1 if b_pre_part.nil?
a_num = a_pre_part.to_i
b_num = b_pre_part.to_i
next if a_num == b_num && a_pre_part.to_s == b_pre_part.to_s # Both are same numeric/alphanumeric parts
return select_numeric_before_alphanumeric(a_num, a_pre_part, b_num, b_pre_part) ||
compare_numeric_parts(a_pre_part, a_num, b_pre_part, b_num) ||
a_pre_part <=> b_pre_part
end
end
def compare_build_meta_parts(a_build_meta, b_build_meta)
(a_build_meta || '').casecmp(b_build_meta || '')
end
def select_numeric_before_alphanumeric(a_num, a_pre_part, b_num, b_pre_part)
return -1 if a_num != b_num && numeric?(a_pre_part) && !numeric?(b_pre_part)
return 1 if a_num != b_num && !numeric?(a_pre_part) && numeric?(b_pre_part)
end
def numeric?(pre_part)
!!Integer(pre_part, exception: false)
end
def compare_numeric_parts(a_pre_part, a_num, b_pre_part, b_num)
a_num <=> b_num if a_num != b_num && numeric?(a_pre_part) && numeric?(b_pre_part)
end
def pick_non_nil(var_a, var_b)
return -1 if var_a && !var_b
return 1 if !var_a && var_b
end
end
end
end
|