Compare commits
6 Commits
v0.7.4
...
cuddle-ple
Author | SHA1 | Date | |
---|---|---|---|
|
11d659dbd7 | ||
762da1e672
|
|||
3bc512ab48 | |||
7d8071d41b
|
|||
1cc4138ec7
|
|||
00517daaaa
|
14
CHANGELOG.md
14
CHANGELOG.md
@@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.8.0] - 2025-08-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- update readme
|
||||||
|
|
||||||
|
## [0.7.5] - 2025-07-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- print big inner
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- more error correction
|
||||||
|
- correct error test to not be as verbose
|
||||||
|
|
||||||
## [0.7.4] - 2025-07-24
|
## [0.7.4] - 2025-07-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -278,7 +278,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notmad"
|
name = "notmad"
|
||||||
version = "0.7.3"
|
version = "0.7.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@@ -3,7 +3,7 @@ members = ["crates/*"]
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.7.4"
|
version = "0.8.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
mad = { path = "crates/mad" }
|
mad = { path = "crates/mad" }
|
||||||
|
159
README.md
159
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
|
## Overview
|
||||||
- Queue bindings
|
|
||||||
- gRPC servers etc
|
|
||||||
- Cron runners
|
|
||||||
|
|
||||||
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
|
```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]
|
#[async_trait]
|
||||||
impl Component for WaitServer {
|
impl Component for WebServer {
|
||||||
fn name(&self) -> Option<String> {
|
fn name(&self) -> Option<String> {
|
||||||
Some("NeverEndingRun".into())
|
Some(format!("WebServer on port {}", self.port))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
|
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
|
||||||
let millis_wait = rand::thread_rng().gen_range(50..1000);
|
println!("Starting web server on port {}", self.port);
|
||||||
|
|
||||||
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
// Your server logic here
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
// The cancellation token will be triggered on shutdown
|
||||||
|
tokio::select! {
|
||||||
|
_ = cancellation.cancelled() => {
|
||||||
|
println!("Shutting down web server");
|
||||||
|
}
|
||||||
|
_ = self.serve() => {
|
||||||
|
println!("Server stopped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WebServer {
|
||||||
|
async fn serve(&self) {
|
||||||
|
// Simulate a running server
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
// Build and run your application
|
||||||
Mad::builder()
|
Mad::builder()
|
||||||
.add(WaitServer {})
|
.add(WebServer { port: 8080 })
|
||||||
.add(WaitServer {})
|
.add(WebServer { port: 8081 }) // You can add multiple instances
|
||||||
.add(WaitServer {})
|
|
||||||
.run()
|
.run()
|
||||||
.await?;
|
.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
|
## Examples
|
||||||
|
|
||||||
Can be found (here)[crates/mad/examples]
|
Check out the [examples directory](crates/mad/examples) for more detailed examples:
|
||||||
|
|
||||||
- basic
|
- **basic** - Simple component lifecycle
|
||||||
- fn
|
- **fn** - Using functions as components
|
||||||
- signals
|
- **signals** - Handling system signals
|
||||||
- error_log
|
- **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)
|
||||||
|
@@ -11,16 +11,16 @@ mod waiter;
|
|||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum MadError {
|
pub enum MadError {
|
||||||
#[error("component failed: {0}")]
|
#[error("component: {0:#?}")]
|
||||||
Inner(#[source] anyhow::Error),
|
Inner(#[source] anyhow::Error),
|
||||||
|
|
||||||
#[error("component(s) failed: {run}")]
|
#[error("component: {run:#?}")]
|
||||||
RunError { run: anyhow::Error },
|
RunError { run: anyhow::Error },
|
||||||
|
|
||||||
#[error("component(s) failed: {close}")]
|
#[error("component(s) failed: {close}")]
|
||||||
CloseError { close: anyhow::Error },
|
CloseError { close: anyhow::Error },
|
||||||
|
|
||||||
#[error("component(s) failed: {0}")]
|
#[error("component(s): {0}")]
|
||||||
AggregateError(AggregateError),
|
AggregateError(AggregateError),
|
||||||
|
|
||||||
#[error("setup not defined")]
|
#[error("setup not defined")]
|
||||||
|
@@ -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:
|
vars:
|
||||||
service: "mad"
|
service: "mad"
|
||||||
@@ -12,6 +12,6 @@ please:
|
|||||||
repository: "mad"
|
repository: "mad"
|
||||||
branch: main
|
branch: main
|
||||||
settings:
|
settings:
|
||||||
api_url: "https://git.front.kjuulh.io"
|
api_url: "https://git.kjuulh.io"
|
||||||
actions:
|
actions:
|
||||||
rust:
|
rust:
|
||||||
|
Reference in New Issue
Block a user