use capi;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::ffi::CStr;
pub use capi::version::{Compatibility, get_compatibility};
pub use capi::version::{TARGET_VERSION_STRING, TARGET_VERSION};
pub use capi::version::{PA_API_VERSION as API_VERSION, PA_PROTOCOL_VERSION as PROTOCOL_VERSION};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[non_exhaustive]
enum ErrorKind {
ParseIntError,
MissingPart,
ExtraParts,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Error {
ver_str: Cow<'static, str>,
}
impl Error {
#[inline]
fn new(ver_str: Cow<'static, str>) -> Self {
Self { ver_str }
}
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
format!("failed to parse PulseAudio system library version string '{}'", &self.ver_str)
.fmt(f)
}
}
#[inline]
pub fn library_version_is_too_old() -> Result<bool, Error> {
match compare_with_library_version(TARGET_VERSION.0, TARGET_VERSION.1)? {
Ordering::Less | Ordering::Equal => Ok(false),
Ordering::Greater => Ok(true),
}
}
#[inline]
pub fn compare_with_library_version(major: u8, minor: u8) -> Result<std::cmp::Ordering, Error> {
let (lib_major, lib_minor, _) = get_library_version_numbers()?;
Ok((major).cmp(&lib_major).then_with(|| minor.cmp(&lib_minor)))
}
#[inline]
pub fn get_library_version_numbers() -> Result<(u8, u8, u8), Error> {
let ver = get_library_version().to_string_lossy();
pa_version_str_to_num(&ver).or_else(|_e| Err(Error::new(ver)))
}
#[inline]
fn pa_version_str_to_num(ver: &str) -> Result<(u8, u8, u8), ErrorKind> {
let mut parts = ver.split('.');
let major: u8 =
parts.next().ok_or(ErrorKind::MissingPart)?.parse().or(Err(ErrorKind::ParseIntError))?;
let minor: u8 =
parts.next().ok_or(ErrorKind::MissingPart)?.parse().or(Err(ErrorKind::ParseIntError))?;
let micro: u8 =
parts.next().ok_or(ErrorKind::MissingPart)?.parse().or(Err(ErrorKind::ParseIntError))?;
match parts.next().is_some() {
true => Err(ErrorKind::ExtraParts), false => Ok((major, minor, micro)),
}
}
#[inline]
pub fn get_library_version() -> &'static CStr {
unsafe { CStr::from_ptr(capi::pa_get_library_version()) }
}
#[test]
fn test_ver_str_to_num() {
assert_eq!(pa_version_str_to_num(""), Err(ErrorKind::ParseIntError));
assert_eq!(pa_version_str_to_num(" "), Err(ErrorKind::ParseIntError));
assert_eq!(pa_version_str_to_num("."), Err(ErrorKind::ParseIntError));
assert_eq!(pa_version_str_to_num("a"), Err(ErrorKind::ParseIntError));
assert_eq!(pa_version_str_to_num("a.a"), Err(ErrorKind::ParseIntError));
assert_eq!(pa_version_str_to_num("a.1"), Err(ErrorKind::ParseIntError));
assert_eq!(pa_version_str_to_num("14"), Err(ErrorKind::MissingPart));
assert_eq!(pa_version_str_to_num("14.0"), Err(ErrorKind::MissingPart));
assert_eq!(pa_version_str_to_num("14.0.0"), Ok((14, 0, 0)));
assert_eq!(pa_version_str_to_num("14.1.0"), Ok((14, 1, 0)));
assert_eq!(pa_version_str_to_num("14.2.0."), Err(ErrorKind::ExtraParts));
assert_eq!(pa_version_str_to_num("14.2.0.0"), Err(ErrorKind::ExtraParts));
assert_eq!(pa_version_str_to_num("12.2a"), Err(ErrorKind::ParseIntError));
assert_eq!(pa_version_str_to_num("12.a"), Err(ErrorKind::ParseIntError));
assert_eq!(pa_version_str_to_num("12.a.1"), Err(ErrorKind::ParseIntError));
}
#[test]
fn test_getting_pa_version() {
let actual_ver_str =
unsafe { CStr::from_ptr(capi::pa_get_library_version()).to_string_lossy() };
let (major, minor, micro) = get_library_version_numbers().unwrap();
assert_eq!(format!("{}.{}.{}", major, minor, micro), actual_ver_str);
}
#[test]
fn test_comparing_pa_version() {
let (major, minor, _micro) = get_library_version_numbers().unwrap();
assert_eq!(compare_with_library_version(major, minor).unwrap(), Ordering::Equal);
assert_eq!(compare_with_library_version(major + 1, minor).unwrap(), Ordering::Greater);
assert_eq!(compare_with_library_version(major - 1, minor).unwrap(), Ordering::Less);
assert_eq!(compare_with_library_version(major, minor + 1).unwrap(), Ordering::Greater);
assert_eq!(compare_with_library_version(major - 1, minor + 1).unwrap(), Ordering::Less);
if minor > 0 {
assert_eq!(compare_with_library_version(major, minor - 1).unwrap(), Ordering::Less);
assert_eq!(compare_with_library_version(major + 1, minor - 1).unwrap(), Ordering::Greater);
}
}
#[test]
fn test_lib_ver_not_too_old() {
assert_eq!(library_version_is_too_old(), Ok(false));
}