1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use libloading::{Library, Symbol};
use std::{any::Any, ffi::OsStr};

#[cfg_attr(not(feature = "nightly"), macro_use)]
#[cfg(not(feature = "nightly"))]
extern crate failure;

#[cfg(not(feature = "nightly"))]
extern crate libloading;

#[cfg(all(not(feature = "nightly"), feature = "hashbrown"))]
extern crate hashbrown;

#[cfg(all(not(feature = "nightly"), feature = "fx"))]
extern crate fxhash;

#[cfg(feature = "swisstable")]
use hashbrown::HashMap;
#[cfg(feature = "fx")]
use fxhash::FxHashMap as HashMap;
#[cfg(all(not(feature = "swisstable"), not(feature = "fx")))]
use std::collections::HashMap;

pub mod error;

pub trait Plugin: Any + Send + Sync {
    fn load(&mut self) {}
    fn unload(&mut self) {}
}

#[macro_export]
macro_rules! declare_plugin {
    ($type:ty, $instance:expr) => {
        #[no_mangle]
        pub extern "C" fn _plugin_create() -> *mut $crate::Plugin {
            let object: $type = $instance;
            let boxed: Box<$crate::Plugin> = Box::new(object);
            Box::into_raw(boxed)
        }
    };
}

pub struct PluginManager {
    plugins: HashMap<String, Box<Plugin>>,
    libraries: HashMap<String, Library>,
}

impl PluginManager {
    #[inline]
    pub fn new() -> Self {
        PluginManager {
            plugins: HashMap::default(),
            libraries: HashMap::default(),
        }
    }

    pub fn get_by_file_name(&self, name: &String) -> Option<&Box<Plugin>> {
        self.plugins.get(name)
    }

    pub unsafe fn get_library_by_file_name(&self, name: &String) -> Option<&Library> {
        self.libraries.get(name)
    }

    pub fn load_plugin<P>(&mut self, path: P) -> error::Result<()>
    where
        P: AsRef<OsStr>,
    {
        let file = path.as_ref();
        let file_str = match file.to_str().map(|s| s.to_owned()) {
            Some(s) => s,
            None => return Err(error::PluginError::UndecodableName.into()),
        };
        let library = Library::new(file)?;
        self.libraries.insert(file_str.clone(), library);

        let mut instance = unsafe {
            let func: Symbol<fn() -> *mut Plugin> = self
                .libraries
                .get(&file_str)
                .unwrap()
                .get(b"_plugin_create")?;
            Box::from_raw(func())
        };

        instance.load();
        self.plugins.insert(file_str, instance);

        Ok(())
    }

    pub fn unload_plugins(&mut self) {
        for (_, mut plugin) in self.plugins.drain() {
            plugin.unload();
            drop(plugin);
        }
        for (_, lib) in self.libraries.drain() {
            drop(lib);
        }
    }
}

impl Drop for PluginManager {
    fn drop(&mut self) {
        self.unload_plugins();
    }
}