1use libloading::{Library, Symbol};
2use std::{any::Any, ffi::OsStr};
3
4#[cfg_attr(not(feature = "nightly"), macro_use)]
5#[cfg(not(feature = "nightly"))]
6extern crate failure;
7
8#[cfg(not(feature = "nightly"))]
9extern crate libloading;
10
11#[cfg(all(not(feature = "nightly"), feature = "hashbrown"))]
12extern crate hashbrown;
13
14#[cfg(all(not(feature = "nightly"), feature = "fx"))]
15extern crate fxhash;
16
17#[cfg(feature = "swisstable")]
18use hashbrown::HashMap;
19#[cfg(feature = "fx")]
20use fxhash::FxHashMap as HashMap;
21#[cfg(all(not(feature = "swisstable"), not(feature = "fx")))]
22use std::collections::HashMap;
23
24pub mod error;
25
26pub trait Plugin: Any + Send + Sync {
27 fn load(&mut self) {}
28 fn unload(&mut self) {}
29}
30
31#[macro_export]
32macro_rules! declare_plugin {
33 ($type:ty, $instance:expr) => {
34 #[no_mangle]
35 pub extern "C" fn _plugin_create() -> *mut $crate::Plugin {
36 let object: $type = $instance;
37 let boxed: Box<$crate::Plugin> = Box::new(object);
38 Box::into_raw(boxed)
39 }
40 };
41}
42
43pub struct PluginManager {
44 plugins: HashMap<String, Box<Plugin>>,
45 libraries: HashMap<String, Library>,
46}
47
48impl PluginManager {
49 #[inline]
50 pub fn new() -> Self {
51 PluginManager {
52 plugins: HashMap::default(),
53 libraries: HashMap::default(),
54 }
55 }
56
57 pub fn get_by_file_name(&self, name: &String) -> Option<&Box<Plugin>> {
58 self.plugins.get(name)
59 }
60
61 pub unsafe fn get_library_by_file_name(&self, name: &String) -> Option<&Library> {
62 self.libraries.get(name)
63 }
64
65 pub fn load_plugin<P>(&mut self, path: P) -> error::Result<()>
66 where
67 P: AsRef<OsStr>,
68 {
69 let file = path.as_ref();
70 let file_str = match file.to_str().map(|s| s.to_owned()) {
71 Some(s) => s,
72 None => return Err(error::PluginError::UndecodableName.into()),
73 };
74 let library = Library::new(file)?;
75 self.libraries.insert(file_str.clone(), library);
76
77 let mut instance = unsafe {
78 let func: Symbol<fn() -> *mut Plugin> = self
79 .libraries
80 .get(&file_str)
81 .unwrap()
82 .get(b"_plugin_create")?;
83 Box::from_raw(func())
84 };
85
86 instance.load();
87 self.plugins.insert(file_str, instance);
88
89 Ok(())
90 }
91
92 pub fn unload_plugins(&mut self) {
93 for (_, mut plugin) in self.plugins.drain() {
94 plugin.unload();
95 drop(plugin);
96 }
97 for (_, lib) in self.libraries.drain() {
98 drop(lib);
99 }
100 }
101}
102
103impl Drop for PluginManager {
104 fn drop(&mut self) {
105 self.unload_plugins();
106 }
107}