From 762da1e67235b839318c6c8e1a3d2dcfb570981c Mon Sep 17 00:00:00 2001 From: kjuulh Date: Thu, 7 Aug 2025 11:23:08 +0200 Subject: [PATCH] feat: update readme Signed-off-by: kjuulh --- Cargo.lock | 2 +- README.md | 163 ++++++++++++++++++++++++++++++++++++++++++++-------- cuddle.yaml | 6 +- 3 files changed, 144 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dbd4c22..2217b53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -278,7 +278,7 @@ dependencies = [ [[package]] name = "notmad" -version = "0.7.4" +version = "0.7.5" dependencies = [ "anyhow", "async-trait", diff --git a/README.md b/README.md index 1690b58..3de47e2 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,92 @@ -# MAD +# MAD - Lifecycle Manager for Rust Applications -Mad is a life-cycle manager for long running rust operations. +[![Crates.io](https://img.shields.io/crates/v/notmad.svg)](https://crates.io/crates/notmad) +[![Documentation](https://docs.rs/notmad/badge.svg)](https://docs.rs/notmad) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -- Webservers -- Queue bindings -- gRPC servers etc -- Cron runners +## Overview -It is supposed to be the main thing the application runs, and everything from it is spawned and managed by it. +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. + +### Perfect for: +- 🌐 Web servers +- 📨 Queue consumers and message processors +- 🔌 gRPC servers +- ⏰ Cron job runners +- 🔄 Background workers +- 📡 Any long-running async operations + +## Features + +- **Component-based architecture** - Build your application from composable, reusable components +- **Graceful shutdown** - Automatic handling of shutdown signals with proper cleanup +- **Concurrent execution** - Run multiple components in parallel with tokio +- **Error handling** - Built-in error propagation and logging +- **Cancellation tokens** - Coordinate shutdown across all components +- **Minimal boilerplate** - Focus on your business logic, not lifecycle management + +## Installation + +Add MAD to your `Cargo.toml`: + +```toml +[dependencies] +notmad = "0.7.5" +tokio = { version = "1", features = ["full"] } +async-trait = "0.1" +``` + +## Quick Start + +Here's a simple example of a component that simulates a long-running server: ```rust -struct WaitServer {} +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 WaitServer { +impl Component for WebServer { fn name(&self) -> Option { - Some("NeverEndingRun".into()) + Some(format!("WebServer on port {}", self.port)) } async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> { - let millis_wait = rand::thread_rng().gen_range(50..1000); - - // Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely - tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await; - + 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(WaitServer {}) - .add(WaitServer {}) - .add(WaitServer {}) + .add(WebServer { port: 8080 }) + .add(WebServer { port: 8081 }) // You can add multiple instances .run() .await?; @@ -41,11 +94,75 @@ async fn main() -> anyhow::Result<()> { } ``` +## Advanced Usage + +### Custom Components + +Components can be anything that implements the `Component` trait: + +```rust +use mad::{Component, Mad}; +use async_trait::async_trait; + +struct QueueProcessor { + queue_name: String, +} + +#[async_trait] +impl Component for QueueProcessor { + fn name(&self) -> Option { + 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(()) + } +} +``` + +### Error Handling + +MAD provides comprehensive error handling through the `MadError` type with automatic conversion from `anyhow::Error`: + +```rust +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(()) +} +``` + ## Examples -Can be found (here)[crates/mad/examples] +Check out the [examples directory](crates/mad/examples) for more detailed examples: -- basic -- fn -- signals -- error_log +- **basic** - Simple component lifecycle +- **fn** - Using functions as components +- **signals** - Handling system signals +- **error_log** - Error handling and logging + +## Contributing + +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. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Author + +Created and maintained by [kjuulh](https://github.com/kjuulh) + +## Repository + +Find the source code at [https://github.com/kjuulh/mad](https://github.com/kjuulh/mad) diff --git a/cuddle.yaml b/cuddle.yaml index d184b3a..9caea29 100644 --- a/cuddle.yaml +++ b/cuddle.yaml @@ -1,6 +1,6 @@ -# yaml-language-server: $schema=https://git.front.kjuulh.io/kjuulh/cuddle/raw/branch/main/schemas/base.json +# yaml-language-server: $schema=https://git.kjuulh.io/kjuulh/cuddle/raw/branch/main/schemas/base.json -base: "git@git.front.kjuulh.io:kjuulh/cuddle-rust-lib-plan.git" +base: "git@git.kjuulh.io:kjuulh/cuddle-rust-lib-plan.git" vars: service: "mad" @@ -12,6 +12,6 @@ please: repository: "mad" branch: main settings: - api_url: "https://git.front.kjuulh.io" + api_url: "https://git.kjuulh.io" actions: rust: