Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -278,7 +278,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "notmad"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
163
README.md
163
README.md
@@ -1,39 +1,92 @@
|
||||
# MAD
|
||||
# MAD - Lifecycle Manager for Rust Applications
|
||||
|
||||
Mad is a life-cycle manager for long running rust operations.
|
||||
[](https://crates.io/crates/notmad)
|
||||
[](https://docs.rs/notmad)
|
||||
[](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<String> {
|
||||
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<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(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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)
|
||||
|
@@ -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:
|
||||
|
Reference in New Issue
Block a user