use std::path::Path;
use std::sync::Arc;
use anyhow::{Context, Result};
use libloading::Library;
pub type PluginEntryPointRaw = unsafe extern "C" fn() -> std::ffi::c_void;
#[derive(Clone)]
pub struct DynLib {
library: Arc<Library>,
path: std::path::PathBuf,
}
impl DynLib {
pub unsafe fn load(path: &Path) -> Result<Self> {
let library = unsafe { Library::new(path) }
.with_context(|| format!("failed to load dynamic library: {}", path.display()))?;
Ok(Self {
library: Arc::new(library),
path: path.to_path_buf(),
})
}
pub fn path(&self) -> &Path {
&self.path
}
pub unsafe fn symbol<T: Copy>(&self, name: &[u8]) -> Result<T> {
let sym = unsafe { self.library.get::<T>(name) }.with_context(|| {
format!(
"symbol '{}' not found in {}",
display_symbol(name),
self.path.display()
)
})?;
Ok(*sym)
}
pub unsafe fn try_symbol<T: Copy>(&self, name: &[u8]) -> Option<T> {
unsafe { self.library.get::<T>(name) }.ok().map(|s| *s)
}
}
pub(crate) fn display_symbol(symbol: &[u8]) -> String {
let end = symbol.iter().position(|&b| b == 0).unwrap_or(symbol.len());
String::from_utf8_lossy(&symbol[..end]).into_owned()
}
pub unsafe fn looks_like_plugin(path: &Path, entry_symbol: &[u8]) -> bool {
match unsafe { DynLib::load(path) } {
Ok(lib) => unsafe { lib.try_symbol::<PluginEntryPointRaw>(entry_symbol) }.is_some(),
Err(_) => false,
}
}