use std::collections::HashMap;
use std::sync::Arc;
use crate::ProxyError;
pub type ModuleResult<T> = Result<T, ProxyError>;
pub trait ModuleLoader: Send + Sync {
fn name(&self) -> &str;
fn validate_config(&self, config: &HashMap<String, String>) -> ModuleResult<()> {
let _ = config;
Ok(())
}
fn create_middleware(
&self,
config: &HashMap<String, String>,
) -> ModuleResult<Option<Arc<dyn salvo::Handler>>> {
let _ = config;
Ok(None)
}
fn create_handler(
&self,
config: &HashMap<String, String>,
) -> ModuleResult<Option<Arc<dyn salvo::Handler>>> {
let _ = config;
Ok(None)
}
fn on_load(&self) -> ModuleResult<()> {
Ok(())
}
fn on_unload(&self) -> ModuleResult<()> {
Ok(())
}
fn on_reload(&self, config: &HashMap<String, String>) -> ModuleResult<()> {
let _ = config;
Ok(())
}
}
pub struct ModuleRegistry {
loaders: HashMap<String, Box<dyn ModuleLoader>>,
}
impl ModuleRegistry {
pub fn new() -> Self {
Self {
loaders: HashMap::new(),
}
}
pub fn register(&mut self, loader: Box<dyn ModuleLoader>) {
let name = loader.name().to_string();
tracing::info!(module = %name, "registered module loader");
self.loaders.insert(name, loader);
}
pub fn get(&self, name: &str) -> Option<&dyn ModuleLoader> {
self.loaders.get(name).map(|b| b.as_ref())
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &dyn ModuleLoader)> {
self.loaders.iter().map(|(k, v)| (k.as_str(), v.as_ref()))
}
pub fn load_all(&self) -> ModuleResult<()> {
for (name, loader) in &self.loaders {
loader.on_load().map_err(|e| {
ProxyError::Internal(format!("module '{name}' on_load failed: {e}"))
})?;
}
Ok(())
}
pub fn unload_all(&self) {
for (name, loader) in &self.loaders {
if let Err(e) = loader.on_unload() {
tracing::warn!(module = %name, error = %e, "module on_unload failed");
}
}
}
pub fn reload_all(&self, configs: &HashMap<String, HashMap<String, String>>) {
for (name, loader) in &self.loaders {
if let Some(cfg) = configs.get(name.as_str())
&& let Err(e) = loader.on_reload(cfg)
{
tracing::warn!(module = %name, error = %e, "module on_reload failed");
}
}
}
pub fn len(&self) -> usize {
self.loaders.len()
}
pub fn is_empty(&self) -> bool {
self.loaders.is_empty()
}
}
impl Default for ModuleRegistry {
fn default() -> Self {
Self::new()
}
}