Kasper Juul Hermansen 777ab509a0
Some checks failed
continuous-integration/drone/pr Build encountered an error
continuous-integration/drone/push Build encountered an error
chore(deps): update rust crate anyhow to v1.0.101
2026-02-06 01:44:35 +00:00
2024-08-06 22:22:46 +02:00
2024-08-06 22:22:46 +02:00
2026-02-05 23:32:51 +01:00
2025-11-15 14:48:14 +01:00
2025-09-03 12:52:24 +02:00
2026-02-05 23:32:51 +01:00
2024-11-24 00:26:37 +01:00

MAD - Lifecycle Manager for Rust Applications

Crates.io Documentation License: MIT

A simple lifecycle manager for long-running Rust applications. Run multiple services concurrently with graceful shutdown handling.

Installation

[dependencies]
notmad = "0.10.0"
tokio = { version = "1", features = ["full"] }

Quick Start

use notmad::{Component, Mad};
use tokio_util::sync::CancellationToken;

struct MyService;

impl Component for MyService {
    async fn run(&self, cancellation: CancellationToken) -> Result<(), notmad::MadError> {
        println!("Service running...");
        cancellation.cancelled().await;
        println!("Service stopped");
        Ok(())
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    Mad::builder()
        .add(MyService)
        .run()
        .await?;
    Ok(())
}

Basic Usage

Axum Web Server with Graceful Shutdown

Here's how to run an Axum server with MAD's graceful shutdown:

use axum::{Router, routing::get};
use notmad::{Component, ComponentInfo};
use tokio_util::sync::CancellationToken;

struct WebServer {
    port: u16,
}

impl Component for WebServer {
    fn info(&self) -> ComponentInfo {
        format!("WebServer:{}", self.port).into()
    }

    async fn run(&self, cancel: CancellationToken) -> Result<(), notmad::MadError> {
        let app = Router::new().route("/", get(|| async { "Hello, World!" }));

        let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", self.port))
            .await?;

        println!("Listening on http://0.0.0.0:{}", self.port);

        // Run server with graceful shutdown
        axum::serve(listener, app)
            .with_graceful_shutdown(async move {
                cancel.cancelled().await;
                println!("Shutting down server...");
            })
            .await?;

        Ok(())
    }
}

Run Multiple Services

Mad::builder()
    .add(WebServer { port: 8080 })
    .add(WebServer { port: 8081 })
    .run()
    .await?;

Use Functions as Components

Mad::builder()
    .add_fn(|cancel| async move {
        println!("Running...");
        cancel.cancelled().await;
        Ok(())
    })
    .run()
    .await?;

Lifecycle Hooks

Components support optional setup and cleanup phases:

impl Component for DatabaseService {
    async fn setup(&self) -> Result<(), notmad::MadError> {
        println!("Connecting to database...");
        Ok(())
    }

    async fn run(&self, cancel: CancellationToken) -> Result<(), notmad::MadError> {
        cancel.cancelled().await;
        Ok(())
    }

    async fn close(&self) -> Result<(), notmad::MadError> {
        println!("Closing database connection...");
        Ok(())
    }
}

Migration from v0.10

Breaking Changes

  1. name()info(): Returns ComponentInfo instead of Option<String>

    // Before
    fn name(&self) -> Option<String> { Some("my-service".into()) }
    
    // After
    fn info(&self) -> ComponentInfo { "my-service".into() }
    
  2. No more async-trait: Remove the dependency and #[async_trait] attribute

    // Before
    #[async_trait]
    impl Component for MyService { }
    
    // After
    impl Component for MyService { }
    

Examples

See examples directory for complete working examples.

License

MIT - see LICENSE

Description
No description provided
Readme 533 KiB
v0.10.0 Latest
2025-11-15 14:48:27 +01:00
Languages
Rust 100%