use once_cell::sync::Lazy;
use std::mem::MaybeUninit;
use crate::ffi_utils;
pub use version_error_enum::*;
mod version_error_enum;
pub static VERSION_STRING: Lazy<&str> = Lazy::new(|| libfdisk::LIBFDISK_VERSION.to_str().unwrap());
pub static VERSION_NUMBER_MAJOR: u32 = libfdisk::LIBFDISK_MAJOR_VERSION as u32;
pub static VERSION_NUMBER_MINOR: u32 = libfdisk::LIBFDISK_MINOR_VERSION as u32;
pub static VERSION_NUMBER_PATCH: u32 = libfdisk::LIBFDISK_PATCH_VERSION as u32;
pub fn version_string_to_release_code<T>(version_string: T) -> Result<i32, VersionError>
where
T: AsRef<str>,
{
unsafe {
let version_string = version_string.as_ref();
let version_cstr = ffi_utils::as_ref_str_to_c_string(version_string).map_err(|e| {
let err_msg = format!("failed to convert value to `CString` {e}");
VersionError::CStringConversion(err_msg)
})?;
let version_code = libfdisk::fdisk_parse_version_string(version_cstr.as_ptr());
log::debug!("version::version_string_to_release_code converted version string {:?} to release code {:?}", version_string, version_code);
Ok(version_code)
}
}
pub fn library_features() -> Result<Vec<String>, VersionError> {
log::debug!("version::library_features getting list of library features");
let mut c_feature_array = MaybeUninit::<*mut *const libc::c_char>::zeroed();
let mut array_len = MaybeUninit::<libc::c_int>::zeroed();
unsafe {
array_len.write(libfdisk::fdisk_get_library_features(
c_feature_array.as_mut_ptr(),
));
}
match unsafe { array_len.assume_init() } {
len if len < 0 => {
let err_msg = "failed to get library features".to_owned();
log::debug!("version::library_features {}. libfdisk::fdisk_get_library_features returned error code: {:?}", err_msg, len);
Err(VersionError::FeaturesAccess(err_msg))
}
len => {
let c_feature_array = unsafe { c_feature_array.assume_init() };
let feature_array =
unsafe { std::slice::from_raw_parts(c_feature_array, len as usize) };
let features: Vec<_> = feature_array
.iter()
.map(|&feat| ffi_utils::c_char_array_to_string(feat))
.collect();
log::debug!("version::library_features value: {:?}", features);
Ok(features)
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[should_panic]
fn version_string_to_release_code_fails_on_invalid_c_string() {
let version_string = String::from_utf8(b"2.39\0.4".to_vec()).unwrap();
let _result = version_string_to_release_code(version_string).unwrap();
}
#[test]
fn version_string_to_release_code_converts_up_to_first_invalid_character() {
let version_string = "v2.39.4";
let expected: i32 = 0;
let result = version_string_to_release_code(version_string).unwrap();
assert_eq!(result, expected);
let version_string = "2.39.x";
let expected: i32 = 239;
let result = version_string_to_release_code(version_string).unwrap();
assert_eq!(result, expected);
}
}