use std::ffi::OsStr;
use devrc_core::{logging::LogLevel, workshop::Designer};
use libloading::{Library, Symbol};
use crate::{
config::{Config, ExecutionConfig},
errors::{DevrcPluginError, DevrcPluginResult},
plugin::Plugin,
};
pub trait ExecutionPlugin: Plugin {
fn execute(
&self,
execution_config: ExecutionConfig,
code: &str,
environment: &indexmap::IndexMap<String, String>,
) -> DevrcPluginResult<i32>;
}
#[macro_export]
macro_rules! declare_execution_plugin {
($plugin_type:ty, $constructor:path) => {
#[no_mangle]
pub extern "C" fn _plugin_create() -> *mut $crate::ExecutionPlugin {
let constructor: fn() -> $plugin_type = $constructor;
let object = constructor();
let boxed: Box<$crate::ExecutionPlugin> = Box::new(object);
Box::into_raw(boxed)
}
};
}
#[derive(Default)]
pub struct ExecutionPluginManager {
plugins: Vec<(String, Box<dyn ExecutionPlugin>)>,
loaded_libraries: Vec<Library>,
designer: Designer,
logger: LogLevel,
}
impl std::fmt::Debug for ExecutionPluginManager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PluginManager").finish()
}
}
impl ExecutionPluginManager {
pub fn new() -> ExecutionPluginManager {
ExecutionPluginManager {
plugins: Vec::new(),
loaded_libraries: Vec::new(),
designer: Designer::default(),
logger: LogLevel::default(),
}
}
pub fn setup_logger(&mut self, logger: LogLevel) {
self.logger = logger;
}
pub unsafe fn load_plugin<P: AsRef<OsStr>>(
&mut self,
name: &str,
filename: P,
logger: LogLevel,
) -> DevrcPluginResult<()> {
type PluginCreate = unsafe fn() -> *mut dyn ExecutionPlugin;
let lib = Library::new(filename.as_ref())?;
self.loaded_libraries.push(lib);
let lib = self.loaded_libraries.last().unwrap();
let constructor: Symbol<PluginCreate> = lib.get(b"_plugin_create")?;
let boxed_raw = constructor();
let mut plugin = Box::from_raw(boxed_raw);
logger.debug(
&format!(
"\n==> Loading PLUGIN: `{}` as `{}` from `{:?}` ...",
plugin.name(),
name,
filename.as_ref()
),
&self.designer.banner(),
);
plugin.on_plugin_load(Config {
logger,
designer: self.designer,
});
self.plugins.push((name.to_string(), plugin));
Ok(())
}
pub fn unload(&mut self) {
self.logger
.debug("\n==> Unloading PLUGINS ...", &self.designer.banner());
for (name, plugin) in self.plugins.drain(..) {
self.logger.debug(
&format!("\n==> Upload PLUGIN: `{}` named `{}` ", plugin.name(), name),
&self.designer.banner(),
);
plugin.on_plugin_unload();
}
for lib in self.loaded_libraries.drain(..) {
drop(lib);
}
}
pub fn get_plugin(
&mut self,
plugin_name: &str,
) -> DevrcPluginResult<&Box<dyn ExecutionPlugin>> {
for (name, plugin) in &self.plugins {
if name == plugin_name {
return Ok(plugin);
}
}
Err(DevrcPluginError::NotFound(plugin_name.to_string()))
}
}
impl Drop for ExecutionPluginManager {
fn drop(&mut self) {
if !self.plugins.is_empty() || !self.loaded_libraries.is_empty() {
self.unload();
}
}
}