use ferroui_base::platform::native_library::{NativeLibrary, NativeLibraryError};
use std::ffi::CStr;
pub(crate) fn load_library<T>(
library: &'static CStr,
load: impl FnOnce(&NativeLibrary) -> Result<T, NativeLibraryError>,
) -> Result<T, String> {
let library = NativeLibrary::open_with_flags(library, libc::RTLD_NOW | libc::RTLD_LOCAL).map_err(message)?;
load(&library).map_err(message)
}
fn message(error: NativeLibraryError) -> String {
match error {
NativeLibraryError::EntryPoint { library, symbol } => format!("{library} does not export {symbol}"),
error @ NativeLibraryError::Library { .. } => error.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_library_that_does_not_exist_is_an_error_with_its_name() {
let error = load_library(c"libferroui-there-is-no-such-library.so.0", |_| Ok(())).unwrap_err();
assert_eq!(
error,
"Unable to load shared library 'libferroui-there-is-no-such-library.so.0' or one of its dependencies."
);
}
#[test]
fn a_function_that_is_missing_is_an_error_with_its_name() {
ferroui_base::native_functions! {
struct Table(lib) {
lib::getpid: unsafe extern "C" fn() -> libc::pid_t;
}
}
let table = load_library(c"libc.so.6", Table::load).unwrap();
assert_eq!(unsafe { (table.getpid)() } as u32, std::process::id());
ferroui_base::native_functions! {
struct Missing(lib) {
lib::ferroui_no_such_function: unsafe extern "C" fn();
}
}
let error = load_library(c"libc.so.6", Missing::load).err().unwrap();
assert_eq!(error, "libc.so.6 does not export ferroui_no_such_function");
}
}