move dsl's to rhailib
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
# Rhailib Examples
|
||||
|
||||
This directory contains end-to-end examples demonstrating the usage of the `rhailib` project, showcasing how the `client`, `engine`, and `worker` crates interact.
|
||||
This directory contains end-to-end examples demonstrating the usage of the `rhailib` project. These examples showcase how multiple crates from the workspace (such as `rhai_client`, `rhailib_engine`, and `rhailib_worker`) interact to build complete applications.
|
||||
|
||||
Each example is self-contained in its own directory and includes a dedicated `README.md` with detailed explanations.
|
||||
|
||||
## Available Examples
|
||||
|
||||
- **`example_math_worker.rs`**: This example demonstrates a worker that performs mathematical operations. It shows how to define Rhai scripts that call Rust functions exposed by the worker, process the results, and interact with the `rhailib` engine and client.
|
||||
- **`example_string_worker.rs`**: This example showcases a worker focused on string manipulations. Similar to the math worker, it illustrates the setup for defining Rhai scripts, registering Rust functions for string operations, and the overall flow of execution within the `rhailib` ecosystem.
|
||||
- **[Access Control](./access_control/README.md)**: Demonstrates a practical access control scenario where a user, Alice, manages her own data, grants specific access to another user, Bob, and denies access to an unauthorized user, Charlie. This example highlights the built-in ownership and write protection provided by the Rhai worker.
|
||||
|
||||
These examples serve as a practical guide to understanding the core functionalities and integration patterns within the `rhailib` project.
|
||||
As more examples are added, they will be listed here.
|
||||
|
41
examples/access_control/README.md
Normal file
41
examples/access_control/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Access Control Demonstration
|
||||
|
||||
This example demonstrates a practical access control scenario using `rhailib`. It showcases how a user, Alice, can manage her own data within her Rhai worker, grant specific access rights to another user, Bob, and deny access to an unauthorized user, Charlie.
|
||||
|
||||
## Overview
|
||||
|
||||
The example involves three key participants:
|
||||
|
||||
1. **Alice (`alice_pk`)**: The owner of the Rhai worker. She runs `alice.rhai` to populate her database with various objects and collections. Some of these are private, while others are explicitly shared with Bob.
|
||||
|
||||
2. **Bob (`bob_pk`)**: A user who has been granted some access rights by Alice. In this example, he attempts to run `bob.rhai`, which tries to write data to Alice's worker.
|
||||
|
||||
3. **Charlie (`charlie_pk`)**: An unauthorized user. He attempts to run `charlie.rhai`, which is identical to Bob's script.
|
||||
|
||||
The core of the access control mechanism lies within the `rhailib_worker`. When a script is submitted for execution, the worker automatically enforces that the `CALLER_PUBLIC_KEY` matches the worker's own `CIRCLE_PUBLIC_KEY` for any write operations. This ensures that only the owner (Alice) can modify her data.
|
||||
|
||||
## Scenario and Expected Outcomes
|
||||
|
||||
1. **Alice Populates Her Database**: Alice's script (`alice.rhai`) runs first. It successfully creates:
|
||||
- A private object.
|
||||
- An object shared with Bob.
|
||||
- A private collection containing a private book and slides that are individually shared with Bob.
|
||||
- A shared collection.
|
||||
This demonstrates that the owner of the worker can freely write to her own database.
|
||||
|
||||
2. **Bob's Query**: Bob's script (`bob.rhai`) is executed next. The script attempts to create new objects in Alice's database. This operation fails with an `Insufficient authorization` error. The logs will show that `bob_pk` does not match the circle's public key, `alice_pk`.
|
||||
|
||||
3. **Charlie's Query**: Charlie's script (`charlie.rhai`) also fails with the same authorization error, as he is not the owner of the worker.
|
||||
|
||||
This example clearly illustrates the built-in ownership and write protection provided by the Rhai worker.
|
||||
|
||||
## Running the Example
|
||||
|
||||
Ensure Redis is running and accessible at `redis://127.0.0.1/`.
|
||||
|
||||
From the `rhailib` root directory, run:
|
||||
```bash
|
||||
cargo run --example access_control
|
||||
```
|
||||
|
||||
Observe the logs to see Alice's script complete successfully, followed by the authorization errors for Bob and Charlie, confirming that the access control is working as expected.
|
@@ -1,3 +1,8 @@
|
||||
new_circle()
|
||||
.title("Alice's Circle")
|
||||
.description("Some objects in this circle are shared with Bob")
|
||||
.save_circle();
|
||||
|
||||
let private_object = new_object()
|
||||
.title("Alice's Private Object")
|
||||
.description("This object can only be seen and modified by Alice")
|
51
examples/access_control/circle.rhai
Normal file
51
examples/access_control/circle.rhai
Normal file
@@ -0,0 +1,51 @@
|
||||
new_circle()
|
||||
.title("Alice and Charlie's Circle")
|
||||
.description("Some objects in this circle are shared with Bob")
|
||||
.add_member("alice_pk")
|
||||
.add_member("charlie_pk")
|
||||
.save_circle();
|
||||
|
||||
let private_object = new_object()
|
||||
.title("Alice and Charlie's Private Object")
|
||||
.description("This object can only be seen and modified by Alice and Charlie")
|
||||
.save_object();
|
||||
|
||||
let object_shared_with_bob = new_object()
|
||||
.title("Alice and Charlie's Shared Object")
|
||||
.description("This object can be seen by Bob but modified only by Alice and Charlie")
|
||||
.save_object();
|
||||
|
||||
let new_access = new_access()
|
||||
.object_id(object_shared_with_bob.id())
|
||||
.circle_public_key("bob_pk")
|
||||
.save_access();
|
||||
|
||||
let book_private = new_book()
|
||||
.title("Alice and Charlie's private book")
|
||||
.description("This book is prive to Alice and Charlie")
|
||||
.save_book();
|
||||
|
||||
let slides_shared = new_slides()
|
||||
.title("Alice and Charlie's shared slides")
|
||||
.description("These slides, despite being in a private collection, are shared with Bob")
|
||||
.save_slides();
|
||||
|
||||
let new_access = new_access()
|
||||
.object_id(slides_shared.id)
|
||||
.circle_public_key("bob_pk")
|
||||
.save_access();
|
||||
|
||||
let collection_private = new_collection()
|
||||
.title("Alice and Charlie's private collection")
|
||||
.description("This collection is only visible to Alice and Charlie")
|
||||
.add_book(book_private.id)
|
||||
.add_slides(slides_shared.id)
|
||||
.save_collection();
|
||||
|
||||
let collection_shared = new_collection()
|
||||
.title("Alice and Charlie's shared collection")
|
||||
.description("This collection is shared with Bob")
|
||||
.save_collection();
|
||||
|
||||
|
||||
|
@@ -1,29 +1,31 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
use rhai_client::RhaiClientBuilder;
|
||||
use rhailib_engine::create_heromodels_engine;
|
||||
use rhailib_worker::spawn_rhai_worker;
|
||||
use std::{fs, path::Path, time::Duration};
|
||||
use std::time::Duration;
|
||||
use tempfile::Builder;
|
||||
use tokio::sync::mpsc;
|
||||
use uuid::Uuid;
|
||||
|
||||
const ALICE_ID: &str = "alice_pk";
|
||||
const BOB_ID: &str = "bob_pk";
|
||||
const CHARLIE_ID: &str = "charlie_pk";
|
||||
const CIRCLE_ID: &str = "circle_pk";
|
||||
const REDIS_URL: &str = "redis://127.0.0.1/";
|
||||
const DB_DIRECTORY: &str = "./db";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||
|
||||
|
||||
// Create a temporary directory for the database
|
||||
let temp_dir = Builder::new().prefix("rhai-example").tempdir()?;
|
||||
let db_path = temp_dir.path().to_str().unwrap().to_string();
|
||||
|
||||
// 1. Create a Rhai engine and register custom functionality
|
||||
let mut engine = rhailib_engine::create_heromodels_engine();
|
||||
let engine = rhailib_engine::create_heromodels_engine();
|
||||
|
||||
// 2. Spawn the Rhai worker
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
|
||||
let worker_handle = tokio::spawn(spawn_rhai_worker(
|
||||
ALICE_ID.to_string(),
|
||||
DB_DIRECTORY.to_string(),
|
||||
db_path.clone(),
|
||||
engine,
|
||||
REDIS_URL.to_string(),
|
||||
shutdown_rx,
|
||||
@@ -44,7 +46,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
client_alice.new_play_request()
|
||||
.recipient_id(&ALICE_ID)
|
||||
.script_path("examples/end_to_end/alice.rhai")
|
||||
.script_path("examples/access_control/alice.rhai")
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await_response().await.unwrap();
|
||||
|
||||
@@ -59,7 +61,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
client_bob.new_play_request()
|
||||
.recipient_id(&ALICE_ID)
|
||||
.script_path("examples/end_to_end/bob.rhai")
|
||||
.script_path("examples/access_control/bob.rhai")
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await_response().await.unwrap();
|
||||
|
||||
@@ -74,12 +76,68 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
client_charlie.new_play_request()
|
||||
.recipient_id(&ALICE_ID)
|
||||
.script_path("examples/end_to_end/charlie.rhai")
|
||||
.script_path("examples/access_control/charlie.rhai")
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await_response().await.unwrap();
|
||||
|
||||
log::info!("Charlie's query to Alice's database completed.");
|
||||
|
||||
|
||||
// Spawn the Rhai worker for Alice's and Charlie's circle
|
||||
let engine = rhailib_engine::create_heromodels_engine();
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
|
||||
let worker_handle = tokio::spawn(spawn_rhai_worker(
|
||||
CIRCLE_ID.to_string(),
|
||||
db_path.clone(),
|
||||
engine,
|
||||
REDIS_URL.to_string(),
|
||||
shutdown_rx,
|
||||
false, // use_sentinel
|
||||
));
|
||||
|
||||
// Alice populates the rhai worker of their circle with Charlie.
|
||||
let client_circle = RhaiClientBuilder::new()
|
||||
.redis_url(REDIS_URL)
|
||||
.caller_id(CIRCLE_ID)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
client_circle.new_play_request()
|
||||
.recipient_id(&CIRCLE_ID)
|
||||
.script_path("examples/access_control/circle.rhai")
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await_response().await.unwrap();
|
||||
|
||||
log::info!("Circles's database populated.");
|
||||
|
||||
// Give the worker a moment to start up
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
|
||||
// Alice queries the rhai worker of their circle with Charlie.
|
||||
client_alice.new_play_request()
|
||||
.recipient_id(&CIRCLE_ID)
|
||||
.script_path("examples/access_control/alice.rhai")
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await_response().await.unwrap();
|
||||
|
||||
log::info!("Bob's query to Alice's database completed.");
|
||||
|
||||
// Charlie queries Alice's rhai worker
|
||||
let client_charlie = RhaiClientBuilder::new()
|
||||
.redis_url(REDIS_URL)
|
||||
.caller_id(CHARLIE_ID)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
client_charlie.new_play_request()
|
||||
.recipient_id(&ALICE_ID)
|
||||
.script_path("examples/access_control/charlie.rhai")
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await_response().await.unwrap();
|
||||
|
||||
log::info!("Charlie's query to Alice's database completed.");
|
||||
|
||||
|
||||
// 5. Shutdown the worker (optional, could also let it run until program exits)
|
||||
log::info!("Signaling worker to shutdown...");
|
||||
let _ = shutdown_tx.send(()).await;
|
@@ -1,119 +0,0 @@
|
||||
use log::{debug, error, info};
|
||||
use rhai::Engine;
|
||||
use rhai_client::{RhaiClient, RhaiClientError}; // RhaiTaskDetails is now used for its fields
|
||||
use rhailib_worker::spawn_rhai_worker;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc;
|
||||
use uuid::Uuid; // Added for generating task_id
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||
|
||||
let redis_url = "redis://127.0.0.1/";
|
||||
let circle_name = "reply_demo_circle";
|
||||
let script_to_run = "let x = 40; x + 2"; // Simple script
|
||||
|
||||
info!("Starting Dedicated Reply Queue Demo...");
|
||||
|
||||
// 1. Create a Rhai Engine for the worker
|
||||
let engine = Engine::new();
|
||||
|
||||
// 2. Setup shutdown channel for the worker
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::channel::<()>(1);
|
||||
|
||||
// 3. Spawn the worker
|
||||
info!("Spawning worker for circle: {}", circle_name);
|
||||
let worker_handle = spawn_rhai_worker(
|
||||
0, // circle_id (can be arbitrary for this demo)
|
||||
circle_name.to_string(),
|
||||
engine,
|
||||
redis_url.to_string(),
|
||||
shutdown_rx,
|
||||
false, // preserve_tasks
|
||||
);
|
||||
|
||||
// Give the worker a moment to start up and connect (optional, but good for demo)
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
|
||||
// 4. Create RhaiClient
|
||||
info!("Creating RhaiClient...");
|
||||
let client = match RhaiClient::new(redis_url) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
error!("Failed to create RhaiClient: {}", e);
|
||||
// Attempt to shutdown worker before exiting
|
||||
let _ = shutdown_tx.send(()).await;
|
||||
let _ = worker_handle.await;
|
||||
// Explicitly cast the error to the trait object to satisfy the return type
|
||||
return Err(Box::new(e) as Box<dyn std::error::Error>);
|
||||
}
|
||||
};
|
||||
info!("RhaiClient created.");
|
||||
|
||||
// 5. Submit script and await result using the new mechanism
|
||||
let task_timeout = Duration::from_secs(10);
|
||||
let task_id = Uuid::new_v4().to_string(); // Generate a unique task_id
|
||||
|
||||
info!(
|
||||
"Submitting script to circle '{}' with task_id '{}' and awaiting result...",
|
||||
circle_name, task_id
|
||||
);
|
||||
info!("Script: {}", script_to_run);
|
||||
|
||||
match client
|
||||
.submit_script_and_await_result(
|
||||
circle_name,
|
||||
task_id.clone(), // Pass the generated task_id
|
||||
script_to_run.to_string(),
|
||||
task_timeout,
|
||||
None, // public_key
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(details) => {
|
||||
info!("Task {} completed successfully!", details.task_id);
|
||||
debug!("Full Task Details: {:#?}", details);
|
||||
// The task_id is now part of the returned RhaiTaskDetails struct.
|
||||
info!(
|
||||
"Received details for task_id: {}, script: {}",
|
||||
details.task_id, details.script
|
||||
);
|
||||
info!("Status: {}", details.status);
|
||||
if let Some(output) = details.output {
|
||||
info!("Output: {}", output); // Expected: 42
|
||||
assert_eq!(output, "42");
|
||||
} else {
|
||||
error!("Expected output, but got None.");
|
||||
}
|
||||
if let Some(error_msg) = details.error {
|
||||
error!("Error: {}", error_msg);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("An error occurred while awaiting task result: {}", e);
|
||||
// The specific error can be inspected if needed, e.g., for timeout
|
||||
if let RhaiClientError::Timeout(returned_task_id) = e {
|
||||
// Note: 'task_id' here is the one from the error, which should match the one we sent.
|
||||
info!("Task {} timed out.", returned_task_id);
|
||||
info!("Task {} timed out.", task_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Shutdown the worker
|
||||
info!("Sending shutdown signal to worker...");
|
||||
if shutdown_tx.send(()).await.is_err() {
|
||||
error!("Failed to send shutdown signal to worker. It might have already exited.");
|
||||
}
|
||||
|
||||
info!("Waiting for worker to complete...");
|
||||
match worker_handle.await {
|
||||
Ok(Ok(_)) => info!("Worker exited successfully."),
|
||||
Ok(Err(e)) => error!("Worker exited with an error: {}", e),
|
||||
Err(e) => error!("Worker task panicked or was cancelled: {}", e),
|
||||
}
|
||||
|
||||
info!("Dedicated Reply Queue Demo finished.");
|
||||
Ok(())
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
# End-to-End Authorization Demo
|
||||
|
||||
This example demonstrates an end-to-end scenario involving a custom Rhai engine, `rhailib_worker`, and `rhai_client` to showcase how authorization based on `CALLER_PUBLIC_KEY` can be implemented.
|
||||
|
||||
## Overview
|
||||
|
||||
1. **Custom Rhai Engine**: A Rhai engine is created, and a custom function `check_permission(caller_pk: String)` is registered. This function returns different messages based on the `caller_pk` provided.
|
||||
2. **Rhai Worker (`rhailib_worker`)**: A worker is spawned with this custom engine. The worker is configured with its own `CIRCLE_PUBLIC_KEY` (e.g., "auth_worker_circle").
|
||||
3. **Rhai Client (`rhai_client`)**: The client is used to submit a script (`auth_script.rhai`) to the worker.
|
||||
4. **Authorization Script (`auth_script.rhai`)**: This script calls the `check_permission` function, passing the `CALLER_PUBLIC_KEY` (which is automatically injected into the script's scope by the worker based on the client's submission).
|
||||
5. **Demonstration**: The `main.rs` program submits the script twice, using two different `CALLER_PUBLIC_KEY`s ("admin_pk" and "user_pk"), and shows that the script produces different results based on the authorization logic in `check_permission`.
|
||||
|
||||
This example illustrates how the `rhailib` components can work together to build systems where script execution is controlled and authorized based on the identity of the calling client.
|
||||
|
||||
## Running the Example
|
||||
|
||||
Assuming you have Redis running and accessible at `redis://127.0.0.1/`:
|
||||
|
||||
Run the example from the `rhailib` root directory:
|
||||
```bash
|
||||
cargo run --example end_to_end_auth_demo
|
||||
```
|
||||
|
||||
You should see output indicating the results of the script execution for both the "admin_pk" and "user_pk" callers.
|
@@ -1,16 +0,0 @@
|
||||
let private_object = new_object()
|
||||
.title("Alice's Private Object")
|
||||
.description("This object can only be seen and modified by Alice")
|
||||
.save_object();
|
||||
|
||||
let object_shared_with_bob = new_object()
|
||||
.title("Alice's Shared Collection")
|
||||
.description("This object can be seen by Bob but modified only by Alice")
|
||||
.save_object();
|
||||
|
||||
let new_access = new_access()
|
||||
.object_id(object_shared_with_bob.id())
|
||||
.circle_public_key("bob_pk")
|
||||
.save_access();
|
||||
|
||||
|
@@ -1,88 +0,0 @@
|
||||
use rhai::Engine;
|
||||
use rhai_client::RhaiClient; // To submit tasks
|
||||
use uuid::Uuid; // For generating task_id
|
||||
|
||||
use rhailib_worker::spawn_rhai_worker;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
|
||||
// Custom function for Rhai
|
||||
fn add(a: i64, b: i64) -> i64 {
|
||||
a + b
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
log::info!("Starting Math Worker Example...");
|
||||
|
||||
// 1. Configure and start the Rhai Worker with a custom engine
|
||||
let mut math_engine = Engine::new();
|
||||
math_engine.register_fn("add", add);
|
||||
log::info!("Custom 'add' function registered with Rhai engine for Math Worker.");
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = tokio::sync::mpsc::channel(1);
|
||||
tokio::spawn(async move {
|
||||
log::info!("Math Worker task starting...");
|
||||
let _worker_handle = spawn_rhai_worker(
|
||||
0,
|
||||
"math_circle".to_string(),
|
||||
math_engine,
|
||||
"redis://127.0.0.1/".to_string(),
|
||||
shutdown_rx,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
// Give the worker a moment to start and connect
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
|
||||
// 2. Use RhaiClient to submit a script to the "math_circle"
|
||||
let client = RhaiClient::new("redis://127.0.0.1/")?;
|
||||
let script_content = r#"
|
||||
let x = 10;
|
||||
let y = add(x, 32); // Use the custom registered function
|
||||
print("Math script: 10 + 32 = " + y);
|
||||
y // Return the result
|
||||
"#;
|
||||
|
||||
log::info!("Submitting math script to 'math_circle' and awaiting result...");
|
||||
|
||||
let timeout_duration = Duration::from_secs(10);
|
||||
let task_id = Uuid::new_v4().to_string();
|
||||
|
||||
match client
|
||||
.submit_script_and_await_result(
|
||||
"math_circle",
|
||||
script_content.to_string(),
|
||||
task_id, // Pass the generated task_id
|
||||
timeout_duration,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(details) => {
|
||||
log::info!(
|
||||
"Math Worker Example: Task finished. Status: {}, Output: {:?}, Error: {:?}",
|
||||
details.status,
|
||||
details.output,
|
||||
details.error
|
||||
);
|
||||
if details.status == "completed" {
|
||||
assert_eq!(details.output, Some("42".to_string()));
|
||||
log::info!("Math Worker Example: Assertion for output 42 passed!");
|
||||
Ok(())
|
||||
} else {
|
||||
log::error!(
|
||||
"Math Worker Example: Task completed with error: {:?}",
|
||||
details.error
|
||||
);
|
||||
Err(format!("Task failed with error: {:?}", details.error).into())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Math Worker Example: Failed to get task result: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
use rhai::Engine;
|
||||
use rhai_client::RhaiClient; // To submit tasks
|
||||
use uuid::Uuid; // For generating task_id
|
||||
|
||||
use rhailib_worker::spawn_rhai_worker;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
|
||||
// Custom function for Rhai
|
||||
fn reverse_string(s: String) -> String {
|
||||
s.chars().rev().collect()
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
log::info!("Starting String Worker Example...");
|
||||
|
||||
// 1. Configure and start the Rhai Worker with a custom engine
|
||||
let mut string_engine = Engine::new();
|
||||
string_engine.register_fn("reverse_it", reverse_string);
|
||||
log::info!("Custom 'reverse_it' function registered with Rhai engine for String Worker.");
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = tokio::sync::mpsc::channel(1);
|
||||
tokio::spawn(async move {
|
||||
log::info!("String Worker task starting...");
|
||||
let _worker_handle = spawn_rhai_worker(
|
||||
0,
|
||||
"string_circle".to_string(),
|
||||
string_engine,
|
||||
"redis://127.0.0.1/".to_string(),
|
||||
shutdown_rx,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
// Give the worker a moment to start and connect
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
|
||||
// 2. Use RhaiClient to submit a script to the "string_circle"
|
||||
let client = RhaiClient::new("redis://127.0.0.1/")?;
|
||||
let script_content = r#"
|
||||
let original = "hello world";
|
||||
let reversed = reverse_it(original);
|
||||
print("String script: original = '" + original + "', reversed = '" + reversed + "'");
|
||||
reversed // Return the result
|
||||
"#;
|
||||
|
||||
log::info!("Submitting string script to 'string_circle' and awaiting result...");
|
||||
|
||||
let timeout_duration = Duration::from_secs(10);
|
||||
let task_id = Uuid::new_v4().to_string();
|
||||
|
||||
match client
|
||||
.submit_script_and_await_result(
|
||||
"string_circle",
|
||||
script_content.to_string(),
|
||||
task_id, // Pass the generated task_id
|
||||
timeout_duration,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(details) => {
|
||||
log::info!(
|
||||
"String Worker Example: Task finished. Status: {}, Output: {:?}, Error: {:?}",
|
||||
details.status,
|
||||
details.output,
|
||||
details.error
|
||||
);
|
||||
if details.status == "completed" {
|
||||
assert_eq!(details.output, Some("dlrow olleh".to_string()));
|
||||
log::info!("String Worker Example: Assertion for output \"dlrow olleh\" passed!");
|
||||
Ok(())
|
||||
} else {
|
||||
log::error!(
|
||||
"String Worker Example: Task completed with error: {:?}",
|
||||
details.error
|
||||
);
|
||||
Err(format!("Task failed with error: {:?}", details.error).into())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("String Worker Example: Failed to get task result: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
|
||||
CIRCLE_NAME="default"
|
||||
TASK_QUEUE_KEY="rhai_tasks:${CIRCLE_NAME}"
|
||||
|
||||
# --- 1. Clean Up Previous State ---
|
||||
echo "Cleaning up previous Redis state..."
|
||||
redis-cli DEL "$TASK_QUEUE_KEY" > /dev/null 2>&1 || true
|
||||
echo "Redis state cleaned."
|
||||
|
||||
# --- 2. Compile and Run the Worker ---
|
||||
echo "Compiling and running the 'lua_client_demo' worker in the background..."
|
||||
export RUST_LOG=info
|
||||
cargo run -p rhailib-examples --bin lua_client_demo &
|
||||
WORKER_PID=$!
|
||||
echo "Worker started with PID: $WORKER_PID"
|
||||
|
||||
# --- 3. Wait for the Worker to be Ready using Ping-Pong ---
|
||||
echo "Waiting for worker to be ready (ping-pong check)..."
|
||||
ATTEMPTS=0
|
||||
MAX_ATTEMPTS=15 # Wait for a maximum of 15 seconds
|
||||
while [ $ATTEMPTS -lt $MAX_ATTEMPTS ]; do
|
||||
# Send a 'ping()' script and check for a 'pong' in the output.
|
||||
# The timeout for the ping itself is short (2 seconds).
|
||||
PING_RESULT=$(redis-cli EVAL "$(cat ../scripts/run_rhai.lua)" 0 "$CIRCLE_NAME" "ping()" 2 || true)
|
||||
if echo "$PING_RESULT" | grep -q "pong"; then
|
||||
echo "Worker is ready. Received pong."
|
||||
break
|
||||
fi
|
||||
echo "Ping failed or timed out. Retrying..."
|
||||
sleep 1
|
||||
ATTEMPTS=$((ATTEMPTS + 1))
|
||||
done
|
||||
|
||||
if [ $ATTEMPTS -eq $MAX_ATTEMPTS ]; then
|
||||
echo "Error: Timed out waiting for worker to respond to ping."
|
||||
kill $WORKER_PID
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- 4. Execute the Actual Script ---
|
||||
echo ""
|
||||
echo "Executing main Rhai script..."
|
||||
RESULT=$(redis-cli EVAL "$(cat ../scripts/run_rhai.lua)" 0 "$CIRCLE_NAME" "let x = 100; x * 2" 5)
|
||||
echo "Result from main script: $RESULT"
|
||||
|
||||
# --- 5. Shutdown the Worker ---
|
||||
echo ""
|
||||
echo "Shutting down the worker (PID: $WORKER_PID)..."
|
||||
kill $WORKER_PID
|
||||
wait $WORKER_PID
|
||||
echo "Worker shut down."
|
||||
|
||||
echo ""
|
||||
if echo "$RESULT" | grep -q "error"; then
|
||||
echo "Demo completed with an error."
|
||||
exit 1
|
||||
else
|
||||
echo "Demo completed successfully."
|
||||
fi
|
Reference in New Issue
Block a user