mirror of
https://github.com/edera-dev/sprout.git
synced 2026-11-04 23:02:21 +00:00
fix(bls): sort pre-release versions before releases
The '~' check ran after the end-of-string check, so "1~rc1" compared as newer than "1". Per the UAPI version format specification, '~' must be handled first so a pre-release sorts older than the release it precedes. An empty string is kept as the oldest version, matching systemd's strverscmp_improved.
This commit is contained in:
1 parent
dd35c2153b
commit
9d6d88c5e0
1 file changed
+33
-3
+33
-3
@@ -247,6 +247,11 @@ pub fn compare_versions_optional(a: Option<&str>, b: Option<&str>) -> Ordering {
|
|||||||
/// Compares two strings using the BLS version comparison specification.
|
/// Compares two strings using the BLS version comparison specification.
|
||||||
/// See: <https://uapi-group.org/specifications/specs/version_format_specification/>
|
/// See: <https://uapi-group.org/specifications/specs/version_format_specification/>
|
||||||
pub fn compare_versions(a: &str, b: &str) -> Ordering {
|
pub fn compare_versions(a: &str, b: &str) -> Ordering {
|
||||||
|
// An empty string is older than any other string.
|
||||||
|
if a.is_empty() || b.is_empty() {
|
||||||
|
return a.len().cmp(&b.len());
|
||||||
|
}
|
||||||
|
|
||||||
// Acquire a peekable iterator for each string.
|
// Acquire a peekable iterator for each string.
|
||||||
let mut a_chars = a.chars().peekable();
|
let mut a_chars = a.chars().peekable();
|
||||||
let mut b_chars = b.chars().peekable();
|
let mut b_chars = b.chars().peekable();
|
||||||
@@ -257,6 +262,19 @@ pub fn compare_versions(a: &str, b: &str) -> Ordering {
|
|||||||
skip_invalid(&mut a_chars);
|
skip_invalid(&mut a_chars);
|
||||||
skip_invalid(&mut b_chars);
|
skip_invalid(&mut b_chars);
|
||||||
|
|
||||||
|
// Handle the ~ character. This must happen before the end of string check,
|
||||||
|
// as a string with ~ is older than a string that has ended (1~rc1 < 1).
|
||||||
|
match (a_chars.peek() == Some(&'~'), b_chars.peek() == Some(&'~')) {
|
||||||
|
(true, false) => return Ordering::Less,
|
||||||
|
(false, true) => return Ordering::Greater,
|
||||||
|
(true, true) => {
|
||||||
|
a_chars.next();
|
||||||
|
b_chars.next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(false, false) => {}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if either string has ended.
|
// Check if either string has ended.
|
||||||
match (a_chars.peek(), b_chars.peek()) {
|
match (a_chars.peek(), b_chars.peek()) {
|
||||||
// No more characters in either string.
|
// No more characters in either string.
|
||||||
@@ -266,9 +284,6 @@ pub fn compare_versions(a: &str, b: &str) -> Ordering {
|
|||||||
(Some(_), None) => return Ordering::Greater,
|
(Some(_), None) => return Ordering::Greater,
|
||||||
// Both strings have characters left.
|
// Both strings have characters left.
|
||||||
(Some(&ca), Some(&cb)) => {
|
(Some(&ca), Some(&cb)) => {
|
||||||
// Handle the ~ character.
|
|
||||||
handle_single_char!(ca, cb, a_chars, b_chars, '~');
|
|
||||||
|
|
||||||
// Handle '-' character.
|
// Handle '-' character.
|
||||||
handle_single_char!(ca, cb, a_chars, b_chars, '-');
|
handle_single_char!(ca, cb, a_chars, b_chars, '-');
|
||||||
|
|
||||||
@@ -689,6 +704,21 @@ efi /EFI/fedora/shimx64.efi
|
|||||||
assert_eq!(compare_versions("1.0", "1~rc1"), Ordering::Greater);
|
assert_eq!(compare_versions("1.0", "1~rc1"), Ordering::Greater);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tilde_is_older_than_end_of_string() {
|
||||||
|
// A pre-release is older than the release it precedes.
|
||||||
|
assert_eq!(compare_versions("1~rc1", "1"), Ordering::Less);
|
||||||
|
assert_eq!(compare_versions("1", "1~rc1"), Ordering::Greater);
|
||||||
|
assert_eq!(compare_versions("6.1~rc3", "6.1"), Ordering::Less);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_string_is_oldest() {
|
||||||
|
assert_eq!(compare_versions("", "~1"), Ordering::Less);
|
||||||
|
assert_eq!(compare_versions("~1", ""), Ordering::Greater);
|
||||||
|
assert_eq!(compare_versions("", "1"), Ordering::Less);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn optional_both_none_equal() {
|
fn optional_both_none_equal() {
|
||||||
assert_eq!(compare_versions_optional(None, None), Ordering::Equal);
|
assert_eq!(compare_versions_optional(None, None), Ordering::Equal);
|
||||||
|
|||||||
Reference in new issue
Block a user