use std::{
ffi::OsStr,
path::{Path, PathBuf},
sync::Arc,
};
use libloading::{Library, Symbol};
use thiserror::Error;
use crate::{CreatePluginFn, VanguardPlugin, CREATE_PLUGIN_SYMBOL};
#[derive(Error, Debug)]
pub enum DylibError {
#[error("Failed to load library: {0}")]
LoadError(#[from] libloading::Error),
#[error("Failed to find plugin entry point: {0}")]
EntryPointNotFound(String),
#[error("Invalid plugin library: {0}")]
InvalidLibrary(String),
}
pub struct PluginLibrary {
_lib: Library, plugin: Arc<dyn VanguardPlugin>,
}
impl PluginLibrary {
pub unsafe fn load<P: AsRef<OsStr>>(path: P) -> Result<Self, DylibError> {
let lib = Library::new(path).map_err(DylibError::LoadError)?;
let create_plugin: Symbol<CreatePluginFn> = lib
.get(CREATE_PLUGIN_SYMBOL)
.map_err(|_| DylibError::EntryPointNotFound("create_plugin".into()))?;
let raw_plugin = create_plugin();
if raw_plugin.is_null() {
return Err(DylibError::InvalidLibrary(
"Plugin creation returned null".into(),
));
}
let raw_plugin = Box::from_raw(raw_plugin);
let plugin = Arc::from(raw_plugin.instance);
Ok(Self { _lib: lib, plugin })
}
pub fn plugin(&self) -> Arc<dyn VanguardPlugin> {
self.plugin.clone()
}
}
pub fn get_dylib_name(name: &str) -> String {
#[cfg(target_os = "windows")]
{
format!("{}.dll", name)
}
#[cfg(target_os = "linux")]
{
format!("lib{}.so", name)
}
#[cfg(target_os = "macos")]
{
format!("lib{}.dylib", name)
}
}
pub fn get_dylib_path(plugin_dir: &Path, name: &str) -> PathBuf {
plugin_dir.join("lib").join(get_dylib_name(name))
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_dylib_name() {
let name = get_dylib_name("test-plugin");
#[cfg(target_os = "windows")]
assert_eq!(name, "test-plugin.dll");
#[cfg(target_os = "linux")]
assert_eq!(name, "libtest-plugin.so");
#[cfg(target_os = "macos")]
assert_eq!(name, "libtest-plugin.dylib");
}
#[test]
fn test_dylib_path() {
let temp_dir = TempDir::new().unwrap();
let path = get_dylib_path(temp_dir.path(), "test-plugin");
let expected = temp_dir
.path()
.join("lib")
.join(get_dylib_name("test-plugin"));
assert_eq!(path, expected);
}
}