2022-06-04 11:58:21 +00:00
|
|
|
use std::{any::Any, fs, sync::Arc};
|
|
|
|
|
2022-06-04 18:10:21 +00:00
|
|
|
use async_trait::async_trait;
|
2022-06-04 11:58:21 +00:00
|
|
|
use libloading::{Library, Symbol};
|
|
|
|
use log::{debug, trace};
|
|
|
|
|
2022-06-04 18:10:21 +00:00
|
|
|
use crate::{commands, Command, CommandManager, CommandManagerType};
|
2022-06-04 11:58:21 +00:00
|
|
|
|
|
|
|
/// A plugin which allows you to add extra functionality.
|
2022-06-04 18:10:21 +00:00
|
|
|
#[async_trait]
|
2022-06-04 11:58:21 +00:00
|
|
|
pub trait Plugin: Any + Send + Sync {
|
|
|
|
/// Get a name describing the `Plugin`.
|
|
|
|
fn name(&self) -> &'static str;
|
|
|
|
/// A function that runs immediately after plugin loading.
|
|
|
|
/// Usually used for initialization.
|
2022-06-04 18:10:21 +00:00
|
|
|
async fn on_plugin_load(&self);
|
2022-06-04 11:58:21 +00:00
|
|
|
/// A function that runs immediately before the plugin is unloaded.
|
|
|
|
/// Use this if you want to do any cleanup.
|
2022-06-04 18:10:21 +00:00
|
|
|
async fn on_plugin_unload(&self);
|
2022-06-04 11:58:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PluginManager {
|
|
|
|
/// Vector with loaded plugins
|
|
|
|
pub plugins: Vec<Box<dyn Plugin>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PluginManager {
|
|
|
|
/// Create empty `PluginManager`
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
plugins: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Unload all plugins and loaded plugin libraries, making sure to fire
|
|
|
|
/// their `on_plugin_unload()` methods so they can do any necessary cleanup.
|
2022-06-04 18:10:21 +00:00
|
|
|
pub async fn unload(&mut self) {
|
2022-06-04 11:58:21 +00:00
|
|
|
debug!("Unloading plugins");
|
|
|
|
|
|
|
|
for plugin in self.plugins.drain(..) {
|
|
|
|
trace!("Firing on_plugin_unload for {:?}", plugin.name());
|
2022-06-04 18:10:21 +00:00
|
|
|
plugin.on_plugin_unload().await;
|
2022-06-04 11:58:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for PluginManager {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait PluginRegistrar {
|
|
|
|
fn register_plugin(&mut self, plugin: Box<dyn Plugin>);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PluginRegistrar for PluginManager {
|
|
|
|
fn register_plugin(&mut self, plugin: Box<dyn Plugin>) {
|
|
|
|
self.plugins.push(plugin)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-04 18:10:21 +00:00
|
|
|
pub trait CommandRegistrar {
|
|
|
|
fn register_plugin(&mut self, command: Box<dyn Command>);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CommandRegistrar for CommandManager {
|
|
|
|
fn register_plugin(&mut self, command: Box<dyn Command>) {
|
|
|
|
self.commands.push(command)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type PluginManagerType = Arc<Vec<Box<dyn Plugin>>>;
|
2022-06-04 11:58:21 +00:00
|
|
|
|
2022-06-04 18:10:21 +00:00
|
|
|
pub fn loader() -> anyhow::Result<(PluginManagerType, CommandManagerType)> {
|
2022-06-04 11:58:21 +00:00
|
|
|
// get path to .so lib from command argument
|
|
|
|
let config_dir = "./plugins";
|
|
|
|
let paths = fs::read_dir(config_dir)?;
|
|
|
|
|
|
|
|
// create a plugin manager where all loaded plugins will be located
|
|
|
|
let mut plugin_manager = PluginManager::new();
|
|
|
|
|
2022-06-04 18:10:21 +00:00
|
|
|
// create a command manager where located all commands
|
|
|
|
let mut command_manager = CommandManager::new();
|
|
|
|
|
|
|
|
// register default commands
|
|
|
|
for command in commands::register_commands() {
|
|
|
|
command_manager.commands.push(command)
|
|
|
|
}
|
|
|
|
|
2022-06-04 11:58:21 +00:00
|
|
|
// for all plugin in directory
|
|
|
|
for path in paths {
|
|
|
|
// get library file path
|
|
|
|
let path = path?.path();
|
|
|
|
|
|
|
|
// loading library with .so is unsafe
|
|
|
|
unsafe {
|
|
|
|
// load library
|
|
|
|
// Box::new and Box::leak must be there because if it isn't there it throws a segmentation fault
|
|
|
|
let lib = Box::leak(Box::new(Library::new(path)?));
|
|
|
|
|
|
|
|
// get `plugin_entry` from library
|
2022-06-04 18:10:21 +00:00
|
|
|
let func: Symbol<
|
|
|
|
unsafe extern "C" fn(&mut dyn PluginRegistrar, &mut dyn CommandRegistrar) -> (),
|
|
|
|
> = lib.get(b"plugin_entry")?;
|
2022-06-04 11:58:21 +00:00
|
|
|
|
|
|
|
// execute initial function
|
2022-06-04 18:10:21 +00:00
|
|
|
func(&mut plugin_manager, &mut command_manager);
|
2022-06-04 11:58:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create Arc in Vector
|
2022-06-04 18:10:21 +00:00
|
|
|
let mut commands = Vec::new();
|
|
|
|
for command in command_manager.commands {
|
|
|
|
commands.push(command)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create Arc in Vector
|
|
|
|
let mut plugins = Vec::new();
|
2022-06-04 11:58:21 +00:00
|
|
|
for plugin in plugin_manager.plugins {
|
2022-06-04 18:10:21 +00:00
|
|
|
plugins.push(plugin)
|
2022-06-04 11:58:21 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 18:10:21 +00:00
|
|
|
Ok((Arc::new(plugins), Arc::new(commands)))
|
2022-06-04 11:58:21 +00:00
|
|
|
}
|