rename client and move incomplete projects to research
This commit is contained in:
parent
e75c050745
commit
3b1bc9a838
38
examples/flows/new_create_payment_intent_error.rhai
Normal file
38
examples/flows/new_create_payment_intent_error.rhai
Normal file
@ -0,0 +1,38 @@
|
||||
// Error handler for failed payment intent creation
|
||||
// This script is triggered when a payment intent creation fails
|
||||
|
||||
print("❌ Payment Intent Creation Failed!");
|
||||
print("==================================");
|
||||
|
||||
// The error data is available as 'parsed_error'
|
||||
if parsed_error != () {
|
||||
print(`Error: ${parsed_error}`);
|
||||
|
||||
// You can handle different types of errors
|
||||
if parsed_error.contains("authentication") {
|
||||
print("🔑 Authentication error - check API key");
|
||||
// eval_file("flows/handle_auth_error.rhai");
|
||||
} else if parsed_error.contains("insufficient_funds") {
|
||||
print("💰 Insufficient funds error");
|
||||
// eval_file("flows/handle_insufficient_funds.rhai");
|
||||
} else if parsed_error.contains("card_declined") {
|
||||
print("💳 Card declined error");
|
||||
// eval_file("flows/handle_card_declined.rhai");
|
||||
} else {
|
||||
print("⚠️ General payment error");
|
||||
// eval_file("flows/handle_general_payment_error.rhai");
|
||||
}
|
||||
|
||||
// Log the error for monitoring
|
||||
print("📊 Logging error for analytics...");
|
||||
// eval_file("flows/log_payment_error.rhai");
|
||||
|
||||
// Notify relevant parties
|
||||
print("📧 Sending error notifications...");
|
||||
// eval_file("flows/send_error_notification.rhai");
|
||||
|
||||
} else {
|
||||
print("⚠️ No error data received");
|
||||
}
|
||||
|
||||
print("🔄 Error handling complete!");
|
34
examples/flows/new_create_payment_intent_response.rhai
Normal file
34
examples/flows/new_create_payment_intent_response.rhai
Normal file
@ -0,0 +1,34 @@
|
||||
// Response handler for successful payment intent creation
|
||||
// This script is triggered when a payment intent is successfully created
|
||||
|
||||
print("✅ Payment Intent Created Successfully!");
|
||||
print("=====================================");
|
||||
|
||||
// The response data is available as 'parsed_data'
|
||||
if parsed_data != () {
|
||||
print(`Payment Intent ID: ${parsed_data.id}`);
|
||||
print(`Amount: ${parsed_data.amount}`);
|
||||
print(`Currency: ${parsed_data.currency}`);
|
||||
print(`Status: ${parsed_data.status}`);
|
||||
|
||||
if parsed_data.client_secret != () {
|
||||
print(`Client Secret: ${parsed_data.client_secret}`);
|
||||
}
|
||||
|
||||
// You can now trigger additional workflows
|
||||
print("🔄 Triggering next steps...");
|
||||
|
||||
// Example: Send confirmation email
|
||||
// eval_file("flows/send_payment_confirmation_email.rhai");
|
||||
|
||||
// Example: Update user account
|
||||
// eval_file("flows/update_user_payment_status.rhai");
|
||||
|
||||
// Example: Log analytics event
|
||||
// eval_file("flows/log_payment_analytics.rhai");
|
||||
|
||||
} else {
|
||||
print("⚠️ No response data received");
|
||||
}
|
||||
|
||||
print("🎉 Payment intent response processing complete!");
|
@ -8,11 +8,11 @@ tokio = { version = "1", features = ["macros", "rt-multi-thread", "time", "sync"
|
||||
url = "2" # For parsing Redis URL
|
||||
tracing = "0.1" # For logging
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
log = "0.4" # rhai_client uses log crate
|
||||
log = "0.4" # rhai_dispatcher uses log crate
|
||||
rustyline = { version = "13.0.0", features = ["derive"] } # For enhanced REPL input
|
||||
tempfile = "3.8" # For creating temporary files for editing
|
||||
|
||||
rhai_client = { path = "../client" }
|
||||
rhai_dispatcher = { path = "../client" }
|
||||
anyhow = "1.0" # For simpler error handling
|
||||
|
||||
rhailib_worker = { path = "../worker", package = "rhailib_worker" }
|
@ -1,5 +1,5 @@
|
||||
use anyhow::Context;
|
||||
use rhai_client::{RhaiClient, RhaiClientError, RhaiTaskDetails};
|
||||
use rhai_dispatcher::{RhaiDispatcher, RhaiDispatcherError, RhaiTaskDetails};
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@ -17,7 +17,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.with_env_filter(
|
||||
EnvFilter::from_default_env()
|
||||
.add_directive("connect_and_play=info".parse().unwrap())
|
||||
.add_directive("rhai_client=info".parse().unwrap()),
|
||||
.add_directive("rhai_dispatcher=info".parse().unwrap()),
|
||||
)
|
||||
.init();
|
||||
|
||||
@ -94,12 +94,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
|
||||
println!(
|
||||
"Initializing RhaiClient for Redis at {} to target worker '{}'...",
|
||||
"Initializing RhaiDispatcher for Redis at {} to target worker '{}'...",
|
||||
redis_url, worker_name
|
||||
);
|
||||
let client = RhaiClient::new(&redis_url)
|
||||
.with_context(|| format!("Failed to create RhaiClient for Redis URL: {}", redis_url))?;
|
||||
println!("RhaiClient initialized.");
|
||||
let client = RhaiDispatcher::new(&redis_url)
|
||||
.with_context(|| format!("Failed to create RhaiDispatcher for Redis URL: {}", redis_url))?;
|
||||
println!("RhaiDispatcher initialized.");
|
||||
|
||||
let script = "let a = 10; let b = 32; let message = \"Hello from example script!\"; message + \" Result: \" + (a + b)";
|
||||
println!("\nSending script:\n```rhai\n{}\n```", script);
|
||||
@ -125,28 +125,28 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
Err(e) => match e {
|
||||
RhaiClientError::Timeout(task_id) => {
|
||||
RhaiDispatcherError::Timeout(task_id) => {
|
||||
eprintln!(
|
||||
"\nError: Script execution timed out for task_id: {}.",
|
||||
task_id
|
||||
);
|
||||
}
|
||||
RhaiClientError::RedisError(redis_err) => {
|
||||
RhaiDispatcherError::RedisError(redis_err) => {
|
||||
eprintln!(
|
||||
"\nError: Redis communication failed: {}. Check Redis connection and server status.",
|
||||
redis_err
|
||||
);
|
||||
}
|
||||
RhaiClientError::SerializationError(serde_err) => {
|
||||
RhaiDispatcherError::SerializationError(serde_err) => {
|
||||
eprintln!(
|
||||
"\nError: Failed to serialize/deserialize task data: {}.",
|
||||
serde_err
|
||||
);
|
||||
}
|
||||
RhaiClientError::TaskNotFound(task_id) => {
|
||||
RhaiDispatcherError::TaskNotFound(task_id) => {
|
||||
eprintln!("\nError: Task {} not found after submission.", task_id);
|
||||
} /* All RhaiClientError variants are handled, so _ arm is not strictly needed
|
||||
unless RhaiClientError becomes non-exhaustive in the future. */
|
||||
} /* All RhaiDispatcherError variants are handled, so _ arm is not strictly needed
|
||||
unless RhaiDispatcherError becomes non-exhaustive in the future. */
|
||||
},
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use anyhow::Context;
|
||||
use rhai_client::{RhaiClient, RhaiClientBuilder, RhaiClientError};
|
||||
use rhai_dispatcher::{RhaiDispatcher, RhaiDispatcherBuilder, RhaiDispatcherError};
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::{Config, DefaultEditor, EditMode};
|
||||
use std::env;
|
||||
@ -12,7 +12,7 @@ use tracing_subscriber::EnvFilter;
|
||||
// Default timeout for script execution
|
||||
const DEFAULT_SCRIPT_TIMEOUT_SECONDS: u64 = 30;
|
||||
|
||||
async fn execute_script(client: &RhaiClient, circle_name: &str, script_content: String) {
|
||||
async fn execute_script(client: &RhaiDispatcher, circle_name: &str, script_content: String) {
|
||||
if script_content.trim().is_empty() {
|
||||
println!("Script is empty, not sending.");
|
||||
return;
|
||||
@ -47,25 +47,25 @@ async fn execute_script(client: &RhaiClient, circle_name: &str, script_content:
|
||||
}
|
||||
}
|
||||
Err(e) => match e {
|
||||
RhaiClientError::Timeout(task_id) => {
|
||||
RhaiDispatcherError::Timeout(task_id) => {
|
||||
eprintln!(
|
||||
"Error: Script execution timed out for task_id: {}.",
|
||||
task_id
|
||||
);
|
||||
}
|
||||
RhaiClientError::RedisError(redis_err) => {
|
||||
RhaiDispatcherError::RedisError(redis_err) => {
|
||||
eprintln!(
|
||||
"Error: Redis communication failed: {}. Check Redis connection and server status.",
|
||||
redis_err
|
||||
);
|
||||
}
|
||||
RhaiClientError::SerializationError(serde_err) => {
|
||||
RhaiDispatcherError::SerializationError(serde_err) => {
|
||||
eprintln!(
|
||||
"Error: Failed to serialize/deserialize task data: {}.",
|
||||
serde_err
|
||||
);
|
||||
}
|
||||
RhaiClientError::TaskNotFound(task_id) => {
|
||||
RhaiDispatcherError::TaskNotFound(task_id) => {
|
||||
eprintln!(
|
||||
"Error: Task {} not found after submission (this should be rare).",
|
||||
task_id
|
||||
@ -81,15 +81,15 @@ async fn run_repl(redis_url: String, circle_name: String) -> anyhow::Result<()>
|
||||
circle_name, redis_url
|
||||
);
|
||||
|
||||
let client = RhaiClientBuilder::new()
|
||||
let client = RhaiDispatcherBuilder::new()
|
||||
.redis_url(&redis_url)
|
||||
.caller_id("ui_repl") // Set a caller_id
|
||||
.build()
|
||||
.with_context(|| format!("Failed to create RhaiClient for Redis URL: {}", redis_url))?;
|
||||
.with_context(|| format!("Failed to create RhaiDispatcher for Redis URL: {}", redis_url))?;
|
||||
|
||||
// No explicit connect() needed for rhai_client, connection is handled per-operation or pooled.
|
||||
// No explicit connect() needed for rhai_dispatcher, connection is handled per-operation or pooled.
|
||||
println!(
|
||||
"RhaiClient initialized. Ready to send scripts to worker '{}'.",
|
||||
"RhaiDispatcher initialized. Ready to send scripts to worker '{}'.",
|
||||
circle_name
|
||||
);
|
||||
println!(
|
||||
@ -212,7 +212,7 @@ async fn run_repl(redis_url: String, circle_name: String) -> anyhow::Result<()>
|
||||
// Failed to save history, not critical
|
||||
}
|
||||
|
||||
// No explicit disconnect for RhaiClient as it manages connections internally.
|
||||
// No explicit disconnect for RhaiDispatcher as it manages connections internally.
|
||||
println!("Exited REPL.");
|
||||
Ok(())
|
||||
}
|
||||
@ -223,7 +223,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
.with_env_filter(
|
||||
EnvFilter::from_default_env()
|
||||
.add_directive("ui_repl=info".parse()?)
|
||||
.add_directive("rhai_client=info".parse()?),
|
||||
.add_directive("rhai_dispatcher=info".parse()?),
|
||||
)
|
||||
.init();
|
||||
|
@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "rhai_client"
|
||||
name = "rhai_dispatcher"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "client"
|
||||
path = "cmd/client.rs"
|
||||
name = "dispatcher"
|
||||
path = "cmd/dispatcher.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.4", features = ["derive"] }
|
@ -4,7 +4,7 @@ The `rhai-client` crate provides a fluent builder-based interface for submitting
|
||||
|
||||
## Features
|
||||
|
||||
- **Fluent Builder API**: A `RhaiClientBuilder` for easy client configuration and a `PlayRequestBuilder` for constructing and submitting script execution requests.
|
||||
- **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.
|
||||
@ -13,8 +13,8 @@ The `rhai-client` crate provides a fluent builder-based interface for submitting
|
||||
|
||||
## Core Components
|
||||
|
||||
- **`RhaiClientBuilder`**: A builder to construct a `RhaiClient`. Requires a `caller_id` and Redis URL.
|
||||
- **`RhaiClient`**: The main client for interacting with the task system. It's used to create `PlayRequestBuilder` instances.
|
||||
- **`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.
|
||||
@ -24,11 +24,11 @@ The `rhai-client` crate provides a fluent builder-based interface for submitting
|
||||
- `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.
|
||||
- **`RhaiClientError`**: An enum for various errors, such as Redis errors, serialization issues, or task timeouts.
|
||||
- **`RhaiDispatcherError`**: An enum for various errors, such as Redis errors, serialization issues, or task timeouts.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. A `RhaiClient` is created using the `RhaiClientBuilder`, configured with a `caller_id` and Redis URL.
|
||||
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:
|
||||
@ -48,7 +48,7 @@ The `rhai-client` crate provides a fluent builder-based interface for submitting
|
||||
The following example demonstrates how to build a client, submit a script, and wait for the result.
|
||||
|
||||
```rust
|
||||
use rhai_client::{RhaiClientBuilder, RhaiClientError};
|
||||
use rhai_dispatcher::{RhaiDispatcherBuilder, RhaiDispatcherError};
|
||||
use std::time::Duration;
|
||||
|
||||
#[tokio::main]
|
||||
@ -56,7 +56,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
|
||||
// 1. Build the client
|
||||
let client = RhaiClientBuilder::new()
|
||||
let client = RhaiDispatcherBuilder::new()
|
||||
.caller_id("my-app-instance-1")
|
||||
.redis_url("redis://127.0.0.1/")
|
||||
.build()?;
|
||||
@ -82,7 +82,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
log::info!("Output: {}", output);
|
||||
}
|
||||
}
|
||||
Err(RhaiClientError::Timeout(task_id)) => {
|
||||
Err(RhaiDispatcherError::Timeout(task_id)) => {
|
||||
log::error!("Task {} timed out.", task_id);
|
||||
}
|
||||
Err(e) => {
|
@ -150,7 +150,7 @@ The client provides clear error messages for:
|
||||
|
||||
### Dependencies
|
||||
|
||||
- `rhai_client`: Core client library for Redis-based script execution
|
||||
- `rhai_dispatcher`: Core client library for Redis-based script execution
|
||||
- `redis`: Redis client for task queue communication
|
||||
- `clap`: Command-line argument parsing
|
||||
- `env_logger`: Logging infrastructure
|
@ -1,5 +1,5 @@
|
||||
use clap::Parser;
|
||||
use rhai_client::{RhaiClient, RhaiClientBuilder};
|
||||
use rhai_dispatcher::{RhaiDispatcher, RhaiDispatcherBuilder};
|
||||
use log::{error, info};
|
||||
use std::io::{self, Write};
|
||||
use std::time::Duration;
|
||||
@ -9,15 +9,15 @@ use std::time::Duration;
|
||||
struct Args {
|
||||
/// Caller public key (caller ID)
|
||||
#[arg(short = 'c', long = "caller-key", help = "Caller public key (your identity)")]
|
||||
caller_public_key: String,
|
||||
caller_id: String,
|
||||
|
||||
/// Circle public key (context ID)
|
||||
#[arg(short = 'k', long = "circle-key", help = "Circle public key (execution context)")]
|
||||
circle_public_key: String,
|
||||
context_id: String,
|
||||
|
||||
/// Worker public key (defaults to circle public key if not provided)
|
||||
#[arg(short = 'w', long = "worker-key", help = "Worker public key (defaults to circle key)")]
|
||||
worker_public_key: Option<String>,
|
||||
worker_id: String,
|
||||
|
||||
/// Redis URL
|
||||
#[arg(short, long, default_value = "redis://localhost:6379", help = "Redis connection URL")]
|
||||
@ -50,8 +50,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Configure logging based on verbosity level
|
||||
let log_config = match args.verbose {
|
||||
0 => "warn,rhai_client=info",
|
||||
1 => "info,rhai_client=debug",
|
||||
0 => "warn,rhai_dispatcher=info",
|
||||
1 => "info,rhai_dispatcher=debug",
|
||||
2 => "debug",
|
||||
_ => "trace",
|
||||
};
|
||||
@ -67,21 +67,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
}
|
||||
|
||||
// Use worker key or default to circle key
|
||||
let worker_key = args.worker_public_key.unwrap_or_else(|| args.circle_public_key.clone());
|
||||
|
||||
info!("🔗 Starting Rhai Client");
|
||||
info!("🔗 Starting Rhai Dispatcher");
|
||||
info!("📋 Configuration:");
|
||||
info!(" Caller Key: {}", args.caller_public_key);
|
||||
info!(" Circle Key: {}", args.circle_public_key);
|
||||
info!(" Worker Key: {}", worker_key);
|
||||
info!(" Caller ID: {}", args.caller_id);
|
||||
info!(" Context ID: {}", args.context_id);
|
||||
info!(" Worker ID: {}", args.worker_id);
|
||||
info!(" Redis URL: {}", args.redis_url);
|
||||
info!(" Timeout: {}s", args.timeout);
|
||||
info!("");
|
||||
|
||||
// Create the Rhai client
|
||||
let client = RhaiClientBuilder::new()
|
||||
.caller_id(&args.caller_public_key)
|
||||
let client = RhaiDispatcherBuilder::new()
|
||||
.caller_id(&args.caller_id)
|
||||
.worker_id(&args.worker_id)
|
||||
.context_id(&args.context_id)
|
||||
.redis_url(&args.redis_url)
|
||||
.build()?;
|
||||
|
||||
@ -91,26 +90,25 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(script_content) = args.script {
|
||||
// Execute inline script
|
||||
info!("📜 Executing inline script");
|
||||
execute_script(&client, &worker_key, script_content, args.timeout).await?;
|
||||
execute_script(&client, script_content, args.timeout).await?;
|
||||
} else if let Some(file_path) = args.file {
|
||||
// Execute script from file
|
||||
info!("📁 Loading script from file: {}", file_path);
|
||||
let script_content = std::fs::read_to_string(&file_path)
|
||||
.map_err(|e| format!("Failed to read script file '{}': {}", file_path, e))?;
|
||||
execute_script(&client, &worker_key, script_content, args.timeout).await?;
|
||||
execute_script(&client, script_content, args.timeout).await?;
|
||||
} else {
|
||||
// Interactive mode
|
||||
info!("🎮 Entering interactive mode");
|
||||
info!("Type Rhai scripts and press Enter to execute. Type 'exit' or 'quit' to close.");
|
||||
run_interactive_mode(&client, &worker_key, args.timeout).await?;
|
||||
run_interactive_mode(&client, args.timeout).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_script(
|
||||
client: &RhaiClient,
|
||||
worker_key: &str,
|
||||
client: &RhaiDispatcher,
|
||||
script: String,
|
||||
timeout_secs: u64,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -120,7 +118,6 @@ async fn execute_script(
|
||||
|
||||
match client
|
||||
.new_play_request()
|
||||
.recipient_id(worker_key)
|
||||
.script(&script)
|
||||
.timeout(timeout)
|
||||
.await_response()
|
||||
@ -146,8 +143,7 @@ async fn execute_script(
|
||||
}
|
||||
|
||||
async fn run_interactive_mode(
|
||||
client: &RhaiClient,
|
||||
worker_key: &str,
|
||||
client: &RhaiDispatcher,
|
||||
timeout_secs: u64,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let timeout = Duration::from_secs(timeout_secs);
|
||||
@ -174,7 +170,6 @@ async fn run_interactive_mode(
|
||||
|
||||
match client
|
||||
.new_play_request()
|
||||
.recipient_id(worker_key)
|
||||
.script(input)
|
||||
.timeout(timeout)
|
||||
.await_response()
|
@ -1,6 +1,6 @@
|
||||
# Architecture of the `rhai_client` Crate
|
||||
# Architecture of the `rhai_dispatcher` Crate
|
||||
|
||||
The `rhai_client` crate provides a Redis-based client library for submitting Rhai scripts to distributed worker services and awaiting their execution results. It implements a request-reply pattern using Redis as the message broker.
|
||||
The `rhai_dispatcher` crate provides a Redis-based client library for submitting Rhai scripts to distributed worker services and awaiting their execution results. It implements a request-reply pattern using Redis as the message broker.
|
||||
|
||||
## Core Architecture
|
||||
|
||||
@ -8,7 +8,7 @@ The client follows a builder pattern design with clear separation of concerns:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[RhaiClientBuilder] --> B[RhaiClient]
|
||||
A[RhaiDispatcherBuilder] --> B[RhaiDispatcher]
|
||||
B --> C[PlayRequestBuilder]
|
||||
C --> D[PlayRequest]
|
||||
D --> E[Redis Task Queue]
|
||||
@ -35,9 +35,9 @@ graph TD
|
||||
|
||||
## Key Components
|
||||
|
||||
### 1. RhaiClientBuilder
|
||||
### 1. RhaiDispatcherBuilder
|
||||
|
||||
A builder pattern implementation for constructing `RhaiClient` instances with proper configuration validation.
|
||||
A builder pattern implementation for constructing `RhaiDispatcher` instances with proper configuration validation.
|
||||
|
||||
**Responsibilities:**
|
||||
- Configure Redis connection URL
|
||||
@ -47,9 +47,9 @@ A builder pattern implementation for constructing `RhaiClient` instances with pr
|
||||
**Key Methods:**
|
||||
- `caller_id(id: &str)` - Sets the caller identifier
|
||||
- `redis_url(url: &str)` - Configures Redis connection
|
||||
- `build()` - Creates the final `RhaiClient` instance
|
||||
- `build()` - Creates the final `RhaiDispatcher` instance
|
||||
|
||||
### 2. RhaiClient
|
||||
### 2. RhaiDispatcher
|
||||
|
||||
The main client interface that manages Redis connections and provides factory methods for creating play requests.
|
||||
|
||||
@ -103,7 +103,7 @@ pub struct RhaiTaskDetails {
|
||||
}
|
||||
```
|
||||
|
||||
#### RhaiClientError
|
||||
#### RhaiDispatcherError
|
||||
Comprehensive error handling for various failure scenarios:
|
||||
- `RedisError` - Redis connection/operation failures
|
||||
- `SerializationError` - JSON serialization/deserialization issues
|
@ -1,5 +1,5 @@
|
||||
use log::info;
|
||||
use rhai_client::{RhaiClientBuilder, RhaiClientError};
|
||||
use rhai_dispatcher::{RhaiDispatcherBuilder, RhaiDispatcherError};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[tokio::main]
|
||||
@ -9,11 +9,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.init();
|
||||
|
||||
// Build the client using the new builder pattern
|
||||
let client = RhaiClientBuilder::new()
|
||||
let client = RhaiDispatcherBuilder::new()
|
||||
.caller_id("timeout-example-runner")
|
||||
.redis_url("redis://127.0.0.1/")
|
||||
.build()?;
|
||||
info!("RhaiClient created.");
|
||||
info!("RhaiDispatcher created.");
|
||||
|
||||
let script_content = r#"
|
||||
// This script will never be executed by a worker because the recipient does not exist.
|
||||
@ -56,8 +56,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!("Elapsed time: {:?}", elapsed);
|
||||
|
||||
match e {
|
||||
RhaiClientError::Timeout(task_id) => {
|
||||
info!("Timeout Example PASSED: Correctly received RhaiClientError::Timeout for task_id: {}", task_id);
|
||||
RhaiDispatcherError::Timeout(task_id) => {
|
||||
info!("Timeout Example PASSED: Correctly received RhaiDispatcherError::Timeout for task_id: {}", task_id);
|
||||
// Ensure the elapsed time is close to the timeout duration
|
||||
// Allow for some buffer for processing
|
||||
assert!(
|
||||
@ -75,11 +75,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
other_error => {
|
||||
log::error!(
|
||||
"Timeout Example FAILED: Expected RhaiClientError::Timeout, but got other error: {:?}",
|
||||
"Timeout Example FAILED: Expected RhaiDispatcherError::Timeout, but got other error: {:?}",
|
||||
other_error
|
||||
);
|
||||
Err(format!(
|
||||
"Expected RhaiClientError::Timeout, got other error: {:?}",
|
||||
"Expected RhaiDispatcherError::Timeout, got other error: {:?}",
|
||||
other_error
|
||||
)
|
||||
.into())
|
@ -7,13 +7,13 @@
|
||||
//! ## Quick Start
|
||||
//!
|
||||
//! ```rust
|
||||
//! use rhai_client::{RhaiClientBuilder, RhaiClientError};
|
||||
//! use rhai_dispatcher::{RhaiDispatcherBuilder, RhaiDispatcherError};
|
||||
//! use std::time::Duration;
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! // Build the client
|
||||
//! let client = RhaiClientBuilder::new()
|
||||
//! let client = RhaiDispatcherBuilder::new()
|
||||
//! .caller_id("my-app-instance-1")
|
||||
//! .redis_url("redis://127.0.0.1/")
|
||||
//! .build()?;
|
||||
@ -76,6 +76,8 @@ pub struct RhaiTaskDetails {
|
||||
pub caller_id: String,
|
||||
#[serde(rename = "contextId")]
|
||||
pub context_id: String,
|
||||
#[serde(rename = "workerId")]
|
||||
pub worker_id: String,
|
||||
}
|
||||
|
||||
/// Comprehensive error type for all possible failures in the Rhai client.
|
||||
@ -83,7 +85,7 @@ pub struct RhaiTaskDetails {
|
||||
/// This enum covers all error scenarios that can occur during client operations,
|
||||
/// from Redis connectivity issues to task execution timeouts.
|
||||
#[derive(Debug)]
|
||||
pub enum RhaiClientError {
|
||||
pub enum RhaiDispatcherError {
|
||||
/// Redis connection or operation error
|
||||
RedisError(redis::RedisError),
|
||||
/// JSON serialization/deserialization error
|
||||
@ -96,37 +98,37 @@ pub enum RhaiClientError {
|
||||
ContextIdMissing,
|
||||
}
|
||||
|
||||
impl From<redis::RedisError> for RhaiClientError {
|
||||
impl From<redis::RedisError> for RhaiDispatcherError {
|
||||
fn from(err: redis::RedisError) -> Self {
|
||||
RhaiClientError::RedisError(err)
|
||||
RhaiDispatcherError::RedisError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for RhaiClientError {
|
||||
impl From<serde_json::Error> for RhaiDispatcherError {
|
||||
fn from(err: serde_json::Error) -> Self {
|
||||
RhaiClientError::SerializationError(err)
|
||||
RhaiDispatcherError::SerializationError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RhaiClientError {
|
||||
impl std::fmt::Display for RhaiDispatcherError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
RhaiClientError::RedisError(e) => write!(f, "Redis error: {}", e),
|
||||
RhaiClientError::SerializationError(e) => write!(f, "Serialization error: {}", e),
|
||||
RhaiClientError::Timeout(task_id) => {
|
||||
RhaiDispatcherError::RedisError(e) => write!(f, "Redis error: {}", e),
|
||||
RhaiDispatcherError::SerializationError(e) => write!(f, "Serialization error: {}", e),
|
||||
RhaiDispatcherError::Timeout(task_id) => {
|
||||
write!(f, "Timeout waiting for task {} to complete", task_id)
|
||||
}
|
||||
RhaiClientError::TaskNotFound(task_id) => {
|
||||
RhaiDispatcherError::TaskNotFound(task_id) => {
|
||||
write!(f, "Task {} not found after submission", task_id)
|
||||
}
|
||||
RhaiClientError::ContextIdMissing => {
|
||||
RhaiDispatcherError::ContextIdMissing => {
|
||||
write!(f, "Context ID is missing")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RhaiClientError {}
|
||||
impl std::error::Error for RhaiDispatcherError {}
|
||||
|
||||
/// The main client for interacting with the Rhai task execution system.
|
||||
///
|
||||
@ -137,19 +139,21 @@ impl std::error::Error for RhaiClientError {}
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rhai_client::RhaiClientBuilder;
|
||||
/// use rhai_dispatcher::RhaiDispatcherBuilder;
|
||||
///
|
||||
/// let client = RhaiClientBuilder::new()
|
||||
/// let client = RhaiDispatcherBuilder::new()
|
||||
/// .caller_id("my-service")
|
||||
/// .redis_url("redis://localhost/")
|
||||
/// .build()?;
|
||||
/// ```
|
||||
pub struct RhaiClient {
|
||||
pub struct RhaiDispatcher {
|
||||
redis_client: redis::Client,
|
||||
caller_id: String,
|
||||
worker_id: String,
|
||||
context_id: String,
|
||||
}
|
||||
|
||||
/// Builder for constructing `RhaiClient` instances with proper configuration.
|
||||
/// Builder for constructing `RhaiDispatcher` instances with proper configuration.
|
||||
///
|
||||
/// This builder ensures that all required configuration is provided before
|
||||
/// creating a client instance. It validates the configuration and provides
|
||||
@ -162,13 +166,15 @@ pub struct RhaiClient {
|
||||
/// # Optional Configuration
|
||||
///
|
||||
/// - `redis_url`: Redis connection URL (defaults to "redis://127.0.0.1/")
|
||||
pub struct RhaiClientBuilder {
|
||||
pub struct RhaiDispatcherBuilder {
|
||||
redis_url: Option<String>,
|
||||
caller_id: String,
|
||||
worker_id: String,
|
||||
context_id: String,
|
||||
}
|
||||
|
||||
impl RhaiClientBuilder {
|
||||
/// Creates a new `RhaiClientBuilder` with default settings.
|
||||
impl RhaiDispatcherBuilder {
|
||||
/// Creates a new `RhaiDispatcherBuilder` with default settings.
|
||||
///
|
||||
/// The builder starts with no Redis URL (will default to "redis://127.0.0.1/")
|
||||
/// and an empty caller ID (which must be set before building).
|
||||
@ -176,6 +182,8 @@ impl RhaiClientBuilder {
|
||||
Self {
|
||||
redis_url: None,
|
||||
caller_id: "".to_string(),
|
||||
worker_id: "".to_string(),
|
||||
context_id: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,6 +200,31 @@ impl RhaiClientBuilder {
|
||||
self.caller_id = caller_id.to_string();
|
||||
self
|
||||
}
|
||||
/// Sets the circle ID for this client instance.
|
||||
///
|
||||
/// The circle ID is used to identify which circle's context a task should be executed in.
|
||||
/// This is required at the time the client dispatches a script, but can be set on construction or on script dispatch.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `context_id` - A unique identifier for this client instance
|
||||
pub fn context_id(mut self, context_id: &str) -> Self {
|
||||
self.context_id = context_id.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the worker ID for this client instance.
|
||||
///
|
||||
/// The worker ID is used to identify which worker a task should be executed on.
|
||||
/// This is required at the time the client dispatches a script, but can be set on construction or on script dispatch.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `worker_id` - A unique identifier for this client instance
|
||||
pub fn worker_id(mut self, worker_id: &str) -> Self {
|
||||
self.worker_id = worker_id.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the Redis connection URL.
|
||||
///
|
||||
@ -205,7 +238,7 @@ impl RhaiClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the final `RhaiClient` instance.
|
||||
/// Builds the final `RhaiDispatcher` instance.
|
||||
///
|
||||
/// This method validates the configuration and creates the Redis client.
|
||||
/// It will return an error if the caller ID is empty or if the Redis
|
||||
@ -213,22 +246,18 @@ impl RhaiClientBuilder {
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(RhaiClient)` - Successfully configured client
|
||||
/// * `Err(RhaiClientError)` - Configuration or connection error
|
||||
pub fn build(self) -> Result<RhaiClient, RhaiClientError> {
|
||||
/// * `Ok(RhaiDispatcher)` - Successfully configured client
|
||||
/// * `Err(RhaiDispatcherError)` - Configuration or connection error
|
||||
pub fn build(self) -> Result<RhaiDispatcher, RhaiDispatcherError> {
|
||||
let url = self
|
||||
.redis_url
|
||||
.unwrap_or_else(|| "redis://127.0.0.1/".to_string());
|
||||
let client = redis::Client::open(url)?;
|
||||
if self.caller_id.is_empty() {
|
||||
return Err(RhaiClientError::RedisError(redis::RedisError::from((
|
||||
redis::ErrorKind::InvalidClientConfig,
|
||||
"Caller ID is empty",
|
||||
))));
|
||||
}
|
||||
Ok(RhaiClient {
|
||||
Ok(RhaiDispatcher {
|
||||
redis_client: client,
|
||||
caller_id: self.caller_id,
|
||||
worker_id: self.worker_id,
|
||||
context_id: self.context_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -265,21 +294,23 @@ pub struct PlayRequest {
|
||||
/// .await?;
|
||||
/// ```
|
||||
pub struct PlayRequestBuilder<'a> {
|
||||
client: &'a RhaiClient,
|
||||
client: &'a RhaiDispatcher,
|
||||
request_id: String,
|
||||
worker_id: String,
|
||||
context_id: String,
|
||||
caller_id: String,
|
||||
script: String,
|
||||
timeout: Duration,
|
||||
}
|
||||
|
||||
impl<'a> PlayRequestBuilder<'a> {
|
||||
pub fn new(client: &'a RhaiClient) -> Self {
|
||||
pub fn new(client: &'a RhaiDispatcher) -> Self {
|
||||
Self {
|
||||
client,
|
||||
request_id: "".to_string(),
|
||||
worker_id: "".to_string(),
|
||||
context_id: "".to_string(),
|
||||
worker_id: client.worker_id.clone(),
|
||||
context_id: client.context_id.clone(),
|
||||
caller_id: client.caller_id.clone(),
|
||||
script: "".to_string(),
|
||||
timeout: Duration::from_secs(10),
|
||||
}
|
||||
@ -315,7 +346,7 @@ impl<'a> PlayRequestBuilder<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<PlayRequest, RhaiClientError> {
|
||||
pub fn build(self) -> Result<PlayRequest, RhaiDispatcherError> {
|
||||
let request_id = if self.request_id.is_empty() {
|
||||
// Generate a UUID for the request_id
|
||||
Uuid::new_v4().to_string()
|
||||
@ -324,7 +355,11 @@ impl<'a> PlayRequestBuilder<'a> {
|
||||
};
|
||||
|
||||
if self.context_id.is_empty() {
|
||||
return Err(RhaiClientError::ContextIdMissing);
|
||||
return Err(RhaiDispatcherError::ContextIdMissing);
|
||||
}
|
||||
|
||||
if self.caller_id.is_empty() {
|
||||
return Err(RhaiDispatcherError::ContextIdMissing);
|
||||
}
|
||||
|
||||
let play_request = PlayRequest {
|
||||
@ -337,7 +372,7 @@ impl<'a> PlayRequestBuilder<'a> {
|
||||
Ok(play_request)
|
||||
}
|
||||
|
||||
pub async fn submit(self) -> Result<(), RhaiClientError> {
|
||||
pub async fn submit(self) -> Result<(), RhaiDispatcherError> {
|
||||
// Build the request and submit using self.client
|
||||
println!(
|
||||
"Submitting request {} with timeout {:?}",
|
||||
@ -347,7 +382,7 @@ impl<'a> PlayRequestBuilder<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn await_response(self) -> Result<RhaiTaskDetails, RhaiClientError> {
|
||||
pub async fn await_response(self) -> Result<RhaiTaskDetails, RhaiDispatcherError> {
|
||||
// Build the request and submit using self.client
|
||||
println!(
|
||||
"Awaiting response for request {} with timeout {:?}",
|
||||
@ -361,7 +396,7 @@ impl<'a> PlayRequestBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl RhaiClient {
|
||||
impl RhaiDispatcher {
|
||||
pub fn new_play_request(&self) -> PlayRequestBuilder {
|
||||
PlayRequestBuilder::new(self)
|
||||
}
|
||||
@ -371,7 +406,7 @@ impl RhaiClient {
|
||||
&self,
|
||||
conn: &mut redis::aio::MultiplexedConnection,
|
||||
play_request: &PlayRequest,
|
||||
) -> Result<(), RhaiClientError> {
|
||||
) -> Result<(), RhaiDispatcherError> {
|
||||
let now = Utc::now();
|
||||
|
||||
let task_key = format!("{}{}", NAMESPACE_PREFIX, play_request.id);
|
||||
@ -422,7 +457,7 @@ impl RhaiClient {
|
||||
task_key: &String,
|
||||
reply_queue_key: &String,
|
||||
timeout: Duration,
|
||||
) -> Result<RhaiTaskDetails, RhaiClientError> {
|
||||
) -> Result<RhaiTaskDetails, RhaiDispatcherError> {
|
||||
// BLPOP on the reply queue
|
||||
// The timeout for BLPOP is in seconds (integer)
|
||||
let blpop_timeout_secs = timeout.as_secs().max(1); // Ensure at least 1 second for BLPOP timeout
|
||||
@ -458,7 +493,7 @@ impl RhaiClient {
|
||||
);
|
||||
// Optionally, delete the reply queue
|
||||
let _: redis::RedisResult<i32> = conn.del(&reply_queue_key).await;
|
||||
Err(RhaiClientError::SerializationError(e))
|
||||
Err(RhaiDispatcherError::SerializationError(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -470,7 +505,7 @@ impl RhaiClient {
|
||||
);
|
||||
// Optionally, delete the reply queue
|
||||
let _: redis::RedisResult<i32> = conn.del(&reply_queue_key).await;
|
||||
Err(RhaiClientError::Timeout(task_key.clone()))
|
||||
Err(RhaiDispatcherError::Timeout(task_key.clone()))
|
||||
}
|
||||
Err(e) => {
|
||||
// Redis error
|
||||
@ -480,7 +515,7 @@ impl RhaiClient {
|
||||
);
|
||||
// Optionally, delete the reply queue
|
||||
let _: redis::RedisResult<i32> = conn.del(&reply_queue_key).await;
|
||||
Err(RhaiClientError::RedisError(e))
|
||||
Err(RhaiDispatcherError::RedisError(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -489,7 +524,7 @@ impl RhaiClient {
|
||||
pub async fn submit_play_request(
|
||||
&self,
|
||||
play_request: &PlayRequest,
|
||||
) -> Result<(), RhaiClientError> {
|
||||
) -> Result<(), RhaiDispatcherError> {
|
||||
let mut conn = self.redis_client.get_multiplexed_async_connection().await?;
|
||||
|
||||
self.submit_play_request_using_connection(
|
||||
@ -504,7 +539,7 @@ impl RhaiClient {
|
||||
pub async fn submit_play_request_and_await_result(
|
||||
&self,
|
||||
play_request: &PlayRequest,
|
||||
) -> Result<RhaiTaskDetails, RhaiClientError> {
|
||||
) -> Result<RhaiTaskDetails, RhaiDispatcherError> {
|
||||
let mut conn = self.redis_client.get_multiplexed_async_connection().await?;
|
||||
|
||||
let reply_queue_key = format!("{}:reply:{}", NAMESPACE_PREFIX, play_request.id); // Derived from the passed task_id
|
||||
@ -535,7 +570,7 @@ impl RhaiClient {
|
||||
pub async fn get_task_status(
|
||||
&self,
|
||||
task_id: &str,
|
||||
) -> Result<Option<RhaiTaskDetails>, RhaiClientError> {
|
||||
) -> Result<Option<RhaiTaskDetails>, RhaiDispatcherError> {
|
||||
let mut conn = self.redis_client.get_multiplexed_async_connection().await?;
|
||||
let task_key = format!("{}{}", NAMESPACE_PREFIX, task_id);
|
||||
|
||||
@ -573,6 +608,7 @@ impl RhaiClient {
|
||||
Utc::now()
|
||||
}),
|
||||
caller_id: map.get("callerId").cloned().expect("callerId field missing from Redis hash"),
|
||||
worker_id: map.get("workerId").cloned().expect("workerId field missing from Redis hash"),
|
||||
context_id: map.get("contextId").cloned().expect("contextId field missing from Redis hash"),
|
||||
};
|
||||
// It's important to also check if the 'taskId' field exists in the map and matches the input task_id
|
BIN
src/repl/.DS_Store
vendored
BIN
src/repl/.DS_Store
vendored
Binary file not shown.
Loading…
Reference in New Issue
Block a user