92c0c223198e23fdd226542258bf3f6cf30bafff
Some checks failed
continuous-integration/drone/push Build encountered an error
MAD - Lifecycle Manager for Rust Applications
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
-
name()→info(): ReturnsComponentInfoinstead ofOption<String>// Before fn name(&self) -> Option<String> { Some("my-service".into()) } // After fn info(&self) -> ComponentInfo { "my-service".into() } -
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
Links
Description