kegani 0.1.0

A developer-friendly, ergonomic, production-ready Rust web framework
Documentation
//! Module trait and lifecycle hooks for Kegani
//!
//! Provides the Module trait for dependency injection and lifecycle management.

use async_trait::async_trait;
use crate::error::Result;

/// Module trait for lifecycle management
///
/// Implement this trait for any component that needs startup/shutdown hooks.
#[async_trait]
pub trait Module: Send + Sync {
    /// Get the module name
    fn name(&self) -> &str;

    /// Get the dependencies (module names that must start before this one)
    fn depends_on(&self) -> Vec<&str> {
        vec![]
    }

    /// Hook called before starting
    async fn on_start(&self) -> Result<()> {
        Ok(())
    }

    /// Hook called after starting
    async fn after_start(&self) -> Result<()> {
        Ok(())
    }

    /// Hook called before stopping
    async fn before_stop(&self) -> Result<()> {
        Ok(())
    }

    /// Hook called after stopping
    async fn on_stop(&self) -> Result<()> {
        Ok(())
    }
}

/// Startup phases for ordering modules
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Phase {
    /// Configuration loading
    Config = 0,
    /// Logging initialization
    Logging = 1,
    /// Database connection
    Database = 2,
    /// Redis/cache connection
    Cache = 3,
    /// Transport layer (HTTP, gRPC)
    Transport = 4,
    /// Business logic modules
    Business = 5,
    /// Health checks
    Health = 6,
}

impl Phase {
    /// Get the phase name
    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",
        }
    }
}

/// Phase-based module wrapper
pub struct PhasedModule<M: Module> {
    module: M,
    phase: Phase,
}

impl<M: Module> PhasedModule<M> {
    /// Create a new phased module
    pub fn new(module: M, phase: Phase) -> Self {
        Self { module, phase }
    }

    /// Get the module
    pub fn module(&self) -> &M {
        &self.module
    }

    /// Get the phase
    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
    }
}

/// Module registry for dependency resolution
pub struct ModuleRegistry {
    modules: std::collections::HashMap<String, Box<dyn Module>>,
    phases: std::collections::HashMap<Phase, Vec<String>>,
}

impl ModuleRegistry {
    /// Create a new registry
    pub fn new() -> Self {
        Self {
            modules: std::collections::HashMap::new(),
            phases: std::collections::HashMap::new(),
        }
    }

    /// Register a module
    pub fn register<M: Module + 'static>(mut self, module: M) -> Self {
        let name = module.name().to_string();
        let phase = Phase::Business; // Default phase

        // Add to modules map
        self.modules.insert(name.clone(), Box::new(module));

        // Add to phases map
        self.phases
            .entry(phase)
            .or_insert_with(Vec::new)
            .push(name);

        self
    }

    /// Register a module with phase
    pub fn register_with_phase<M: Module + 'static>(mut self, module: M, phase: Phase) -> Self {
        let name = module.name().to_string();

        // Add to modules map
        self.modules.insert(name.clone(), Box::new(module));

        // Add to phases map
        self.phases
            .entry(phase)
            .or_insert_with(Vec::new)
            .push(name);

        self
    }

    /// Get a module by name
    pub fn get(&self, name: &str) -> Option<&dyn Module> {
        self.modules.get(name).map(|b| b.as_ref())
    }

    /// Get modules in startup order
    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
    }

    /// Get modules in shutdown order (reverse)
    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()
    }
}