# Rhai Client The `rhai-client` crate provides a fluent builder-based interface for submitting Rhai scripts to a distributed task execution system over Redis. It enables applications to offload Rhai script execution to one or more worker services and await the results. ## Features - **Fluent Builder API**: A `RhaiDispatcherBuilder` for easy client configuration and a `PlayRequestBuilder` for constructing and submitting script execution requests. - **Asynchronous Operations**: Built with `tokio` for non-blocking I/O. - **Request-Reply Pattern**: Submits tasks and awaits results on a dedicated reply queue, eliminating the need for polling. - **Configurable Timeouts**: Set timeouts for how long the client should wait for a task to complete. - **Direct-to-Worker-Queue Submission**: Tasks are sent to a queue named after the `worker_id`, allowing for direct and clear task routing. - **Manual Status Check**: Provides an option to manually check the status of a task by its ID. ## Core Components - **`RhaiDispatcherBuilder`**: A builder to construct a `RhaiDispatcher`. Requires a `caller_id` and Redis URL. - **`RhaiDispatcher`**: The main client for interacting with the task system. It's used to create `PlayRequestBuilder` instances. - **`PlayRequestBuilder`**: A fluent builder for creating and dispatching a script execution request. You can set: - `worker_id`: The ID of the worker queue to send the task to. - `script` or `script_path`: The Rhai script to execute. - `request_id`: An optional unique ID for the request. - `timeout`: How long to wait for a result. - **Submission Methods**: - `submit()`: Submits the request and returns immediately (fire-and-forget). - `await_response()`: Submits the request and waits for the result or a timeout. - **`RhaiTaskDetails`**: A struct representing the details of a task, including its script, status (`pending`, `processing`, `completed`, `error`), output, and error messages. - **`RhaiDispatcherError`**: An enum for various errors, such as Redis errors, serialization issues, or task timeouts. ## How It Works 1. A `RhaiDispatcher` is created using the `RhaiDispatcherBuilder`, configured with a `caller_id` and Redis URL. 2. A `PlayRequestBuilder` is created from the client. 3. The script, `worker_id`, and an optional `timeout` are configured on the builder. 4. When `await_response()` is called: a. A unique `task_id` (UUID v4) is generated. b. Task details are stored in a Redis hash with a key like `rhailib:`. c. The `task_id` is pushed to the worker's queue, named `rhailib:`. d. The client performs a blocking pop (`BLPOP`) on a dedicated reply queue (`rhailib:reply:`), waiting for the worker to send the result. 5. A `rhai-worker` process, listening on the `rhailib:` queue, picks up the task, executes it, and pushes the final `RhaiTaskDetails` to the reply queue. 6. The client receives the result from the reply queue and returns it to the caller. ## Prerequisites - A running Redis instance accessible by the client and the worker services. ## Usage Example The following example demonstrates how to build a client, submit a script, and wait for the result. ```rust use rhai_dispatcher::{RhaiDispatcherBuilder, RhaiDispatcherError}; use std::time::Duration; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); // 1. Build the client let client = RhaiDispatcherBuilder::new() .caller_id("my-app-instance-1") .redis_url("redis://127.0.0.1/") .build()?; // 2. Define the script and target worker let script = r#" "Hello, " + worker_id + "!" "#; let worker_id = "worker-1"; // 3. Use the PlayRequestBuilder to configure and submit the request let result = client .new_play_request() .worker_id(worker_id) .script(script) .timeout(Duration::from_secs(5)) .await_response() .await; match result { Ok(details) => { log::info!("Task completed successfully!"); log::info!("Status: {}", details.status); if let Some(output) = details.output { log::info!("Output: {}", output); } } Err(RhaiDispatcherError::Timeout(task_id)) => { log::error!("Task {} timed out.", task_id); } Err(e) => { log::error!("An unexpected error occurred: {}", e); } } Ok(()) } ``` Refer to the `examples/` directory for more specific use cases, such as `timeout_example.rs` which tests the timeout mechanism. ## Building and Running Examples To run an example (e.g., `timeout_example`): ```bash cd src/client # (or wherever this client's Cargo.toml is) cargo run --example timeout_example ``` Ensure a Redis server is running and accessible at `redis://127.0.0.1/`.