///|
/// Compare two package versions using the segment rules used by pkg-config
/// implementations: separators are ignored, numeric segments compare by
/// magnitude, numeric segments sort after alphabetic segments, and `~` sorts
/// before every other segment.
///
/// The result is negative, zero, or positive when `left` is respectively less
/// than, equal to, or greater than `right`.
pub fn compare_versions(left : String, right : String) -> Int {
let a = left.to_array()
let b = right.to_array()
let mut ai = 0
let mut bi = 0
while true {
while ai < a.length() && !version_alnum(a[ai]) && a[ai] != '~' {
ai = ai + 1
}
while bi < b.length() && !version_alnum(b[bi]) && b[bi] != '~' {
bi = bi + 1
}
if (ai < a.length() && a[ai] == '~') || (bi < b.length() && b[bi] == '~') {
if ai >= a.length() || a[ai] != '~' {
return 1
}
if bi >= b.length() || b[bi] != '~' {
return -1
}
ai = ai + 1
bi = bi + 1
continue
}
if ai >= a.length() || bi >= b.length() {
break
}
let a_numeric = version_digit(a[ai])
let b_numeric = version_digit(b[bi])
if a_numeric && !b_numeric {
return 1
}
if !a_numeric && b_numeric {
return -1
}
let mut a_end = ai
let mut b_end = bi
if a_numeric {
while a_end < a.length() && version_digit(a[a_end]) {
a_end = a_end + 1
}
while b_end < b.length() && version_digit(b[b_end]) {
b_end = b_end + 1
}
let mut a_value = ai
let mut b_value = bi
while a_value < a_end && a[a_value] == '0' {
a_value = a_value + 1
}
while b_value < b_end && b[b_value] == '0' {
b_value = b_value + 1
}
let a_length = a_end - a_value
let b_length = b_end - b_value
if a_length != b_length {
return if a_length < b_length { -1 } else { 1 }
}
let mut offset = 0
while offset < a_length {
if a[a_value + offset] != b[b_value + offset] {
return if a[a_value + offset] < b[b_value + offset] { -1 } else { 1 }
}
offset = offset + 1
}
} else {
while a_end < a.length() && version_alpha(a[a_end]) {
a_end = a_end + 1
}
while b_end < b.length() && version_alpha(b[b_end]) {
b_end = b_end + 1
}
let mut offset = 0
let common = if a_end - ai < b_end - bi { a_end - ai } else { b_end - bi }
while offset < common {
if a[ai + offset] != b[bi + offset] {
return if a[ai + offset] < b[bi + offset] { -1 } else { 1 }
}
offset = offset + 1
}
if a_end - ai != b_end - bi {
return if a_end - ai < b_end - bi { -1 } else { 1 }
}
}
ai = a_end
bi = b_end
}
while ai < a.length() && !version_alnum(a[ai]) && a[ai] != '~' {
ai = ai + 1
}
while bi < b.length() && !version_alnum(b[bi]) && b[bi] != '~' {
bi = bi + 1
}
if ai < a.length() && a[ai] == '~' {
return -1
}
if bi < b.length() && b[bi] == '~' {
return 1
}
if ai < a.length() {
1
} else if bi < b.length() {
-1
} else {
0
}
}
///|
/// Test an installed version against one parsed dependency requirement.
pub fn Requirement::matches(
self : Requirement,
installed_version : String,
) -> Bool {
if self.op == Any {
return true
}
let order = compare_versions(installed_version, self.version)
match self.op {
Any => true
Equal => order == 0
NotEqual => order != 0
Less => order < 0
LessEqual => order <= 0
Greater => order > 0
GreaterEqual => order >= 0
}
}
///|
fn version_digit(c : Char) -> Bool {
c >= '0' && c <= '9'
}
///|
fn version_alpha(c : Char) -> Bool {
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
///|
fn version_alnum(c : Char) -> Bool {
version_digit(c) || version_alpha(c)
}