use crate::config::RouteConfig;
use crate::plugins::plugin::{Plugin, PluginLoadResult};
use crate::server::server::AppState;
use humphrey::http::{Request, Response};
use humphrey::stream::Stream;
use libloading::Library;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Default)]
pub struct PluginManager {
plugins: Vec<Box<dyn Plugin>>,
libraries: Vec<Library>,
}
impl PluginManager {
pub unsafe fn load_plugin(
&mut self,
path: &str,
config: &HashMap<String, String>,
state: Arc<AppState>,
) -> PluginLoadResult<String, &'static str> {
type PluginInitFunction = unsafe extern "C" fn() -> *mut dyn Plugin;
if let Ok(library) = Library::new(path) {
self.libraries.push(library);
let library = self.libraries.last().unwrap();
if let Ok(init_function) = library.get::<PluginInitFunction>(b"_plugin_init") {
let boxed_raw = init_function();
let mut plugin = Box::from_raw(boxed_raw);
let result = plugin.on_load(config, state);
result.map(|_| {
let name = plugin.name().to_string();
self.plugins.push(plugin);
name
})
} else {
PluginLoadResult::Fatal(
"Couldn't find plugin initialisation function in the library",
)
}
} else {
PluginLoadResult::Fatal("Couldn't load dynamic library")
}
}
pub fn on_request(
&self,
request: &mut Request,
state: Arc<AppState>,
route: &RouteConfig,
) -> Option<Response> {
for plugin in &self.plugins {
if let Some(response) = plugin.on_request(request, state.clone(), route) {
return Some(response);
}
}
None
}
pub fn on_websocket_request(
&self,
request: &mut Request,
mut stream: Stream,
state: Arc<AppState>,
route: Option<&RouteConfig>,
) -> Option<Stream> {
for plugin in &self.plugins {
if let Some(returned_stream) =
plugin.on_websocket_request(request, stream, state.clone(), route)
{
stream = returned_stream;
} else {
return None;
}
}
Some(stream)
}
pub fn on_response(&self, response: &mut Response, state: Arc<AppState>, route: &RouteConfig) {
for plugin in &self.plugins {
plugin.on_response(response, state.clone(), route);
}
}
pub fn unload(&mut self) {
self.plugins
.iter_mut()
.for_each(|plugin| plugin.on_unload());
for library in self.libraries.drain(..) {
drop(library);
}
}
pub fn plugin_count(&self) -> usize {
self.plugins.len()
}
}
impl Drop for PluginManager {
fn drop(&mut self) {
if !self.plugins.is_empty() || !self.libraries.is_empty() {
self.unload();
}
}
}