1use std::{
2 error::Error,
3 ffi::OsStr,
4 ops::{Deref, DerefMut},
5 path::Path, mem::ManuallyDrop,
6};
7
8use libloading::Library;
9
10use crate::ShellState;
11
12pub trait Plugin: Send + Sync {
14 fn init(&self) -> Box<dyn Plugin>;
17 fn name(&self) -> &str;
20 fn call(&self, _state: *mut ShellState) -> Result<(), Box<dyn Error + Send + Sync>> {
22 Ok(())
23 }
24}
25
26pub struct PluginHook {
28 hook: libloading::Library,
29 obj: ManuallyDrop<Box<dyn Plugin>>,
30 path: String,
31}
32
33impl Deref for PluginHook {
34 type Target = Box<dyn Plugin>;
35
36 fn deref(&self) -> &Self::Target {
37 &self.obj
38 }
39}
40
41impl PluginHook {
42 pub unsafe fn new<S: AsRef<OsStr>>(path: S) -> Result<Self, libloading::Error> {
43 let str_path = OsStr::new(&path)
44 .to_str()
45 .map(|s| s.to_owned())
46 .unwrap_or("".to_owned());
47 let hook = Library::new(path)?;
48 let obj = hook.get::<libloading::Symbol<fn() -> Box<dyn Plugin>>>(b"init")?();
49 Ok(Self {
50 hook,
51 obj: ManuallyDrop::new(obj),
52 path: str_path,
53 })
54 }
55}
56
57impl Drop for PluginHook {
58 fn drop(&mut self) {
59 unsafe { ManuallyDrop::drop(&mut self.obj) };
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use crate::PluginHook;
66
67 #[test]
68 fn drop() {
69 let hook = unsafe { PluginHook::new("/home/jamie/.config/zula/plugins/libtest_plugin.so") }.unwrap();
70 }
71}