pub use libloading;
#[macro_export]
macro_rules! lib_name {
($name:expr) => {
#[no_mangle]
pub fn name() -> &'static str {
$name
}
}
}
#[macro_export]
macro_rules! plugin_registry {
($( fn $name:ident($( $param_name:ident: $param_ty:ty ),*) -> $ty:ty; )*) => {
#[derive(Default)]
pub struct Registry(Plugins);
#[derive(Default)]
pub struct Plugins {
_libs: ::std::collections::HashMap<String, $crate::libloading::Library>,
$( $name: Vec<String> ),*
}
impl Plugins {
$(
pub fn $name(&self) -> impl
ExactSizeIterator<Item=impl '_ + Fn($( $param_ty ),*) -> $ty>
{
self.$name.iter().map(move |name| {
let symbol = unsafe {
self._libs[name].get::<fn($( $param_ty ),*) -> $ty>(stringify!($name).as_bytes())
}.expect(concat!("symbol not found: ", stringify!($name)));
move |$( $param_name: $param_ty ),*| symbol($( $param_name ),*)
})
}
)*
}
impl Registry {
pub fn load<'a>(&mut self, lib: &str) -> ::std::io::Result<()> {
let lib = $crate::libloading::Library::new(lib)?;
let name = unsafe { lib.get::<fn() -> &'a str>(b"name")?() }.to_owned();
$(
if let Ok(_) = unsafe { lib.get::<fn($( $param_ty ),*) -> $ty>(stringify!($name).as_bytes()) } {
(self.0).$name.push(name.clone());
}
)*
self.0._libs.insert(name, lib);
Ok(())
}
pub fn unload(&mut self, name: &str) {
$( (self.0).$name.retain(|lib| lib != name); )*
self.0._libs.remove(name);
}
pub fn plugins(&self) -> &Plugins {
&self.0
}
}
#[macro_export]
macro_rules! plugin {
$(
($name: $func:path) => {
#[no_mangle]
pub fn $name($( $param_name: $param_ty ),*) -> $ty {
$func($( $param_name ),*)
}
}
);*
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn types() {
plugin_registry!(
fn is_empty(x: &str) -> bool;
);
lib_name!("dyplugin");
plugin!(is_empty: plugin);
fn plugin(x: &str) -> bool {
x.is_empty()
}
let reg = Registry::default();
assert_eq!(reg.plugins().is_empty().len(), 0);
let is_empty = reg.plugins().is_empty().last();
match is_empty {
Some(is_empty) => assert_eq!(is_empty(""), true),
None => ()
}
}
}