use async_trait::async_trait;
use crate::error::Result;
#[async_trait]
pub trait Module: Send + Sync {
fn name(&self) -> &str;
fn depends_on(&self) -> Vec<&str> {
vec![]
}
async fn on_start(&self) -> Result<()> {
Ok(())
}
async fn after_start(&self) -> Result<()> {
Ok(())
}
async fn before_stop(&self) -> Result<()> {
Ok(())
}
async fn on_stop(&self) -> Result<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Phase {
Config = 0,
Logging = 1,
Database = 2,
Cache = 3,
Transport = 4,
Business = 5,
Health = 6,
}
impl Phase {
pub fn name(&self) -> &'static str {
match self {
Phase::Config => "Config",
Phase::Logging => "Logging",
Phase::Database => "Database",
Phase::Cache => "Cache",
Phase::Transport => "Transport",
Phase::Business => "Business",
Phase::Health => "Health",
}
}
}
pub struct PhasedModule<M: Module> {
module: M,
phase: Phase,
}
impl<M: Module> PhasedModule<M> {
pub fn new(module: M, phase: Phase) -> Self {
Self { module, phase }
}
pub fn module(&self) -> &M {
&self.module
}
pub fn phase(&self) -> Phase {
self.phase
}
}
impl<M: Module> std::ops::Deref for PhasedModule<M> {
type Target = M;
fn deref(&self) -> &Self::Target {
&self.module
}
}
pub struct ModuleRegistry {
modules: std::collections::HashMap<String, Box<dyn Module>>,
phases: std::collections::HashMap<Phase, Vec<String>>,
}
impl ModuleRegistry {
pub fn new() -> Self {
Self {
modules: std::collections::HashMap::new(),
phases: std::collections::HashMap::new(),
}
}
pub fn register<M: Module + 'static>(mut self, module: M) -> Self {
let name = module.name().to_string();
let phase = Phase::Business;
self.modules.insert(name.clone(), Box::new(module));
self.phases
.entry(phase)
.or_insert_with(Vec::new)
.push(name);
self
}
pub fn register_with_phase<M: Module + 'static>(mut self, module: M, phase: Phase) -> Self {
let name = module.name().to_string();
self.modules.insert(name.clone(), Box::new(module));
self.phases
.entry(phase)
.or_insert_with(Vec::new)
.push(name);
self
}
pub fn get(&self, name: &str) -> Option<&dyn Module> {
self.modules.get(name).map(|b| b.as_ref())
}
pub fn startup_order(&self) -> Vec<&dyn Module> {
let mut order = Vec::new();
for phase in &[Phase::Config, Phase::Logging, Phase::Database,
Phase::Cache, Phase::Transport, Phase::Business, Phase::Health] {
if let Some(names) = self.phases.get(phase) {
for name in names {
if let Some(module) = self.modules.get(name) {
order.push(module.as_ref());
}
}
}
}
order
}
pub fn shutdown_order(&self) -> Vec<&dyn Module> {
let mut order = self.startup_order();
order.reverse();
order
}
}
impl Default for ModuleRegistry {
fn default() -> Self {
Self::new()
}
}