manager_handlers 0.5.13

This crate represents the implementation of manager capable of creating a microservice which has multiple handlers, which can be accesed via http from outside. Each handler can use the other ones via a bus to process the request. The handlers can have a number of replicas
Documentation

# Manager Crate

## Overview

`Manager` is a scalable, async-driven system that handles requests and communication between different components using a pub/sub model in Rust. It is built on top of the `Rocket` framework for HTTP server handling and utilizes the Tokio runtime for asynchronous tasks. This crate allows you to create handlers that process requests and handle messages via a message bus, making it highly modular and easy to extend.

## Features

- **Dynamic Handler Registration**: Register new handlers dynamically that can process requests and publish messages.
- **Pub/Sub Messaging**: Implement publish/subscribe messaging between different services or components.
- **Concurrency Control**: Uses a semaphore to limit to `nr` request at the time from http request. The handlers inside the service can replicate as many times as they are configured.
- **Graceful Shutdown**: Includes an HTTP shutdown mechanism for controlled termination of the service.
- **Asynchronous Processing**: All handlers and requests are processed asynchronously using Tokio's async runtime.
- **Shared state**: All handlers can access a shared state given by variable shared_state.

## Prerequisites

Before you begin, ensure you have met the following requirements:

- Rust version >= 1.56 (due to the use of the `async_trait` crate and async/await syntax).

## Installation

To use this crate in your project, add the following dependencies to your `Cargo.toml` file:

```toml
[dependencies]
async-trait = "0.1"
futures = "0.3.30"
```

## How to Use

### Define a Handler

To create a custom handler, you need to implement the `Base` trait. The `Base` trait requires two functions:

- `run`: Handles the actual business logic of processing incoming messages.
- `create`: Handles the creation of the struct

It also gives two ways to communicate between handlers: 
- `publish`: Publishes messages to other components via the `MultiBus` and awaits the response.
- `dispatch`: Dispatches messages to another service or handler.

```rust
use async_trait::async_trait;
use std::sync::Arc;
use manager_handlers::multibus::{MultiBus};
use manager_handlers::manager::{StateType, SharedState};

pub struct MyHandler;

#[async_trait]
impl Base for MyHandler {
    async fn run(&self, src: String, data: String, communication_line: Arc<MultiBus>, shared_state: Arc<SharedState>) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
        // Your business logic goes here
        // Example of publish:   
        let response = self.publish(data, "handler_1".to_string(), "handler_2".to_string(), communication_line.clone()).await;
        // Example of dispatch:
        self.dispatch(data, "handler_2".to_string(), communication_line.clone()).await;
        // Example of inserting primitive type:
        shared_state.insert(&"matei".to_string(), StateType::Int(43)).await;
        // Example of inserting sync function: 
        let shared_function: Arc<dyn Fn(String) -> String + Sync + Send> = Arc::new(|input: String| -> String {
          println!("Hello, {}!", input);
          input + " pesto"
        });
        shared_state.insert(&"sync_func".to_string(), StateType::FunctionSync(shared_function.clone())).await;
        // Example of inserting async function: 
        let shared_async_function: Arc<dyn Fn(String) -> BoxFuture<'static, String> + Send + Sync> = Arc::new(|input: String| async move {
          println!("Got in the async function");
          sleep(Duration::from_secs(5)).await;
          "Done".to_string()
        }.boxed());
        shared_state.insert(&"async_func".to_string(), StateType::FunctionAsync(shared_async_function.clone())).await;
        Ok("Handled successfully".to_string())
    }

    fn new() -> Self {
        MyHandler {}
    }
}
```

### Create and Start the Manager

The `Manager` is responsible for initializing all the handlers and launching the HTTP server. Add your custom handlers to the manager using `add_handler`. 
You can configure the number of replicas a handler can have. 
IMPORTANT: There will always answer only one request at a time from outside (http), but between handlers the number of replicas will determine how many requests can be handled.

```rust
use manager_handlers::manager::Manager;

#[tokio::main]
async fn main() {
    let mut manager = Manager::new();

    // Register custom handlers
    manager.add_handler::<MyHandler>("my_handler", 2);

    // Start the manager
    manager.start().await;
}
```

### HTTP Endpoints

This crate provides a couple of HTTP endpoints that allow interaction with the system.

1. **POST `/shutdown`**: Shuts down the server gracefully.
   
   Example:

   ```bash
   curl -X POST http://localhost:8080/shutdown
   ```

2. **POST `/handler_name`**: Sends a request to a registered handler with a string body. You can after that process into a JSON.

   Example:

   ```bash
   curl -X POST http://localhost:8080/my_handler -d "{\"key\": \"value\"}" -H "Content-Type: text/plain"
   ```


## Error Handling

Errors during request processing or message dispatching are handled gracefully, and appropriate error messages are returned via JSON. If a handler encounters an error, it logs the issue and returns an error message.

### Example Error Response

```json
{
    "status": "Error",
    "message": "An error occurred while processing the request."
}
```

## License

This crate is open-sourced under the MIT license.