use async_trait::async_trait;
use crate::error::Result;
use super::module::{Module, ModuleRegistry, Phase};
use std::sync::Arc;
use tokio::sync::RwLock;
pub type LifecycleHook = Arc<dyn Fn(&str) -> Result<()> + Send + Sync>;
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 {
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)),
}
}
pub fn register<M: Module + 'static>(self, module: M) -> Self {
Self {
registry: self.registry.register(module),
..self
}
}
pub fn register_with_phase<M: Module + 'static>(self, module: M, phase: Phase) -> Self {
Self {
registry: self.registry.register_with_phase(module, phase),
..self
}
}
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
}
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
}
pub async fn start(&self) -> Result<()> {
{
let mut state = self.state.write().await;
*state = CarapaceState::Starting;
}
tracing::info!("Carapace starting...");
for module in self.registry.startup_order() {
let name = module.name();
tracing::info!("Starting module: {}", name);
module.on_start().await?;
for hook in &self.on_start_hooks {
hook(name)?;
}
tracing::info!("Module started: {}", name);
}
{
let mut state = self.state.write().await;
*state = CarapaceState::Running;
}
tracing::info!("Carapace started successfully");
Ok(())
}
pub async fn stop(&self) -> Result<()> {
{
let mut state = self.state.write().await;
*state = CarapaceState::Stopping;
}
tracing::info!("Carapace stopping...");
for module in self.registry.shutdown_order() {
let name = module.name();
tracing::info!("Stopping module: {}", name);
module.before_stop().await?;
module.on_stop().await?;
for hook in &self.on_stop_hooks {
hook(name)?;
}
tracing::info!("Module stopped: {}", name);
}
{
let mut state = self.state.write().await;
*state = CarapaceState::Stopped;
}
tracing::info!("Carapace stopped");
Ok(())
}
pub async fn state(&self) -> CarapaceState {
*self.state.read().await
}
pub fn registry(&self) -> &ModuleRegistry {
&self.registry
}
}
impl Default for Carapace {
fn default() -> Self {
Self::new()
}
}
impl Drop for Carapace {
fn drop(&mut self) {
}
}
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(())
}
}
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(())
}
}