use crate::{res, HandlerResponse, Loader, Method, Plugin, Request, RequestHandler, StatusCode}; use path_tree::PathTree; use serde_json as json; use std::collections::HashMap; use std::iter::Iterator; use std::sync::{Arc, Mutex}; /// Plugin to keep track of registered plugins pub(crate) struct PluginRegistry { plugins: Mutex)>>, routes: Mutex>, } impl PluginRegistry { const NAME: &'static str = "plugins"; pub fn new() -> Arc { Arc::new(PluginRegistry { plugins: Mutex::new(HashMap::new()), routes: Mutex::new(PathTree::new()), }) } pub fn match_plugin_handler(&self, path: &str) -> Option<(Plugin, Arc)> { let routes = self.routes.lock().unwrap(); let plugins = self.plugins.lock().unwrap(); let (name, _) = routes.find(path)?; let (plugin, handler) = plugins.get(name)?; Some((plugin.clone(), handler.clone())) } pub fn register(&self, plugin: Plugin, handler: Box) { let mut routes = self.routes.lock().unwrap(); let mut plugins = self.plugins.lock().unwrap(); routes.insert(&plugin.prefix(), plugin.name()); plugins.insert(plugin.name().into(), (plugin, handler.into())); } fn plugin_list(&self) -> Vec { self.plugins .lock() .unwrap() .values() .map(|(p, _)| p.clone()) .collect() } pub fn as_handler( self: Arc, loader: Arc, ) -> (Plugin, Box) { ( Plugin::BuiltIn { name: Self::NAME.into(), }, Box::new(move |mut req: Request| { let registry = self.clone(); let loader = loader.clone(); Box::pin(async move { match req.method() { Method::Get => { let plugins = registry.plugin_list(); json::to_vec(&plugins) .map_or(res(StatusCode::InternalServerError, ""), |list| { list.into() }) } Method::Post => match req.body_json().await { Ok(plugin) => match loader.load(&plugin) { Ok(handler) => { registry.register(plugin, handler); res(StatusCode::Created, "") } Err(_) => res(StatusCode::UnprocessableEntity, ""), }, Err(_) => res(StatusCode::BadRequest, ""), }, _ => res(StatusCode::MethodNotAllowed, ""), } }) as HandlerResponse }), ) } }