#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
pub mod ffi {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
pub use ffi::{
LIBVERSION_VERSION_MAJOR, LIBVERSION_VERSION_MINOR, LIBVERSION_VERSION_PATCH,
VERSIONFLAG_ANY_IS_PATCH, VERSIONFLAG_LOWER_BOUND, VERSIONFLAG_P_IS_PATCH,
VERSIONFLAG_UPPER_BOUND, version_compare2, version_compare4,
};
use std::cmp::Ordering;
use std::ffi::CString;
pub fn compare(v1: &str, v2: &str) -> Ordering {
let v1 = CString::new(v1).expect("v1 contains interior null byte");
let v2 = CString::new(v2).expect("v2 contains interior null byte");
let result = unsafe { ffi::version_compare2(v1.as_ptr(), v2.as_ptr()) };
result.cmp(&0)
}
pub fn compare_with_flags(v1: &str, v2: &str, v1_flags: u32, v2_flags: u32) -> Ordering {
let v1 = CString::new(v1).expect("v1 contains interior null byte");
let v2 = CString::new(v2).expect("v2 contains interior null byte");
let result = unsafe {
ffi::version_compare4(v1.as_ptr(), v2.as_ptr(), v1_flags as i32, v2_flags as i32)
};
result.cmp(&0)
}
pub fn version_string() -> &'static str {
std::str::from_utf8(&ffi::LIBVERSION_VERSION[..ffi::LIBVERSION_VERSION.len() - 1])
.expect("libversion version string is not valid UTF-8")
}
#[allow(clippy::absurd_extreme_comparisons)]
pub const fn version_atleast(major: u32, minor: u32, patch: u32) -> bool {
(LIBVERSION_VERSION_MAJOR > major)
|| (LIBVERSION_VERSION_MAJOR == major && LIBVERSION_VERSION_MINOR > minor)
|| (LIBVERSION_VERSION_MAJOR == major
&& LIBVERSION_VERSION_MINOR == minor
&& LIBVERSION_VERSION_PATCH >= patch)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_comparison() {
assert_eq!(compare("0.99", "1.11"), Ordering::Less);
assert_eq!(compare("1.0", "1.0.0"), Ordering::Equal);
assert_eq!(compare("1.0", "0.99"), Ordering::Greater);
}
#[test]
fn prerelease() {
assert_eq!(compare("1.0alpha1", "1.0"), Ordering::Less);
assert_eq!(compare("1.0alpha1", "1.0rc1"), Ordering::Less);
assert_eq!(compare("1.0rc1", "1.0"), Ordering::Less);
}
#[test]
fn postrelease() {
assert_eq!(compare("1.0patch1", "1.0"), Ordering::Greater);
assert_eq!(compare("1.0.1", "1.0"), Ordering::Greater);
}
#[test]
fn p_is_patch_flag() {
assert_eq!(compare("1.0p1", "1.0"), Ordering::Less);
assert_eq!(
compare_with_flags("1.0p1", "1.0", VERSIONFLAG_P_IS_PATCH, 0),
Ordering::Greater,
);
}
#[test]
fn any_is_patch_flag() {
assert_eq!(compare("1.0foopatchset1", "1.0"), Ordering::Less);
assert_eq!(
compare_with_flags("1.0foopatchset1", "1.0", VERSIONFLAG_ANY_IS_PATCH, 0),
Ordering::Greater,
);
}
#[test]
fn lower_bound_flag() {
assert_eq!(
compare_with_flags("1.0alpha1", "1.0", 0, VERSIONFLAG_LOWER_BOUND),
Ordering::Greater,
);
assert_eq!(
compare_with_flags("0.999", "1.0", 0, VERSIONFLAG_LOWER_BOUND),
Ordering::Less,
);
}
#[test]
fn upper_bound_flag() {
assert_eq!(
compare_with_flags("1.0.1", "1.0", 0, VERSIONFLAG_UPPER_BOUND),
Ordering::Less,
);
assert_eq!(
compare_with_flags("1.1", "1.0", 0, VERSIONFLAG_UPPER_BOUND),
Ordering::Greater,
);
}
#[test]
#[should_panic(expected = "v1 contains interior null byte")]
fn compare_rejects_interior_null() {
let _ = compare("1.0\0rc1", "1.0");
}
#[test]
#[should_panic(expected = "v2 contains interior null byte")]
fn compare_with_flags_rejects_interior_null() {
let _ = compare_with_flags("1.0", "1.0\0rc1", 0, 0);
}
#[test]
fn version_metadata() {
let parts = version_string()
.split('.')
.map(|part| part.parse::<u32>().unwrap())
.collect::<Vec<_>>();
assert_eq!(
parts,
vec![
LIBVERSION_VERSION_MAJOR,
LIBVERSION_VERSION_MINOR,
LIBVERSION_VERSION_PATCH,
]
);
assert!(version_atleast(
LIBVERSION_VERSION_MAJOR,
LIBVERSION_VERSION_MINOR,
LIBVERSION_VERSION_PATCH,
));
assert!(!version_string().is_empty());
}
#[test]
fn ffi_direct() {
let v1 = CString::new("1.0").unwrap();
let v2 = CString::new("2.0").unwrap();
let result = unsafe { ffi::version_compare2(v1.as_ptr(), v2.as_ptr()) };
assert_eq!(result, -1);
}
#[test]
fn ffi_compare4_direct() {
let v1 = CString::new("1.0p1").unwrap();
let v2 = CString::new("1.0").unwrap();
let result = unsafe {
ffi::version_compare4(v1.as_ptr(), v2.as_ptr(), VERSIONFLAG_P_IS_PATCH as i32, 0)
};
assert_eq!(result, 1);
}
}