kegani 0.1.0

A developer-friendly, ergonomic, production-ready Rust web framework
Documentation
//! Carapace - Application lifecycle orchestrator for Kegani
//!
//! Carapace manages the startup and shutdown of all modules.

use async_trait::async_trait;
use crate::error::Result;
use super::module::{Module, ModuleRegistry, Phase};
use std::sync::Arc;
use tokio::sync::RwLock;

/// Lifecycle hook callback type
pub type LifecycleHook = Arc<dyn Fn(&str) -> Result<()> + Send + Sync>;

/// Carapace - Module orchestration container
pub struct Carapace {
    registry: ModuleRegistry,
    on_start_hooks: Vec<LifecycleHook>,
    on_stop_hooks: Vec<LifecycleHook>,
    state: Arc<RwLock<CarapaceState>>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CarapaceState {
    Created,
    Starting,
    Running,
    Stopping,
    Stopped,
}

impl Carapace {
    /// Create a new Carapace
    pub fn new() -> Self {
        Self {
            registry: ModuleRegistry::new(),
            on_start_hooks: Vec::new(),
            on_stop_hooks: Vec::new(),
            state: Arc::new(RwLock::new(CarapaceState::Created)),
        }
    }

    /// Register a module
    pub fn register<M: Module + 'static>(self, module: M) -> Self {
        Self {
            registry: self.registry.register(module),
            ..self
        }
    }

    /// Register a module with phase
    pub fn register_with_phase<M: Module + 'static>(self, module: M, phase: Phase) -> Self {
        Self {
            registry: self.registry.register_with_phase(module, phase),
            ..self
        }
    }

    /// Add a startup hook
    pub fn on_start<F>(mut self, hook: F) -> Self
    where
        F: Fn(&str) -> Result<()> + Send + Sync + 'static,
    {
        self.on_start_hooks.push(Arc::new(hook));
        self
    }

    /// Add a shutdown hook
    pub fn on_stop<F>(mut self, hook: F) -> Self
    where
        F: Fn(&str) -> Result<()> + Send + Sync + 'static,
    {
        self.on_stop_hooks.push(Arc::new(hook));
        self
    }

    /// Start all modules
    pub async fn start(&self) -> Result<()> {
        // Update state
        {
            let mut state = self.state.write().await;
            *state = CarapaceState::Starting;
        }

        tracing::info!("Carapace starting...");

        // Start modules in order
        for module in self.registry.startup_order() {
            let name = module.name();
            tracing::info!("Starting module: {}", name);

            // Call before_start hook
            module.on_start().await?;

            // Call global hooks
            for hook in &self.on_start_hooks {
                hook(name)?;
            }

            tracing::info!("Module started: {}", name);
        }

        // Update state
        {
            let mut state = self.state.write().await;
            *state = CarapaceState::Running;
        }

        tracing::info!("Carapace started successfully");
        Ok(())
    }

    /// Stop all modules
    pub async fn stop(&self) -> Result<()> {
        // Update state
        {
            let mut state = self.state.write().await;
            *state = CarapaceState::Stopping;
        }

        tracing::info!("Carapace stopping...");

        // Stop modules in reverse order
        for module in self.registry.shutdown_order() {
            let name = module.name();
            tracing::info!("Stopping module: {}", name);

            // Call before_stop hook
            module.before_stop().await?;

            // Call on_stop hook
            module.on_stop().await?;

            // Call global hooks
            for hook in &self.on_stop_hooks {
                hook(name)?;
            }

            tracing::info!("Module stopped: {}", name);
        }

        // Update state
        {
            let mut state = self.state.write().await;
            *state = CarapaceState::Stopped;
        }

        tracing::info!("Carapace stopped");
        Ok(())
    }

    /// Get the current state
    pub async fn state(&self) -> CarapaceState {
        *self.state.read().await
    }

    /// Get the registry
    pub fn registry(&self) -> &ModuleRegistry {
        &self.registry
    }
}

impl Default for Carapace {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for Carapace {
    fn drop(&mut self) {
        // Attempt graceful shutdown on drop
        // In practice, this should be called explicitly
    }
}

/// Example: Database module
pub struct DatabaseModule {
    pool: crate::db::DbPool,
}

impl DatabaseModule {
    pub fn new(pool: crate::db::DbPool) -> Self {
        Self { pool }
    }
}

#[async_trait]
impl Module for DatabaseModule {
    fn name(&self) -> &str {
        "database"
    }

    fn depends_on(&self) -> Vec<&str> {
        vec!["config"]
    }

    async fn on_start(&self) -> Result<()> {
        tracing::info!("Connecting to database...");
        self.pool.ping().await?;
        tracing::info!("Database connected");
        Ok(())
    }

    async fn on_stop(&self) -> Result<()> {
        tracing::info!("Closing database connection...");
        Ok(())
    }
}

/// Example: HTTP server module
pub struct HttpServerModule {
    server: Option<actix_web::dev::ServerHandle>,
}

impl HttpServerModule {
    pub fn new() -> Self {
        Self { server: None }
    }

    pub fn with_handle(mut self, handle: actix_web::dev::ServerHandle) -> Self {
        self.server = Some(handle);
        self
    }
}

#[async_trait]
impl Module for HttpServerModule {
    fn name(&self) -> &str {
        "http-server"
    }

    fn depends_on(&self) -> Vec<&str> {
        vec!["database", "cache"]
    }

    async fn on_stop(&self) -> Result<()> {
        if let Some(handle) = self.server.take() {
            tracing::info!("Stopping HTTP server...");
            handle.stop(true).await;
        }
        Ok(())
    }
}