| Crates.io | notmad |
| lib.rs | notmad |
| version | 0.10.0 |
| created_at | 2024-11-08 23:28:16.220863+00 |
| updated_at | 2025-11-15 13:48:53.76128+00 |
| description | notmad is a life-cycle manager for long running rust operations |
| homepage | |
| repository | https://github.com/kjuulh/mad |
| max_upload_size | |
| id | 1441671 |
| size | 74,652 |
MAD is a robust lifecycle manager designed for long-running Rust operations. It provides a simple, composable way to manage multiple concurrent services within your application, handling graceful startup and shutdown automatically.
Add MAD to your Cargo.toml:
[dependencies]
notmad = "0.7.5"
tokio = { version = "1", features = ["full"] }
async-trait = "0.1"
Here's a simple example of a component that simulates a long-running server:
use mad::{Component, Mad};
use async_trait::async_trait;
use tokio_util::sync::CancellationToken;
// Define your component
struct WebServer {
port: u16,
}
#[async_trait]
impl Component for WebServer {
fn name(&self) -> Option<String> {
Some(format!("WebServer on port {}", self.port))
}
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
println!("Starting web server on port {}", self.port);
// Your server logic here
// The cancellation token will be triggered on shutdown
tokio::select! {
_ = cancellation.cancelled() => {
println!("Shutting down web server");
}
_ = self.serve() => {
println!("Server stopped");
}
}
Ok(())
}
}
impl WebServer {
async fn serve(&self) {
// Simulate a running server
tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Build and run your application
Mad::builder()
.add(WebServer { port: 8080 })
.add(WebServer { port: 8081 }) // You can add multiple instances
.run()
.await?;
Ok(())
}
Components can be anything that implements the Component trait:
use mad::{Component, Mad};
use async_trait::async_trait;
struct QueueProcessor {
queue_name: String,
}
#[async_trait]
impl Component for QueueProcessor {
fn name(&self) -> Option<String> {
Some(format!("QueueProcessor-{}", self.queue_name))
}
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
while !cancellation.is_cancelled() {
// Process messages from queue
self.process_next_message().await?;
}
Ok(())
}
}
MAD provides comprehensive error handling through the MadError type with automatic conversion from anyhow::Error:
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
// Errors automatically convert from anyhow::Error to MadError
database_operation().await?;
// Or return explicit errors
if some_condition {
return Err(anyhow::anyhow!("Something went wrong").into());
}
Ok(())
}
Check out the examples directory for more detailed examples:
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
Created and maintained by kjuulh
Find the source code at https://github.com/kjuulh/mad