use std::{
error::Error,
ffi::OsStr,
ops::{Deref, DerefMut},
path::Path, mem::ManuallyDrop,
};
use libloading::Library;
use crate::ShellState;
pub trait Plugin: Send + Sync {
fn init(&self) -> Box<dyn Plugin>;
fn name(&self) -> &str;
fn call(&self, _state: *mut ShellState) -> Result<(), Box<dyn Error + Send + Sync>> {
Ok(())
}
}
pub struct PluginHook {
hook: libloading::Library,
obj: ManuallyDrop<Box<dyn Plugin>>,
path: String,
}
impl Deref for PluginHook {
type Target = Box<dyn Plugin>;
fn deref(&self) -> &Self::Target {
&self.obj
}
}
impl PluginHook {
pub unsafe fn new<S: AsRef<OsStr>>(path: S) -> Result<Self, libloading::Error> {
let str_path = OsStr::new(&path)
.to_str()
.map(|s| s.to_owned())
.unwrap_or("".to_owned());
let hook = Library::new(path)?;
let obj = hook.get::<libloading::Symbol<fn() -> Box<dyn Plugin>>>(b"init")?();
Ok(Self {
hook,
obj: ManuallyDrop::new(obj),
path: str_path,
})
}
}
impl Drop for PluginHook {
fn drop(&mut self) {
unsafe { ManuallyDrop::drop(&mut self.obj) };
}
}
#[cfg(test)]
mod tests {
use crate::PluginHook;
#[test]
fn drop() {
let hook = unsafe { PluginHook::new("/home/jamie/.config/zula/plugins/libtest_plugin.so") }.unwrap();
}
}