move dsl's to rhailib

This commit is contained in:
Timur Gordon 2025-06-25 03:22:03 +02:00
parent b980f0d8c1
commit 7619e3b944
92 changed files with 4284 additions and 800 deletions

2
Cargo.lock generated
View File

@ -1229,6 +1229,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bincode 2.0.1", "bincode 2.0.1",
"chrono", "chrono",
"derive",
"heromodels-derive", "heromodels-derive",
"heromodels_core", "heromodels_core",
"ourdb", "ourdb",
@ -2356,6 +2357,7 @@ dependencies = [
"rhailib_worker", "rhailib_worker",
"serde", "serde",
"serde_json", "serde_json",
"tempfile",
"tokio", "tokio",
"uuid", "uuid",
] ]

View File

@ -22,16 +22,12 @@ derive = { path = "src/derive" }
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] } criterion = { version = "0.5", features = ["html_reports"] }
uuid = { version = "1.6", features = ["v4", "serde"] } # For examples like dedicated_reply_queue_demo uuid = { version = "1.6", features = ["v4", "serde"] } # For examples like dedicated_reply_queue_demo
tempfile = "3.10"
[[bench]] [[bench]]
name = "simple_rhai_bench" name = "simple_rhai_bench"
harness = false harness = false
[[example]]
name = "end_to_end_auth_demo"
path = "examples/end_to_end/main.rs"
[workspace] [workspace]
members = [ members = [
".", # Represents the root package (rhailib) ".", # Represents the root package (rhailib)

View File

@ -78,46 +78,6 @@ fn create_batch_tasks(task_count: usize) -> Result<Vec<String>, Box<dyn std::err
Ok(task_keys) Ok(task_keys)
} }
fn wait_and_measure(task_key: &str) -> Result<f64, redis::RedisError> {
let client = Client::open(REDIS_URL)?;
let mut conn = client.get_connection()?;
let start_time = std::time::Instant::now();
let timeout = Duration::from_secs(100);
// Poll until task is completed or timeout
loop {
let status: Option<String> = conn.hget(task_key, "status")?;
match status.as_deref() {
Some("completed") | Some("error") => {
println!(
"Task {} completed with status: {}",
task_key,
status.as_deref().unwrap_or("unknown")
);
let created_at: u64 = conn.hget(task_key, "createdAt")?;
let updated_at: u64 = conn.hget(task_key, "updatedAt")?;
return Ok((updated_at - created_at) as f64 * 1000.0); // Convert to milliseconds
}
Some("pending") | Some("processing") => {
thread::sleep(Duration::from_millis(100));
}
_ => {
thread::sleep(Duration::from_millis(100));
}
}
// Check timeout
if start_time.elapsed() > timeout {
return Err(redis::RedisError::from((
redis::ErrorKind::IoError,
"Timeout waiting for task completion",
)));
}
}
}
fn wait_for_batch_completion(task_keys: &[String]) -> Result<f64, Box<dyn std::error::Error>> { fn wait_for_batch_completion(task_keys: &[String]) -> Result<f64, Box<dyn std::error::Error>> {
let client = Client::open(REDIS_URL)?; let client = Client::open(REDIS_URL)?;
let mut conn = client.get_connection()?; let mut conn = client.get_connection()?;

Binary file not shown.

View File

@ -1 +0,0 @@
74

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
276

Binary file not shown.

View File

@ -1,10 +1,11 @@
# Rhailib Examples # 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 ## 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. - **[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.
- **`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.
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.

View 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.

View File

@ -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() let private_object = new_object()
.title("Alice's Private Object") .title("Alice's Private Object")
.description("This object can only be seen and modified by Alice") .description("This object can only be seen and modified by Alice")

View 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();

View File

@ -1,29 +1,31 @@
use rhai::{Engine, EvalAltResult};
use rhai_client::RhaiClientBuilder; use rhai_client::RhaiClientBuilder;
use rhailib_engine::create_heromodels_engine;
use rhailib_worker::spawn_rhai_worker; 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 tokio::sync::mpsc;
use uuid::Uuid;
const ALICE_ID: &str = "alice_pk"; const ALICE_ID: &str = "alice_pk";
const BOB_ID: &str = "bob_pk"; const BOB_ID: &str = "bob_pk";
const CHARLIE_ID: &str = "charlie_pk"; const CHARLIE_ID: &str = "charlie_pk";
const CIRCLE_ID: &str = "circle_pk";
const REDIS_URL: &str = "redis://127.0.0.1/"; const REDIS_URL: &str = "redis://127.0.0.1/";
const DB_DIRECTORY: &str = "./db";
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); 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 // 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 // 2. Spawn the Rhai worker
let (shutdown_tx, shutdown_rx) = mpsc::channel(1); let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
let worker_handle = tokio::spawn(spawn_rhai_worker( let worker_handle = tokio::spawn(spawn_rhai_worker(
ALICE_ID.to_string(), ALICE_ID.to_string(),
DB_DIRECTORY.to_string(), db_path.clone(),
engine, engine,
REDIS_URL.to_string(), REDIS_URL.to_string(),
shutdown_rx, shutdown_rx,
@ -44,7 +46,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
client_alice.new_play_request() client_alice.new_play_request()
.recipient_id(&ALICE_ID) .recipient_id(&ALICE_ID)
.script_path("examples/end_to_end/alice.rhai") .script_path("examples/access_control/alice.rhai")
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.await_response().await.unwrap(); .await_response().await.unwrap();
@ -59,7 +61,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
client_bob.new_play_request() client_bob.new_play_request()
.recipient_id(&ALICE_ID) .recipient_id(&ALICE_ID)
.script_path("examples/end_to_end/bob.rhai") .script_path("examples/access_control/bob.rhai")
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.await_response().await.unwrap(); .await_response().await.unwrap();
@ -74,12 +76,68 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
client_charlie.new_play_request() client_charlie.new_play_request()
.recipient_id(&ALICE_ID) .recipient_id(&ALICE_ID)
.script_path("examples/end_to_end/charlie.rhai") .script_path("examples/access_control/charlie.rhai")
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.await_response().await.unwrap(); .await_response().await.unwrap();
log::info!("Charlie's query to Alice's database completed."); 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) // 5. Shutdown the worker (optional, could also let it run until program exits)
log::info!("Signaling worker to shutdown..."); log::info!("Signaling worker to shutdown...");
let _ = shutdown_tx.send(()).await; let _ = shutdown_tx.send(()).await;

View File

@ -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(())
}

View File

@ -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.

View File

@ -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();

View File

@ -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())
}
}
}

View File

@ -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())
}
}
}

View File

@ -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

View File

@ -1,48 +0,0 @@
--[[
Submits a Rhai script to a distributed worker and returns the task ID.
Since BLPOP cannot block inside Lua scripts (they execute atomically),
this script only submits the task and returns the task ID. The client
must then use BLPOP separately to wait for the result.
ARGV[1] (string): The target circle name (e.g., "default").
ARGV[2] (string): The Rhai script content to execute.
Returns:
- A JSON string containing the task ID and reply queue name.
]]
-- 1. Argument Validation
local circle_name = ARGV[1]
if not circle_name or circle_name == '' then
return cjson.encode({error = "ARGV[1] 'circle_name' is required."})
end
local rhai_script = ARGV[2]
if not rhai_script or rhai_script == '' then
return cjson.encode({error = "ARGV[2] 'rhai_script' is required."})
end
-- 2. Initialization
local task_details_prefix = "rhai_task_details:"
local tasks_queue_prefix = "rhai_tasks:"
local reply_queue_prefix = "rhai_reply:"
local task_id = redis.sha1hex(rhai_script .. redis.call('TIME')[1] .. redis.call('TIME')[2] .. math.random())
local reply_queue_key = reply_queue_prefix .. task_id
local task_details_key = task_details_prefix .. task_id
local task_queue_key = tasks_queue_prefix .. circle_name
-- 3. Task Creation & Queuing
redis.call('HSET', task_details_key,
'script', rhai_script,
'status', 'pending',
'replyToQueue', reply_queue_key
)
redis.call('LPUSH', task_queue_key, task_id)
-- 4. Return task information for client to wait on
return cjson.encode({
task_id = task_id,
reply_queue = reply_queue_key
})

View File

@ -1,54 +0,0 @@
-- Simple Batch Task Creation Script for Rhailib Benchmarking
-- Creates N tasks and returns their Redis keys for timing analysis
-- Script Arguments (ARGV):
-- ARGV[1]: circle_name - The worker circle to send tasks to (e.g., "bench_circle")
-- ARGV[2]: task_count - Number of tasks to create (N)
-- ARGV[3]: rhai_script_content - The Rhai script to execute for all tasks
-- ARGV[4]: batch_id - Batch identifier for grouping
-- Validate arguments
if #ARGV < 4 then
return redis.error_reply("Usage: EVAL script 0 circle_name task_count rhai_script_content batch_id")
end
local circle_name = ARGV[1]
local task_count = tonumber(ARGV[2])
local rhai_script_content = ARGV[3]
local batch_id = ARGV[4]
-- Validate task_count
if not task_count or task_count <= 0 or task_count > 10000 then
return redis.error_reply("task_count must be a positive integer between 1 and 10000")
end
-- Get current timestamp
local current_time = redis.call('TIME')[1]
local rhai_task_queue = 'rhai_tasks:' .. circle_name
local task_keys = {}
-- Create tasks and collect their keys
for i = 1, task_count do
-- Generate unique task ID
local task_id = 'task_' .. redis.call('INCR', 'global_task_counter')
local task_details_key = 'rhai_task_details:' .. task_id
-- Create task details hash with creation timestamp
redis.call('HSET', task_details_key,
'script', rhai_script_content,
'status', 'pending',
'batch_id', batch_id,
'createdAt', current_time,
'updatedAt', current_time,
'task_sequence', tostring(i)
)
-- Queue the task for workers
redis.call('LPUSH', rhai_task_queue, task_id)
-- Add key to return array
table.insert(task_keys, task_details_key)
end
-- Return array of task keys for benchmarking tool to analyze
return task_keys

View File

@ -1,38 +1,43 @@
# Rhai Client # Rhai Client
The `rhai_client` crate provides a client interface for submitting Rhai scripts to a distributed task execution system that uses Redis as a message broker and task store. It allows applications to offload Rhai script execution to one or more worker services. 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 ## Features
- **Script Submission**: Submit Rhai scripts for asynchronous execution. - **Fluent Builder API**: A `RhaiClientBuilder` for easy client configuration and a `PlayRequestBuilder` for constructing and submitting script execution requests.
- **Redis Integration**: Uses Redis lists as task queues and Redis hashes for storing task details (status, input, output, errors). - **Asynchronous Operations**: Built with `tokio` for non-blocking I/O.
- **Asynchronous Operations**: Built with `tokio` for non-blocking operations. - **Request-Reply Pattern**: Submits tasks and awaits results on a dedicated reply queue, eliminating the need for polling.
- **Result Polling**: - **Configurable Timeouts**: Set timeouts for how long the client should wait for a task to complete.
- Submit a script and get a `task_id` back immediately. - **Direct-to-Worker-Queue Submission**: Tasks are sent to a queue named after the `recipient_id`, allowing for direct and clear task routing.
- Poll for task status and results using the `task_id`. - **Manual Status Check**: Provides an option to manually check the status of a task by its ID.
- Optionally, submit a script and await its completion (or error/timeout) with configurable timeout and polling intervals.
- **Public Key-based Task Routing**: Scripts are submitted to a "circle" identified by its unique `secp256k1` public key. This ensures tasks are routed to the correct, isolated worker process.
## Core Components ## Core Components
- **`RhaiClient`**: The main struct for interacting with the Rhai task system. - **`RhaiClientBuilder`**: A builder to construct a `RhaiClient`. Requires a `caller_id` and Redis URL.
- `new(redis_url: &str)`: Creates a new client. - **`RhaiClient`**: The main client for interacting with the task system. It's used to create `PlayRequestBuilder` instances.
- `submit_script(...)`: Submits a script and returns a `task_id`. It requires the target circle's public key for routing. - **`PlayRequestBuilder`**: A fluent builder for creating and dispatching a script execution request. You can set:
- `get_task_status(task_id: &str)`: Retrieves the current status and details of a task. - `recipient_id`: The ID of the worker queue to send the task to.
- `submit_script_and_await_result(...)`: A convenient wrapper that submits a script and polls until it completes, errors out, or the specified timeout is reached. - `script` or `script_path`: The Rhai script to execute.
- **`RhaiTaskDetails`**: A struct representing the details of a task, including its script, status (`pending`, `processing`, `completed`, `error`), output, error messages, and the public key of the caller. - `request_id`: An optional unique ID for the request.
- **`RhaiClientError`**: An enum for various errors that can occur, such as Redis errors, serialization issues, or task timeouts. - `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.
- **`RhaiClientError`**: An enum for various errors, such as Redis errors, serialization issues, or task timeouts.
## How It Works ## How It Works
1. The `RhaiClient` is initialized with the Redis server URL. 1. A `RhaiClient` is created using the `RhaiClientBuilder`, configured with a `caller_id` and Redis URL.
2. When a script is submitted (e.g., via `submit_script_and_await_result`): 2. A `PlayRequestBuilder` is created from the client.
3. The script, `recipient_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. a. A unique `task_id` (UUID v4) is generated.
b. `RhaiTaskDetails` are created, including the script, the caller's public key (if provided), and an initial status of "pending". b. Task details are stored in a Redis hash with a key like `rhailib:<task_id>`.
c. These details are stored in a Redis hash with a key like `rhai_task_details:<task_id>`. c. The `task_id` is pushed to the worker's queue, named `rhailib:<recipient_id>`.
d. The `task_id` is pushed onto a Redis list named `rhai_tasks:<circle_public_key>`, which acts as a queue for the worker assigned to that specific circle. d. The client performs a blocking pop (`BLPOP`) on a dedicated reply queue (`rhailib:reply:<task_id>`), waiting for the worker to send the result.
3. A dedicated `rhai_worker` process, which was spawned with the same `circle_public_key`, pops the `task_id` from its queue, retrieves the task details, executes the script, and updates the results in the Redis hash. 5. A `rhai-worker` process, listening on the `rhailib:<recipient_id>` queue, picks up the task, executes it, and pushes the final `RhaiTaskDetails` to the reply queue.
4. The `RhaiClient` can use `get_task_status` to poll the Redis hash for these updates, or `submit_script_and_await_result` to automate the polling. 6. The client receives the result from the reply queue and returns it to the caller.
## Prerequisites ## Prerequisites
@ -40,66 +45,51 @@ The `rhai_client` crate provides a client interface for submitting Rhai scripts
## Usage Example ## Usage Example
The following example demonstrates submitting a script to a circle identified by its public key and waiting for the result. The following example demonstrates how to build a client, submit a script, and wait for the result.
```rust ```rust
use rhai_client::RhaiClient; use rhai_client::{RhaiClientBuilder, RhaiClientError};
use std::time::Duration; use std::time::Duration;
use serde_json::json;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init(); // Optional: for logging env_logger::init();
// Initialize the client // 1. Build the client
let client = RhaiClient::new("redis://127.0.0.1/")?; let client = RhaiClientBuilder::new()
log::info!("RhaiClient created."); .caller_id("my-app-instance-1")
.redis_url("redis://127.0.0.1/")
.build()?;
let script_content = r#" // 2. Define the script and target worker
// This script can access both CIRCLE_PUBLIC_KEY and CALLER_PUBLIC_KEY let script = r#" "Hello, " + recipient_id + "!" "#;
"Hello from circle: " + CIRCLE_PUBLIC_KEY + ", called by: " + CALLER_PUBLIC_KEY let recipient_id = "worker-1";
"#;
// The target circle is identified by its public key
let circle_public_key = "02f...some_public_key_hex";
// The entity calling the script also has a public key
let caller_public_key = Some("03a...another_public_key_hex");
let timeout = Duration::from_secs(10); // 3. Use the PlayRequestBuilder to configure and submit the request
let result = client
.new_play_request()
.recipient_id(recipient_id)
.script(script)
.timeout(Duration::from_secs(5))
.await_response()
.await;
log::info!("Submitting script to circle '{}' and awaiting result...", circle_public_key); match result {
match client
.submit_script_and_await_result(
circle_public_key,
script_content.to_string(),
None, // Optional client-side RPC ID
timeout,
caller_public_key,
)
.await
{
Ok(details) => { Ok(details) => {
log::info!("Task completed successfully!"); log::info!("Task completed successfully!");
log::info!("Status: {}", details.status); log::info!("Status: {}", details.status);
if let Some(output) = details.output { if let Some(output) = details.output {
log::info!("Output: {}", output); log::info!("Output: {}", output);
} }
if let Some(error_msg) = details.error { }
log::error!("Error: {}", error_msg); Err(RhaiClientError::Timeout(task_id)) => {
} log::error!("Task {} timed out.", task_id);
} }
Err(e) => { Err(e) => {
log::error!("An error occurred: {}", e); log::error!("An unexpected error occurred: {}", e);
match e {
rhai_client::RhaiClientError::Timeout(task_id) => {
log::warn!("Task {} timed out.", task_id);
}
_ => {
log::error!("Unhandled client error: {:?}", e);
}
}
} }
} }
Ok(()) Ok(())
} }
``` ```

View File

@ -1,6 +1,6 @@
use log::info; use log::info;
use rhai_client::RhaiClient; // Assuming RhaiTaskDetails might be part of the success path, though we expect error use rhai_client::{RhaiClientBuilder, RhaiClientError};
use std::time::Duration; use std::time::{Duration, Instant};
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -8,36 +8,41 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.filter_level(log::LevelFilter::Info) .filter_level(log::LevelFilter::Info)
.init(); .init();
let client = RhaiClient::new("redis://127.0.0.1/")?; // Build the client using the new builder pattern
let client = RhaiClientBuilder::new()
.caller_id("timeout-example-runner")
.redis_url("redis://127.0.0.1/")
.build()?;
info!("RhaiClient created."); info!("RhaiClient created.");
let script_content = r#" let script_content = r#"
// This script will never be executed by a worker because the recipient does not exist.
let x = 10; let x = 10;
let y = x + 32; let y = x + 32;
y // This script will never be executed by a worker y
"#; "#;
let non_existent_circle = "non_existent_circle_for_timeout_test_12345"; // The recipient_id points to a worker queue that doesn't have a worker.
let non_existent_recipient = "non_existent_worker_for_timeout_test";
let very_short_timeout = Duration::from_secs(2); let very_short_timeout = Duration::from_secs(2);
let poll_interval = Duration::from_millis(100);
info!( info!(
"Submitting script to non-existent circle '{}' with a timeout of {:?}...", "Submitting script to non-existent recipient '{}' with a timeout of {:?}...",
non_existent_circle, very_short_timeout non_existent_recipient, very_short_timeout
); );
let start_time = std::time::Instant::now(); let start_time = Instant::now();
match client // Use the new PlayRequestBuilder
.submit_script_and_await_result( let result = client
non_existent_circle, .new_play_request()
script_content.to_string(), .recipient_id(non_existent_recipient)
"some_task_id".to_string(), // No specific task_id .script(script_content)
very_short_timeout, .timeout(very_short_timeout)
None, .await_response()
) .await;
.await
{ match result {
Ok(details) => { Ok(details) => {
log::error!( log::error!(
"Timeout Example FAILED: Expected a timeout, but got Ok: {:?}", "Timeout Example FAILED: Expected a timeout, but got Ok: {:?}",
@ -51,7 +56,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
info!("Elapsed time: {:?}", elapsed); info!("Elapsed time: {:?}", elapsed);
match e { match e {
rhai_client::RhaiClientError::Timeout(task_id) => { RhaiClientError::Timeout(task_id) => {
info!("Timeout Example PASSED: Correctly received RhaiClientError::Timeout for task_id: {}", task_id); info!("Timeout Example PASSED: Correctly received RhaiClientError::Timeout for task_id: {}", task_id);
// Ensure the elapsed time is close to the timeout duration // Ensure the elapsed time is close to the timeout duration
// Allow for some buffer for processing // Allow for some buffer for processing

View File

@ -2,7 +2,6 @@ use chrono::Utc;
use log::{debug, error, info, warn}; // Added error use log::{debug, error, info, warn}; // Added error
use redis::AsyncCommands; use redis::AsyncCommands;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::time::timeout;
use std::time::Duration; // Duration is still used, Instant and sleep were removed use std::time::Duration; // Duration is still used, Instant and sleep were removed
use uuid::Uuid; use uuid::Uuid;
@ -149,7 +148,7 @@ impl<'a> PlayRequestBuilder<'a> {
self self
} }
pub fn submit(self) -> Result<(), RhaiClientError> { pub async fn submit(self) -> Result<(), RhaiClientError> {
let request_id = if self.request_id.is_empty() { let request_id = if self.request_id.is_empty() {
// Generate a UUID for the request_id // Generate a UUID for the request_id
Uuid::new_v4().to_string() Uuid::new_v4().to_string()
@ -165,7 +164,7 @@ impl<'a> PlayRequestBuilder<'a> {
script: self.script.clone(), script: self.script.clone(),
timeout: self.timeout, timeout: self.timeout,
} }
); ).await?;
Ok(()) Ok(())
} }
@ -222,7 +221,7 @@ impl RhaiClient {
NAMESPACE_PREFIX NAMESPACE_PREFIX
); );
let mut hset_args: Vec<(String, String)> = vec![ let hset_args: Vec<(String, String)> = vec![
("taskId".to_string(), play_request.id.to_string()), // Add taskId ("taskId".to_string(), play_request.id.to_string()), // Add taskId
("script".to_string(), play_request.script.clone()), // script is moved here ("script".to_string(), play_request.script.clone()), // script is moved here
("callerId".to_string(), self.caller_id.clone()), // script is moved here ("callerId".to_string(), self.caller_id.clone()), // script is moved here

78
src/derive/README.md Normal file
View File

@ -0,0 +1,78 @@
# Rhai Derive Macros
This crate provides procedural macros to simplify the integration of Rust types with the Rhai scripting engine.
## `RhaiApi` Derive Macro
The `RhaiApi` macro automatically generates a Rhai module with a fluent, builder-style API for your Rust structs. This allows you to create and modify your structs in Rhai scripts using chained method calls.
### How It Works
When you derive `RhaiApi` on a struct, the macro generates:
1. A new Rust module named `{struct_name}_rhai_dsl`.
2. A Rhai `export_module` within that module named `generated_rhai_module`.
3. A `new_{struct_name}()` function to create a new instance of your struct.
4. Setter functions for each field in your struct, allowing for method chaining.
5. An `id()` function to retrieve the object's ID.
### Example
**Rust Struct Definition:**
```rust
use derive::RhaiApi;
#[derive(RhaiApi, Clone)]
pub struct Product {
pub id: i64,
pub name: String,
pub price: f64,
}
```
**Generated Rhai API Usage:**
```rhai
// Import the generated module
import product_rhai_dsl::generated_rhai_module as product_api;
// Use the fluent API to build a new product
let my_product = product_api::new_product()
.id(1)
.name("Awesome Gadget")
.price(99.99);
print(my_product.id()); // prints 1
```
## `FromVec` Derive Macro
The `FromVec` macro is a utility for tuple structs that contain a single field. It implements the `From<T>` trait, where `T` is the inner type, allowing for seamless conversions.
### Example
**Rust Struct Definition:**
```rust
use derive::FromVec;
#[derive(FromVec)]
pub struct MyVec(Vec<u8>);
```
**Usage:**
```rust
let data = vec![1, 2, 3];
let my_vec = MyVec::from(data);
```
## Usage
To use these macros in your project, add this crate as a dependency in your `Cargo.toml` file:
```toml
[dependencies]
derive = { path = "../path/to/rhailib/src/derive" }
```

View File

@ -3,6 +3,13 @@ use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields}; use syn::{parse_macro_input, Data, DeriveInput, Fields};
mod rhai_api;
#[proc_macro_derive(RhaiApi)]
pub fn rhai_api_derive(input: TokenStream) -> TokenStream {
rhai_api::impl_rhai_api(input)
}
#[proc_macro_derive(FromVec)] #[proc_macro_derive(FromVec)]
pub fn from_vec_derive(input: TokenStream) -> TokenStream { pub fn from_vec_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);

View File

@ -0,0 +1,85 @@
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, Data, DeriveInput, Fields};
pub fn impl_rhai_api(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let struct_name = &input.ident;
let struct_name_lowercase_str = struct_name.to_string().to_lowercase();
let mod_name = format_ident!("{}_rhai_dsl", struct_name_lowercase_str);
let id_fn_name = format_ident!("{}_id", struct_name_lowercase_str);
// --- Generate `new` function ---
let new_fn_name_str = format!("new_{}", struct_name_lowercase_str);
let new_fn_name_ident = format_ident!("new_{}", struct_name_lowercase_str);
let new_fn = quote! {
#[rhai_fn(name = #new_fn_name_str, return_raw)]
pub fn #new_fn_name_ident() -> Result<RhaiObject, Box<EvalAltResult>> {
let object = RhaiObject::new();
Ok(object)
}
};
// --- Generate setter functions from struct fields ---
let fields = if let Data::Struct(s) = &input.data {
if let Fields::Named(fields) = &s.fields {
&fields.named
} else {
panic!("RhaiApi can only be derived for structs with named fields.");
}
} else {
panic!("RhaiApi can only be derived for structs.");
};
let setter_fns = fields.iter().map(|f| {
let field_name = f.ident.as_ref().unwrap();
let field_type = &f.ty;
if field_name.to_string() == "base_data" {
return quote! {};
}
let rhai_fn_name_str = field_name.to_string();
let rust_fn_name = format_ident!("{}_{}", struct_name_lowercase_str, field_name);
quote! {
#[rhai_fn(name = #rhai_fn_name_str, return_raw, global, pure)]
pub fn #rust_fn_name(
object: &mut RhaiObject,
value: #field_type,
) -> Result<RhaiObject, Box<EvalAltResult>> {
let owned_object = std::mem::take(object);
*object = owned_object.#field_name(value);
Ok(object.clone())
}
}
});
let expanded = quote! {
pub mod #mod_name {
use rhai::plugin::*;
use rhai::{EvalAltResult, INT};
use super::#struct_name;
use std::mem;
type RhaiObject = #struct_name;
#[export_module]
pub mod generated_rhai_module {
use super::*;
#new_fn
#[rhai_fn(name = "id", return_raw, global, pure)]
pub fn #id_fn_name(object: &mut RhaiObject) -> Result<i64, Box<EvalAltResult>> {
Ok(object.id() as i64)
}
#(#setter_fns)*
}
}
};
TokenStream::from(expanded)
}

View File

@ -4,13 +4,12 @@ print("--- Testing Access Rhai Module ---");
// --- Image --- // --- Image ---
print("\n1. Creating and saving an access..."); print("\n1. Creating and saving an access...");
let access = new_access() let new_access = new_access()
.object_id(1) .object_id(1)
.circle_pk("circle_pk") .circle_public_key("some_pk")
.group_id(1) .group_id(1)
.contact_id(1) .contact_id(1)
.expires_at(10); .expires_at(1735689600) // Future timestamp
.save_access();
save_access(access); print("Access saved with ID: " + new_access.id);
print("Access saved with ID: " + access.id);

View File

@ -1,9 +1,11 @@
use heromodels::db::hero::OurDB; use heromodels::db::hero::OurDB;
use rhailib_dsl::access::register_access_rhai_module; use rhailib_dsl::access::register_access_rhai_module;
use rhai::Engine; use rhai::{Engine, Dynamic};
use std::sync::Arc; use std::sync::Arc;
use std::{fs, path::Path}; use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine // Initialize Rhai engine
let mut engine = Engine::new(); let mut engine = Engine::new();
@ -14,10 +16,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if Path::new(db_path).exists() { if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?; fs::remove_dir_all(db_path)?;
} }
let db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database")); let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai // Register the library module with Rhai
register_access_rhai_module(&mut engine, db.clone()); register_access_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions, db.clone());
// Load and evaluate the Rhai script // Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR"); let manifest_dir = env!("CARGO_MANIFEST_DIR");

View File

@ -0,0 +1,8 @@
// biz.rhai
// Example of using the company module
let new_company = new_company()
.name("HeroCode Inc.")
.save_company();
print(new_company.name);

View File

@ -0,0 +1,48 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::biz::register_biz_rhai_module;
use rhai::Engine;
use std::sync::Arc;
use std::{fs, path::Path};
use rhai::Dynamic;
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_biz_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_biz_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("biz").join("biz.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Biz Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,16 @@
// calendar.rhai
let new_event = new_event()
.title("Team Meeting")
.description("Weekly sync-up")
.location("Virtual")
.add_attendee(new_attendee(1).status("Accepted"))
.reschedule(1672531200, 1672534800) // Example timestamps
.save_event();
let new_calendar = new_calendar("Work Calendar")
.description("Calendar for all work-related events")
.save_calendar();
print(new_calendar);
print(new_event);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::calendar::register_calendar_rhai_module;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_calendar_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_calendar_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("calendar").join("calendar.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Calendar Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,13 @@
// circle.rhai
let new_circle = new_circle()
.title("HeroCode Community")
.ws_url("ws://herocode.com/community")
.description("A circle for HeroCode developers.")
.logo("logo.png")
.theme_property("primaryColor", "#FF0000")
.add_circle("General")
.add_member("user123")
.save_circle();
print(new_circle);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::circle::register_circle_rhai_module;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_circle_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_circle_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("circle").join("circle.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Circle Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,18 @@
// company.rhai
let new_company = new_company()
.name("HeroCode Solutions")
.registration_number("HC12345")
.incorporation_date(1609459200)
.fiscal_year_end("31-12")
.email("contact@herocode.com")
.phone("123-456-7890")
.website("www.herocode.com")
.address("123 Hero Way, Codeville")
.business_type("Global")
.industry("Software Development")
.description("Providing heroic coding solutions.")
.status("Active")
.save_company();
print(new_company);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::company::register_company_rhai_module;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_company_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_company_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("company").join("company.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Company Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,21 @@
// contact.rhai
let new_contact = new_contact()
.name("John Doe")
.description("A test contact")
.address("123 Main St")
.phone("555-1234")
.email("john.doe@example.com")
.notes("This is a note.")
.circle("friends")
.save_contact();
print(new_contact);
let new_group = new_group()
.name("Test Group")
.description("A group for testing")
.add_contact(1)
.save_group();
print(new_group);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::contact::register_contact_rhai_module;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_contact_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_contact_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("contact").join("contact.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Contact Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,9 @@
// core.rhai
let new_comment = new_comment()
.user_id(1)
.content("This is a test comment.")
.parent_comment_id(0)
.save_comment();
print(new_comment);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::core::register_core_rhai_module;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_core_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_core_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("core").join("core.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Core Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,58 @@
// finance.rhai
// Account
let new_account = new_account()
.name("My Test Account")
.user_id(1)
.description("A test account for finance.")
.ledger("main")
.address("0x123...")
.pubkey("0x456...")
.add_asset(1)
.save_account();
print("New Account:");
print(new_account);
// Asset
let new_asset = new_asset()
.name("HeroCoin")
.description("The official coin of HeroCode.")
.amount(1000.0)
.address("0xabc...")
.decimals(18)
.asset_type("erc20")
.save_asset();
print("\nNew Asset:");
print(new_asset);
// Listing
let new_listing = new_listing()
.title("100 HeroCoins for sale")
.description("Get your HeroCoins now!")
.asset_id("1")
.seller_id("1")
.price(1.5)
.currency("USD")
.asset_type("erc20")
.listing_type("FixedPrice")
.status("Active")
.expires_at(1735689600) // Some future timestamp
.image_url("http://example.com/herocoin.png")
.add_tag("crypto")
.save_listing();
print("\nNew Listing:");
print(new_listing);
// Bid
let new_bid = new_bid()
.listing_id("1")
.bidder_id(2)
.amount(1.6)
.currency("USD")
.status("Active");
print("\nNew Bid:");
print(new_bid);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::finance::register_finance_rhai_modules;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_finance_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_finance_rhai_modules(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("finance").join("finance.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Finance Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,32 @@
// flow.rhai
// Create a signature requirement
let sig_req = new_signature_requirement()
.flow_step_id(1)
.public_key("0xABCDEF1234567890")
.message("Please sign to approve this step.")
.status("Pending")
.save_signature_requirement();
print("New Signature Requirement:");
print(sig_req);
// Create a flow step
let step1 = new_flow_step()
.description("Initial approval by manager")
.step_order(1)
.status("Pending")
.save_flow_step();
print("\nNew Flow Step:");
print(step1);
// Create a flow and add the step
let my_flow = new_flow("purchase-request-flow-123")
.name("Purchase Request Approval Flow")
.status("Active")
.add_step(step1)
.save_flow();
print("\nNew Flow:");
print(my_flow);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::flow::register_flow_rhai_modules;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_flow_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_flow_rhai_modules(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("flow").join("flow.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Flow Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -1,6 +1,5 @@
use rhai::{Engine, Module, Position, Scope, Dynamic}; use rhai::{Engine, Module, Position, Scope, Dynamic};
use std::sync::Arc; use std::sync::Arc;
use tempfile::tempdir;
// Import DB traits with an alias for the Collection trait to avoid naming conflicts. // Import DB traits with an alias for the Collection trait to avoid naming conflicts.
// Import DB traits from heromodels::db as suggested by compiler errors. // Import DB traits from heromodels::db as suggested by compiler errors.
@ -8,9 +7,10 @@ use heromodels::db::{Db, Collection as DbCollection};
use heromodels::{ use heromodels::{
db::hero::OurDB, db::hero::OurDB,
models::library::collection::Collection, // Actual data model for single items models::library::collection::Collection, // Actual data model for single items
models::library::rhai::RhaiCollectionArray, // Wrapper for arrays of collections
models::access::access::Access, models::access::access::Access,
}; };
use rhailib_dsl::library::RhaiCollectionArray;
// Import macros and the functions they depend on, which must be in scope during invocation. // Import macros and the functions they depend on, which must be in scope during invocation.
use rhailib_dsl::{register_authorized_get_by_id_fn, register_authorized_list_fn}; use rhailib_dsl::{register_authorized_get_by_id_fn, register_authorized_list_fn};
@ -41,12 +41,10 @@ fn register_example_module(engine: &mut Engine, db: Arc<OurDB>) {
register_authorized_list_fn!( register_authorized_list_fn!(
module: &mut module, module: &mut module,
db_clone: db.clone(),
rhai_fn_name: "list_all_collections", rhai_fn_name: "list_all_collections",
resource_type_str: "Collection", resource_type_str: "Collection",
rhai_return_rust_type: heromodels::models::library::collection::Collection, // Use Collection struct rhai_return_rust_type: heromodels::models::library::collection::Collection,
item_id_accessor: id, // Assumes Collection has an id() method that returns u32 rhai_return_wrapper_type: RhaiCollectionArray
rhai_return_wrapper_type: heromodels::models::library::rhai::RhaiCollectionArray // Wrapper type for Rhai
); );
engine.register_global_module(module.into()); engine.register_global_module(module.into());

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::object::register_object_fns;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_object_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_object_fns(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("object").join("object.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Object Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,12 @@
// object.rhai
// Assuming a builder function `object__builder` exists based on the project's pattern.
let new_object = object__builder(1)
.name("My Dynamic Object")
.description("An example of a generic object.")
.set_property("custom_field", "custom_value")
.build()
.save_object();
print("New Object:");
print(new_object);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::product::register_product_rhai_module;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_product_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_product_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("product").join("product.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Product Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,27 @@
// product.rhai
// Create a product component
let component = new_product_component()
.name("Sub-component A")
.description("A vital part of the main product.")
.quantity(10);
print("New Product Component:");
print(component);
// Create a product and add the component
let new_product = new_product()
.name("Super Product")
.description("A product that does amazing things.")
.price(99.99)
.type("Product")
.category("Gadgets")
.status("Available")
.max_amount(100)
.purchase_till(1735689600) // Future timestamp
.active_till(1735689600) // Future timestamp
.add_component(component)
.save_product();
print("\nNew Product:");
print(new_product);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::sale::register_sale_rhai_module;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_sale_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_sale_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("sale").join("sale.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Sale Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,28 @@
// sale.rhai
// Create a sale item
let sale_item = new_sale_item()
.product_id(1)
.name("Super Product")
.quantity(2)
.unit_price(99.99)
.subtotal(199.98)
.service_active_until(1735689600); // Future timestamp
print("New Sale Item:");
print(sale_item);
// Create a sale and add the item
let new_sale = new_sale()
.company_id(1)
.buyer_id(2)
.transaction_id(12345)
.total_amount(199.98)
.status("Completed")
.sale_date(1672531200) // Past timestamp
.add_item(sale_item)
.notes("This is a test sale.")
.save_sale();
print("\nNew Sale:");
print(new_sale);

View File

@ -0,0 +1,47 @@
use heromodels::db::hero::OurDB;
use rhailib_dsl::shareholder::register_shareholder_rhai_module;
use rhai::{Engine, Dynamic};
use std::sync::Arc;
use std::{fs, path::Path};
const CALLER_ID: &str = "example_caller";
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Rhai engine
let mut engine = Engine::new();
// Initialize database with OurDB
let db_path = "temp_shareholder_db";
// Clean up previous database file if it exists
if Path::new(db_path).exists() {
fs::remove_dir_all(db_path)?;
}
let _db = Arc::new(OurDB::new(db_path, true).expect("Failed to create database"));
// Register the library module with Rhai
register_shareholder_rhai_module(&mut engine);
let mut db_config = rhai::Map::new();
db_config.insert("DB_PATH".into(), db_path.into());
db_config.insert("CALLER_PUBLIC_KEY".into(), CALLER_ID.into());
db_config.insert("CIRCLE_PUBLIC_KEY".into(), CALLER_ID.into());
engine.set_default_tag(Dynamic::from(db_config)); // Or pass via CallFnOptions
// Load and evaluate the Rhai script
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let script_path = Path::new(manifest_dir).join("examples").join("shareholder").join("shareholder.rhai");
println!("Script path: {}", script_path.display());
let script = fs::read_to_string(&script_path)?;
println!("--- Running Shareholder Rhai Script ---");
match engine.eval::<()>(&script) {
Ok(_) => println!("\n--- Script executed successfully! ---"),
Err(e) => eprintln!("\n--- Script execution failed: {} ---", e),
}
// Clean up the database file
fs::remove_dir_all(db_path)?;
println!("--- Cleaned up temporary database. ---");
Ok(())
}

View File

@ -0,0 +1,15 @@
// shareholder.rhai
// Create a shareholder
let new_shareholder = new_shareholder()
.company_id(1)
.user_id(1)
.name("John Doe")
.shares(1000.0)
.percentage(10.0)
.type("Individual")
.since(1640995200) // Timestamp for Jan 1, 2022
.save_shareholder();
print("New Shareholder:");
print(new_shareholder);

View File

@ -1,7 +1,7 @@
use heromodels::db::Db; use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn}; use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*; use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position}; use rhai::{Dynamic, Engine, EvalAltResult, Module};
use std::mem; use std::mem;
use std::sync::Arc; use std::sync::Arc;

173
src/dsl/src/biz/company.rs Normal file
View File

@ -0,0 +1,173 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Module, Position};
use std::mem;
use std::sync::Arc;
use heromodels::models::biz::company::{Company, CompanyStatus, BusinessType};
type RhaiCompany = Company;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
#[export_module]
mod rhai_company_module {
use super::{RhaiCompany, CompanyStatus, BusinessType};
#[rhai_fn(name = "new_company", return_raw)]
pub fn new_company() -> Result<RhaiCompany, Box<EvalAltResult>> {
Ok(Company::new())
}
// --- Setters ---
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(company: &mut RhaiCompany, name: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.name(name);
Ok(company.clone())
}
#[rhai_fn(name = "registration_number", return_raw)]
pub fn set_registration_number(company: &mut RhaiCompany, reg_num: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.registration_number(reg_num);
Ok(company.clone())
}
#[rhai_fn(name = "incorporation_date", return_raw)]
pub fn set_incorporation_date(company: &mut RhaiCompany, date: i64) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.incorporation_date(date);
Ok(company.clone())
}
#[rhai_fn(name = "fiscal_year_end", return_raw)]
pub fn set_fiscal_year_end(company: &mut RhaiCompany, fye: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.fiscal_year_end(fye);
Ok(company.clone())
}
#[rhai_fn(name = "email", return_raw)]
pub fn set_email(company: &mut RhaiCompany, email: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.email(email);
Ok(company.clone())
}
#[rhai_fn(name = "phone", return_raw)]
pub fn set_phone(company: &mut RhaiCompany, phone: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.phone(phone);
Ok(company.clone())
}
#[rhai_fn(name = "website", return_raw)]
pub fn set_website(company: &mut RhaiCompany, website: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.website(website);
Ok(company.clone())
}
#[rhai_fn(name = "address", return_raw)]
pub fn set_address(company: &mut RhaiCompany, address: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.address(address);
Ok(company.clone())
}
#[rhai_fn(name = "industry", return_raw)]
pub fn set_industry(company: &mut RhaiCompany, industry: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.industry(industry);
Ok(company.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(company: &mut RhaiCompany, description: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned = std::mem::take(company);
*company = owned.description(description);
Ok(company.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(company: &mut RhaiCompany, status_str: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let status = match status_str.to_lowercase().as_str() {
"active" => CompanyStatus::Active,
"inactive" => CompanyStatus::Inactive,
"suspended" => CompanyStatus::Suspended,
_ => {
return Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid company status: {}", status_str).into(),
Position::NONE,
)));
}
};
let owned = std::mem::take(company);
*company = owned.status(status);
Ok(company.clone())
}
#[rhai_fn(name = "business_type", return_raw)]
pub fn set_business_type(company: &mut RhaiCompany, type_str: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let biz_type = match type_str.to_lowercase().as_str() {
"coop" => BusinessType::Coop,
"single" => BusinessType::Single,
"twin" => BusinessType::Twin,
"starter" => BusinessType::Starter,
"global" => BusinessType::Global,
_ => {
return Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid business type: {}", type_str).into(),
Position::NONE,
)));
}
};
let owned = std::mem::take(company);
*company = owned.business_type(biz_type);
Ok(company.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(c: &mut RhaiCompany) -> i64 { c.base_data.id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_name(c: &mut RhaiCompany) -> String { c.name.clone() }
#[rhai_fn(get = "registration_number", pure)] pub fn get_registration_number(c: &mut RhaiCompany) -> String { c.registration_number.clone() }
#[rhai_fn(get = "incorporation_date", pure)] pub fn get_incorporation_date(c: &mut RhaiCompany) -> i64 { c.incorporation_date }
#[rhai_fn(get = "fiscal_year_end", pure)] pub fn get_fiscal_year_end(c: &mut RhaiCompany) -> String { c.fiscal_year_end.clone() }
#[rhai_fn(get = "email", pure)] pub fn get_email(c: &mut RhaiCompany) -> String { c.email.clone() }
#[rhai_fn(get = "phone", pure)] pub fn get_phone(c: &mut RhaiCompany) -> String { c.phone.clone() }
#[rhai_fn(get = "website", pure)] pub fn get_website(c: &mut RhaiCompany) -> String { c.website.clone() }
#[rhai_fn(get = "address", pure)] pub fn get_address(c: &mut RhaiCompany) -> String { c.address.clone() }
#[rhai_fn(get = "industry", pure)] pub fn get_industry(c: &mut RhaiCompany) -> String { c.industry.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_description(c: &mut RhaiCompany) -> String { c.description.clone() }
#[rhai_fn(get = "status", pure)] pub fn get_status(c: &mut RhaiCompany) -> String { format!("{:?}", c.status) }
#[rhai_fn(get = "business_type", pure)] pub fn get_business_type(c: &mut RhaiCompany) -> String { format!("{:?}", c.business_type) }
}
pub fn register_company_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiCompany>();
let mut module = exported_module!(rhai_company_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
engine.register_global_module(module.into());
println!("Successfully registered company Rhai module.");
}

14
src/dsl/src/biz/mod.rs Normal file
View File

@ -0,0 +1,14 @@
use rhai::Engine;
pub mod company;
pub mod product;
pub mod sale;
pub mod shareholder;
pub fn register_biz_rhai_module(engine: &mut Engine) {
company::register_company_rhai_module(engine);
product::register_product_rhai_module(engine);
sale::register_sale_rhai_module(engine);
shareholder::register_shareholder_rhai_module(engine);
println!("Successfully registered biz Rhai module.");
}

306
src/dsl/src/biz/product.rs Normal file
View File

@ -0,0 +1,306 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Engine, EvalAltResult, Module, Position, FLOAT, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::biz::product::{Product, ProductComponent, ProductStatus, ProductType};
type RhaiProduct = Product;
type RhaiProductComponent = ProductComponent;
#[export_module]
mod rhai_product_component_module {
use super::{RhaiProductComponent, INT};
#[rhai_fn(name = "new_product_component", return_raw)]
pub fn new_product_component() -> Result<RhaiProductComponent, Box<EvalAltResult>> {
Ok(ProductComponent::new())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(
component: &mut RhaiProductComponent,
name: String,
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned = std::mem::take(component);
*component = owned.name(name);
Ok(component.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(
component: &mut RhaiProductComponent,
description: String,
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned = std::mem::take(component);
*component = owned.description(description);
Ok(component.clone())
}
#[rhai_fn(name = "quantity", return_raw)]
pub fn set_quantity(
component: &mut RhaiProductComponent,
quantity: INT,
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned = std::mem::take(component);
*component = owned.quantity(quantity as u32);
Ok(component.clone())
}
// --- Getters ---
#[rhai_fn(get = "name", pure)]
pub fn get_name(c: &mut RhaiProductComponent) -> String {
c.name.clone()
}
#[rhai_fn(get = "description", pure)]
pub fn get_description(c: &mut RhaiProductComponent) -> String {
c.description.clone()
}
#[rhai_fn(get = "quantity", pure)]
pub fn get_quantity(c: &mut RhaiProductComponent) -> INT {
c.quantity as INT
}
}
#[export_module]
mod rhai_product_module {
use super::{Array, ProductStatus, ProductType, RhaiProduct, RhaiProductComponent, FLOAT, INT};
#[rhai_fn(name = "new_product", return_raw)]
pub fn new_product() -> Result<RhaiProduct, Box<EvalAltResult>> {
Ok(Product::new())
}
// --- Setters ---
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(product: &mut RhaiProduct, name: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.name(name);
Ok(product.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(
product: &mut RhaiProduct,
description: String,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.description(description);
Ok(product.clone())
}
#[rhai_fn(name = "price", return_raw)]
pub fn set_price(product: &mut RhaiProduct, price: FLOAT) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.price(price);
Ok(product.clone())
}
#[rhai_fn(name = "category", return_raw)]
pub fn set_category(
product: &mut RhaiProduct,
category: String,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.category(category);
Ok(product.clone())
}
#[rhai_fn(name = "max_amount", return_raw)]
pub fn set_max_amount(
product: &mut RhaiProduct,
max_amount: INT,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.max_amount(max_amount as u16);
Ok(product.clone())
}
#[rhai_fn(name = "purchase_till", return_raw)]
pub fn set_purchase_till(
product: &mut RhaiProduct,
purchase_till: INT,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.purchase_till(purchase_till);
Ok(product.clone())
}
#[rhai_fn(name = "active_till", return_raw)]
pub fn set_active_till(
product: &mut RhaiProduct,
active_till: INT,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.active_till(active_till);
Ok(product.clone())
}
#[rhai_fn(name = "type", return_raw)]
pub fn set_type(product: &mut RhaiProduct, type_str: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
let product_type = match type_str.to_lowercase().as_str() {
"product" => ProductType::Product,
"service" => ProductType::Service,
_ => {
return Err(EvalAltResult::ErrorSystem(
"Invalid product type".into(),
Box::new(
EvalAltResult::ErrorRuntime(
format!("Unknown type: {}", type_str).into(),
Position::NONE)),
)
.into())
}
};
let owned = std::mem::take(product);
*product = owned.type_(product_type);
Ok(product.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(
product: &mut RhaiProduct,
status_str: String,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let status = match status_str.to_lowercase().as_str() {
"available" => ProductStatus::Available,
"unavailable" => ProductStatus::Unavailable,
_ => {
return Err(EvalAltResult::ErrorSystem(
"Invalid product status".into(),
Box::new(EvalAltResult::ErrorRuntime(
format!("Unknown status: {}", status_str).into(),
Position::NONE,
)),
)
.into());
}
};
let owned = std::mem::take(product);
*product = owned.status(status);
Ok(product.clone())
}
#[rhai_fn(name = "add_component", return_raw)]
pub fn add_component(
product: &mut RhaiProduct,
component: RhaiProductComponent,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.add_component(component);
Ok(product.clone())
}
#[rhai_fn(name = "components", return_raw)]
pub fn set_components(
product: &mut RhaiProduct,
components: Array,
) -> Result<RhaiProduct, Box<EvalAltResult>> {
let mut rust_components = Vec::with_capacity(components.len());
for component in components {
if let Some(c) = component.try_cast::<RhaiProductComponent>() {
rust_components.push(c);
} else {
return Err(Box::new(EvalAltResult::ErrorSystem(
"Invalid Type".into(),
"All elements in components array must be of type ProductComponent".into(),
)));
}
}
let owned = std::mem::take(product);
*product = owned.components(rust_components);
Ok(product.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)]
pub fn get_id(p: &mut RhaiProduct) -> i64 {
p.base_data.id as i64
}
#[rhai_fn(get = "name", pure)]
pub fn get_name(p: &mut RhaiProduct) -> String {
p.name.clone()
}
#[rhai_fn(get = "description", pure)]
pub fn get_description(p: &mut RhaiProduct) -> String {
p.description.clone()
}
#[rhai_fn(get = "price", pure)]
pub fn get_price(p: &mut RhaiProduct) -> FLOAT {
p.price
}
#[rhai_fn(get = "category", pure)]
pub fn get_category(p: &mut RhaiProduct) -> String {
p.category.clone()
}
#[rhai_fn(get = "max_amount", pure)]
pub fn get_max_amount(p: &mut RhaiProduct) -> INT {
p.max_amount as INT
}
#[rhai_fn(get = "purchase_till", pure)]
pub fn get_purchase_till(p: &mut RhaiProduct) -> INT {
p.purchase_till
}
#[rhai_fn(get = "active_till", pure)]
pub fn get_active_till(p: &mut RhaiProduct) -> INT {
p.active_till
}
#[rhai_fn(get = "type", pure)]
pub fn get_type(p: &mut RhaiProduct) -> String {
format!("{:?}", p.type_)
}
#[rhai_fn(get = "status", pure)]
pub fn get_status(p: &mut RhaiProduct) -> String {
format!("{:?}", p.status)
}
#[rhai_fn(get = "components", pure)]
pub fn get_components(p: &mut RhaiProduct) -> Array {
p.components
.clone()
.into_iter()
.map(Dynamic::from)
.collect()
}
}
pub fn register_product_rhai_module(engine: &mut Engine) {
engine
.build_type::<RhaiProductComponent>()
.register_get("name", rhai_product_component_module::get_name)
.register_get(
"description",
rhai_product_component_module::get_description,
)
.register_get("quantity", rhai_product_module::get_max_amount);
let component_module = exported_module!(rhai_product_component_module);
engine.register_global_module(component_module.into());
let mut product_module = exported_module!(rhai_product_module);
register_authorized_create_by_id_fn!(
module: &mut product_module,
rhai_fn_name: "save_product",
resource_type_str: "Product",
rhai_return_rust_type: heromodels::models::biz::product::Product
);
register_authorized_get_by_id_fn!(
module: &mut product_module,
rhai_fn_name: "get_product",
resource_type_str: "Product",
rhai_return_rust_type: heromodels::models::biz::product::Product
);
register_authorized_delete_by_id_fn!(
module: &mut product_module,
rhai_fn_name: "delete_product",
resource_type_str: "Product",
rhai_return_rust_type: heromodels::models::biz::product::Product
);
engine.register_global_module(product_module.into());
println!("Successfully registered product Rhai module.");
}

220
src/dsl/src/biz/sale.rs Normal file
View File

@ -0,0 +1,220 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Engine, EvalAltResult, Module, Position, FLOAT, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::biz::sale::{Sale, SaleItem, SaleStatus};
type RhaiSale = Sale;
type RhaiSaleItem = SaleItem;
#[export_module]
mod rhai_sale_item_module {
use super::{RhaiSaleItem, FLOAT, INT};
#[rhai_fn(name = "new_sale_item", return_raw)]
pub fn new_sale_item() -> Result<RhaiSaleItem, Box<EvalAltResult>> {
Ok(SaleItem::new())
}
#[rhai_fn(name = "product_id", return_raw)]
pub fn set_product_id(item: &mut RhaiSaleItem, product_id: INT) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.product_id(product_id as u32);
Ok(item.clone())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(item: &mut RhaiSaleItem, name: String) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.name(name);
Ok(item.clone())
}
#[rhai_fn(name = "quantity", return_raw)]
pub fn set_quantity(item: &mut RhaiSaleItem, quantity: INT) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.quantity(quantity as i32);
Ok(item.clone())
}
#[rhai_fn(name = "unit_price", return_raw)]
pub fn set_unit_price(item: &mut RhaiSaleItem, unit_price: FLOAT) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.unit_price(unit_price);
Ok(item.clone())
}
#[rhai_fn(name = "subtotal", return_raw)]
pub fn set_subtotal(item: &mut RhaiSaleItem, subtotal: FLOAT) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.subtotal(subtotal);
Ok(item.clone())
}
#[rhai_fn(name = "service_active_until", return_raw)]
pub fn set_service_active_until(item: &mut RhaiSaleItem, service_active_until: INT) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
let until_option = if service_active_until > 0 { Some(service_active_until) } else { None };
*item = owned.service_active_until(until_option);
Ok(item.clone())
}
// --- Getters ---
#[rhai_fn(get = "product_id", pure)] pub fn get_product_id(item: &mut RhaiSaleItem) -> INT { item.product_id as INT }
#[rhai_fn(get = "name", pure)] pub fn get_name(item: &mut RhaiSaleItem) -> String { item.name.clone() }
#[rhai_fn(get = "quantity", pure)] pub fn get_quantity(item: &mut RhaiSaleItem) -> INT { item.quantity as INT }
#[rhai_fn(get = "unit_price", pure)] pub fn get_unit_price(item: &mut RhaiSaleItem) -> FLOAT { item.unit_price }
#[rhai_fn(get = "subtotal", pure)] pub fn get_subtotal(item: &mut RhaiSaleItem) -> FLOAT { item.subtotal }
#[rhai_fn(get = "service_active_until", pure)] pub fn get_service_active_until(item: &mut RhaiSaleItem) -> Option<INT> { item.service_active_until }
}
#[export_module]
mod rhai_sale_module {
use super::{Array, RhaiSale, RhaiSaleItem, SaleStatus, FLOAT, INT};
#[rhai_fn(name = "new_sale", return_raw)]
pub fn new_sale() -> Result<RhaiSale, Box<EvalAltResult>> {
Ok(Sale::new())
}
// --- Setters ---
#[rhai_fn(name = "company_id", return_raw)]
pub fn set_company_id(sale: &mut RhaiSale, company_id: INT) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.company_id(company_id as u32);
Ok(sale.clone())
}
#[rhai_fn(name = "buyer_id", return_raw)]
pub fn set_buyer_id(sale: &mut RhaiSale, buyer_id: INT) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.buyer_id(buyer_id as u32);
Ok(sale.clone())
}
#[rhai_fn(name = "transaction_id", return_raw)]
pub fn set_transaction_id(sale: &mut RhaiSale, transaction_id: INT) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.transaction_id(transaction_id as u32);
Ok(sale.clone())
}
#[rhai_fn(name = "total_amount", return_raw)]
pub fn set_total_amount(sale: &mut RhaiSale, total_amount: FLOAT) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.total_amount(total_amount);
Ok(sale.clone())
}
#[rhai_fn(name = "sale_date", return_raw)]
pub fn set_sale_date(sale: &mut RhaiSale, sale_date: INT) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.sale_date(sale_date);
Ok(sale.clone())
}
#[rhai_fn(name = "notes", return_raw)]
pub fn set_notes(sale: &mut RhaiSale, notes: String) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.notes(notes);
Ok(sale.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(sale: &mut RhaiSale, status_str: String) -> Result<RhaiSale, Box<EvalAltResult>> {
let status = match status_str.to_lowercase().as_str() {
"pending" => SaleStatus::Pending,
"completed" => SaleStatus::Completed,
"cancelled" => SaleStatus::Cancelled,
_ => {
return Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid sale status: {}", status_str).into(),
Position::NONE,
)));
}
};
let owned = std::mem::take(sale);
*sale = owned.status(status);
Ok(sale.clone())
}
#[rhai_fn(name = "add_item", return_raw)]
pub fn add_item(sale: &mut RhaiSale, item: RhaiSaleItem) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.add_item(item);
Ok(sale.clone())
}
#[rhai_fn(name = "items", return_raw)]
pub fn set_items(sale: &mut RhaiSale, items: Array) -> Result<RhaiSale, Box<EvalAltResult>> {
let mut rust_items = Vec::with_capacity(items.len());
for item in items {
if let Some(i) = item.try_cast::<RhaiSaleItem>() {
rust_items.push(i);
} else {
return Err(Box::new(EvalAltResult::ErrorRuntime(
"All elements in items array must be of type SaleItem".into(),
Position::NONE,
)));
}
}
let owned = std::mem::take(sale);
*sale = owned.items(rust_items);
Ok(sale.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(s: &mut RhaiSale) -> i64 { s.base_data.id as i64 }
#[rhai_fn(get = "company_id", pure)] pub fn get_company_id(s: &mut RhaiSale) -> INT { s.company_id as INT }
#[rhai_fn(get = "buyer_id", pure)] pub fn get_buyer_id(s: &mut RhaiSale) -> INT { s.buyer_id as INT }
#[rhai_fn(get = "transaction_id", pure)] pub fn get_transaction_id(s: &mut RhaiSale) -> INT { s.transaction_id as INT }
#[rhai_fn(get = "total_amount", pure)] pub fn get_total_amount(s: &mut RhaiSale) -> FLOAT { s.total_amount }
#[rhai_fn(get = "sale_date", pure)] pub fn get_sale_date(s: &mut RhaiSale) -> INT { s.sale_date }
#[rhai_fn(get = "notes", pure)] pub fn get_notes(s: &mut RhaiSale) -> String { s.notes.clone() }
#[rhai_fn(get = "status", pure)] pub fn get_status(s: &mut RhaiSale) -> String { format!("{:?}", s.status) }
#[rhai_fn(get = "items", pure)] pub fn get_items(s: &mut RhaiSale) -> Array { s.items.clone().into_iter().map(Dynamic::from).collect() }
}
pub fn register_sale_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiSale>();
engine
.build_type::<RhaiSaleItem>()
.register_get("product_id", rhai_sale_item_module::get_product_id)
.register_get("name", rhai_sale_item_module::get_name)
.register_get("quantity", rhai_sale_item_module::get_quantity)
.register_get("unit_price", rhai_sale_item_module::get_unit_price)
.register_get("subtotal", rhai_sale_item_module::get_subtotal)
.register_get("service_active_until", rhai_sale_item_module::get_service_active_until);
let item_module = exported_module!(rhai_sale_item_module);
engine.register_global_module(item_module.into());
let mut sale_module = exported_module!(rhai_sale_module);
register_authorized_create_by_id_fn!(
module: &mut sale_module,
rhai_fn_name: "save_sale",
resource_type_str: "Sale",
rhai_return_rust_type: heromodels::models::biz::sale::Sale
);
register_authorized_get_by_id_fn!(
module: &mut sale_module,
rhai_fn_name: "get_sale",
resource_type_str: "Sale",
rhai_return_rust_type: heromodels::models::biz::sale::Sale
);
register_authorized_delete_by_id_fn!(
module: &mut sale_module,
rhai_fn_name: "delete_sale",
resource_type_str: "Sale",
rhai_return_rust_type: heromodels::models::biz::sale::Sale
);
engine.register_global_module(sale_module.into());
println!("Successfully registered sale Rhai module.");
}

View File

@ -0,0 +1,122 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Module, Position, FLOAT, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::biz::shareholder::{Shareholder, ShareholderType};
type RhaiShareholder = Shareholder;
#[export_module]
mod rhai_shareholder_module {
use super::{RhaiShareholder, ShareholderType, FLOAT, INT};
#[rhai_fn(name = "new_shareholder", return_raw)]
pub fn new_shareholder() -> Result<RhaiShareholder, Box<EvalAltResult>> {
Ok(Shareholder::new())
}
// --- Setters ---
#[rhai_fn(name = "company_id", return_raw)]
pub fn set_company_id(shareholder: &mut RhaiShareholder, company_id: INT) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.company_id(company_id as u32);
Ok(shareholder.clone())
}
#[rhai_fn(name = "user_id", return_raw)]
pub fn set_user_id(shareholder: &mut RhaiShareholder, user_id: INT) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.user_id(user_id as u32);
Ok(shareholder.clone())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(shareholder: &mut RhaiShareholder, name: String) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.name(name);
Ok(shareholder.clone())
}
#[rhai_fn(name = "shares", return_raw)]
pub fn set_shares(shareholder: &mut RhaiShareholder, shares: FLOAT) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.shares(shares);
Ok(shareholder.clone())
}
#[rhai_fn(name = "percentage", return_raw)]
pub fn set_percentage(shareholder: &mut RhaiShareholder, percentage: FLOAT) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.percentage(percentage);
Ok(shareholder.clone())
}
#[rhai_fn(name = "since", return_raw)]
pub fn set_since(shareholder: &mut RhaiShareholder, since: INT) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.since(since);
Ok(shareholder.clone())
}
#[rhai_fn(name = "type", return_raw)]
pub fn set_type(shareholder: &mut RhaiShareholder, type_str: String) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let shareholder_type = match type_str.to_lowercase().as_str() {
"individual" => ShareholderType::Individual,
"corporate" => ShareholderType::Corporate,
_ => {
return Err(EvalAltResult::ErrorSystem(
"Invalid shareholder type".into(),
Box::new(EvalAltResult::ErrorRuntime(
format!("Unknown type: {}", type_str).into(),
Position::NONE,
)),
)
.into())
}
};
let owned = std::mem::take(shareholder);
*shareholder = owned.type_(shareholder_type);
Ok(shareholder.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(s: &mut RhaiShareholder) -> i64 { s.base_data.id as i64 }
#[rhai_fn(get = "company_id", pure)] pub fn get_company_id(s: &mut RhaiShareholder) -> INT { s.company_id as INT }
#[rhai_fn(get = "user_id", pure)] pub fn get_user_id(s: &mut RhaiShareholder) -> INT { s.user_id as INT }
#[rhai_fn(get = "name", pure)] pub fn get_name(s: &mut RhaiShareholder) -> String { s.name.clone() }
#[rhai_fn(get = "shares", pure)] pub fn get_shares(s: &mut RhaiShareholder) -> FLOAT { s.shares }
#[rhai_fn(get = "percentage", pure)] pub fn get_percentage(s: &mut RhaiShareholder) -> FLOAT { s.percentage }
#[rhai_fn(get = "since", pure)] pub fn get_since(s: &mut RhaiShareholder) -> INT { s.since }
#[rhai_fn(get = "type", pure)] pub fn get_type(s: &mut RhaiShareholder) -> String { format!("{:?}", s.type_) }
}
pub fn register_shareholder_rhai_module(engine: &mut Engine) {
let mut shareholder_module = exported_module!(rhai_shareholder_module);
register_authorized_create_by_id_fn!(
module: &mut shareholder_module,
rhai_fn_name: "save_shareholder",
resource_type_str: "Shareholder",
rhai_return_rust_type: heromodels::models::biz::shareholder::Shareholder
);
register_authorized_get_by_id_fn!(
module: &mut shareholder_module,
rhai_fn_name: "get_shareholder",
resource_type_str: "Shareholder",
rhai_return_rust_type: heromodels::models::biz::shareholder::Shareholder
);
register_authorized_delete_by_id_fn!(
module: &mut shareholder_module,
rhai_fn_name: "delete_shareholder",
resource_type_str: "Shareholder",
rhai_return_rust_type: heromodels::models::biz::shareholder::Shareholder
);
engine.register_global_module(shareholder_module.into());
println!("Successfully registered shareholder Rhai module.");
}

173
src/dsl/src/calendar.rs Normal file
View File

@ -0,0 +1,173 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Module};
use std::mem;
use std::sync::Arc;
use heromodels::models::calendar::{Calendar, Event, Attendee, AttendanceStatus};
type RhaiCalendar = Calendar;
type RhaiEvent = Event;
type RhaiAttendee = Attendee;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
#[export_module]
mod rhai_calendar_module {
use super::{RhaiCalendar, RhaiEvent, RhaiAttendee, AttendanceStatus};
// --- Attendee Builder ---
#[rhai_fn(name = "new_attendee", return_raw)]
pub fn new_attendee(contact_id: i64) -> Result<RhaiAttendee, Box<EvalAltResult>> {
Ok(Attendee::new(contact_id as u32))
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_attendee_status(attendee: &mut RhaiAttendee, status_str: String) -> Result<RhaiAttendee, Box<EvalAltResult>> {
let status = match status_str.to_lowercase().as_str() {
"accepted" => AttendanceStatus::Accepted,
"declined" => AttendanceStatus::Declined,
"tentative" => AttendanceStatus::Tentative,
"noresponse" => AttendanceStatus::NoResponse,
_ => return Err(EvalAltResult::ErrorSystem("Invalid Status".to_string(), "Must be one of: Accepted, Declined, Tentative, NoResponse".into()).into()),
};
let owned = std::mem::take(attendee);
*attendee = owned.status(status);
Ok(attendee.clone())
}
// --- Event Builder ---
#[rhai_fn(name = "new_event", return_raw)]
pub fn new_event() -> Result<RhaiEvent, Box<EvalAltResult>> {
Ok(Event::new())
}
#[rhai_fn(name = "title", return_raw)]
pub fn set_event_title(event: &mut RhaiEvent, title: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
let owned = std::mem::take(event);
*event = owned.title(title);
Ok(event.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_event_description(event: &mut RhaiEvent, description: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
let owned = std::mem::take(event);
*event = owned.description(description);
Ok(event.clone())
}
#[rhai_fn(name = "location", return_raw)]
pub fn set_event_location(event: &mut RhaiEvent, location: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
let owned = std::mem::take(event);
*event = owned.location(location);
Ok(event.clone())
}
#[rhai_fn(name = "add_attendee", return_raw)]
pub fn add_event_attendee(event: &mut RhaiEvent, attendee: RhaiAttendee) -> Result<RhaiEvent, Box<EvalAltResult>> {
let owned = std::mem::take(event);
*event = owned.add_attendee(attendee);
Ok(event.clone())
}
#[rhai_fn(name = "reschedule", return_raw)]
pub fn reschedule_event(event: &mut RhaiEvent, start_time: i64, end_time: i64) -> Result<RhaiEvent, Box<EvalAltResult>> {
let owned = std::mem::take(event);
*event = owned.reschedule(start_time, end_time);
Ok(event.clone())
}
// --- Calendar Builder ---
#[rhai_fn(name = "new_calendar", return_raw)]
pub fn new_calendar(name: String) -> Result<RhaiCalendar, Box<EvalAltResult>> {
Ok(Calendar::new(None, name))
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_calendar_name(calendar: &mut RhaiCalendar, name: String) -> Result<RhaiCalendar, Box<EvalAltResult>> {
let owned = std::mem::take(calendar);
*calendar = owned.name(name);
Ok(calendar.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_calendar_description(calendar: &mut RhaiCalendar, description: String) -> Result<RhaiCalendar, Box<EvalAltResult>> {
let owned = std::mem::take(calendar);
*calendar = owned.description(description);
Ok(calendar.clone())
}
#[rhai_fn(name = "add_event", return_raw)]
pub fn add_calendar_event(calendar: &mut RhaiCalendar, event_id: i64) -> Result<RhaiCalendar, Box<EvalAltResult>> {
let owned = std::mem::take(calendar);
*calendar = owned.add_event(event_id);
Ok(calendar.clone())
}
// --- Getters ---
// Calendar
#[rhai_fn(get = "id", pure)] pub fn get_calendar_id(c: &mut RhaiCalendar) -> i64 { c.base_data.id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_calendar_name(c: &mut RhaiCalendar) -> String { c.name.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_calendar_description(c: &mut RhaiCalendar) -> Option<String> { c.description.clone() }
#[rhai_fn(get = "events", pure)] pub fn get_calendar_events(c: &mut RhaiCalendar) -> Array { c.events.clone().into_iter().map(Dynamic::from).collect() }
// Event
#[rhai_fn(get = "id", pure)] pub fn get_event_id(e: &mut RhaiEvent) -> i64 { e.base_data.id as i64 }
#[rhai_fn(get = "title", pure)] pub fn get_event_title(e: &mut RhaiEvent) -> String { e.title.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_event_description(e: &mut RhaiEvent) -> Option<String> { e.description.clone() }
#[rhai_fn(get = "start_time", pure)] pub fn get_event_start_time(e: &mut RhaiEvent) -> i64 { e.start_time }
#[rhai_fn(get = "end_time", pure)] pub fn get_event_end_time(e: &mut RhaiEvent) -> i64 { e.end_time }
#[rhai_fn(get = "attendees", pure)] pub fn get_event_attendees(e: &mut RhaiEvent) -> Array { e.attendees.clone().into_iter().map(Dynamic::from).collect() }
#[rhai_fn(get = "location", pure)] pub fn get_event_location(e: &mut RhaiEvent) -> Option<String> { e.location.clone() }
// Attendee
#[rhai_fn(get = "contact_id", pure)] pub fn get_attendee_contact_id(a: &mut RhaiAttendee) -> i64 { a.contact_id as i64 }
#[rhai_fn(get = "status", pure)] pub fn get_attendee_status(a: &mut RhaiAttendee) -> String { format!("{:?}", a.status) }
}
pub fn register_calendar_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_calendar_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_calendar",
resource_type_str: "Calendar",
rhai_return_rust_type: heromodels::models::calendar::Calendar
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_calendar",
resource_type_str: "Calendar",
rhai_return_rust_type: heromodels::models::calendar::Calendar
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_calendar",
resource_type_str: "Calendar",
rhai_return_rust_type: heromodels::models::calendar::Calendar
);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_event",
resource_type_str: "Event",
rhai_return_rust_type: heromodels::models::calendar::Event
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_event",
resource_type_str: "Event",
rhai_return_rust_type: heromodels::models::calendar::Event
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_event",
resource_type_str: "Event",
rhai_return_rust_type: heromodels::models::calendar::Event
);
engine.register_type_with_name::<RhaiAttendee>("Attendee");
engine.register_type_with_name::<RhaiEvent>("Event");
engine.register_global_module(module.into());
println!("Successfully registered calendar Rhai module.");
}

106
src/dsl/src/circle.rs Normal file
View File

@ -0,0 +1,106 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map, Module};
use std::mem;
use std::sync::Arc;
use heromodels::models::circle::Circle;
type RhaiCircle = Circle;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
use heromodels::models::circle::ThemeData;
#[export_module]
mod rhai_circle_module {
use super::{RhaiCircle};
#[rhai_fn(name = "new_circle", return_raw)]
pub fn new_circle() -> Result<RhaiCircle, Box<EvalAltResult>> {
Ok(Circle::new())
}
#[rhai_fn(name = "title", return_raw)]
pub fn set_title(circle: &mut RhaiCircle, title: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned = std::mem::take(circle);
*circle = owned.title(title);
Ok(circle.clone())
}
#[rhai_fn(name = "ws_url", return_raw)]
pub fn set_ws_url(circle: &mut RhaiCircle, ws_url: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned = std::mem::take(circle);
*circle = owned.ws_url(ws_url);
Ok(circle.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(circle: &mut RhaiCircle, description: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned = std::mem::take(circle);
*circle = owned.description(description);
Ok(circle.clone())
}
#[rhai_fn(name = "logo", return_raw)]
pub fn set_logo(circle: &mut RhaiCircle, logo: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned = std::mem::take(circle);
*circle = owned.logo(logo);
Ok(circle.clone())
}
#[rhai_fn(name = "theme", return_raw)]
pub fn set_theme(circle: &mut RhaiCircle, theme: ThemeData) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned = std::mem::take(circle);
*circle = owned.theme(theme);
Ok(circle.clone())
}
#[rhai_fn(name = "add_circle", return_raw)]
pub fn add_circle(circle: &mut RhaiCircle, new_circle: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned = std::mem::take(circle);
*circle = owned.add_circle(new_circle);
Ok(circle.clone())
}
#[rhai_fn(name = "add_member", return_raw)]
pub fn add_member(circle: &mut RhaiCircle, member: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
let owned = std::mem::take(circle);
*circle = owned.add_member(member);
Ok(circle.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(c: &mut RhaiCircle) -> i64 { c.base_data.id as i64 }
#[rhai_fn(get = "title", pure)] pub fn get_title(c: &mut RhaiCircle) -> String { c.title.clone() }
#[rhai_fn(get = "ws_url", pure)] pub fn get_ws_url(c: &mut RhaiCircle) -> String { c.ws_url.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_description(c: &mut RhaiCircle) -> Option<String> { c.description.clone() }
#[rhai_fn(get = "logo", pure)] pub fn get_logo(c: &mut RhaiCircle) -> Option<String> { c.logo.clone() }
#[rhai_fn(get = "circles", pure)] pub fn get_circles(c: &mut RhaiCircle) -> Array { c.circles.clone().into_iter().map(Dynamic::from).collect() }
#[rhai_fn(get = "members", pure)] pub fn get_members(c: &mut RhaiCircle) -> Array { c.members.clone().into_iter().map(Dynamic::from).collect() }
}
pub fn register_circle_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_circle_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_circle",
resource_type_str: "Circle",
rhai_return_rust_type: heromodels::models::circle::Circle
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_circle",
resource_type_str: "Circle",
rhai_return_rust_type: heromodels::models::circle::Circle
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_circle",
resource_type_str: "Circle",
rhai_return_rust_type: heromodels::models::circle::Circle
);
engine.register_global_module(module.into());
println!("Successfully registered circle Rhai module.");
}

248
src/dsl/src/company.rs Normal file
View File

@ -0,0 +1,248 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Dynamic, Engine, EvalAltResult, Module};
use std::mem;
use std::sync::Arc;
use heromodels::models::biz::company::{Company, CompanyStatus, BusinessType};
type RhaiCompany = Company;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
#[export_module]
mod rhai_company_module {
use super::{RhaiCompany, CompanyStatus, BusinessType};
// --- Company Functions ---
#[rhai_fn(name = "new_company", return_raw)]
pub fn new_company() -> Result<RhaiCompany, Box<EvalAltResult>> {
let company = Company::new();
Ok(company)
}
// --- Setters ---
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(company: &mut RhaiCompany, name: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.name(name);
Ok(company.clone())
}
#[rhai_fn(name = "registration_number", return_raw)]
pub fn set_registration_number(company: &mut RhaiCompany, registration_number: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.registration_number(registration_number);
Ok(company.clone())
}
#[rhai_fn(name = "incorporation_date", return_raw)]
pub fn set_incorporation_date(company: &mut RhaiCompany, incorporation_date: i64) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.incorporation_date(incorporation_date);
Ok(company.clone())
}
#[rhai_fn(name = "fiscal_year_end", return_raw)]
pub fn set_fiscal_year_end(company: &mut RhaiCompany, fiscal_year_end: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.fiscal_year_end(fiscal_year_end);
Ok(company.clone())
}
#[rhai_fn(name = "email", return_raw)]
pub fn set_email(company: &mut RhaiCompany, email: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.email(email);
Ok(company.clone())
}
#[rhai_fn(name = "phone", return_raw)]
pub fn set_phone(company: &mut RhaiCompany, phone: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.phone(phone);
Ok(company.clone())
}
#[rhai_fn(name = "website", return_raw)]
pub fn set_website(company: &mut RhaiCompany, website: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.website(website);
Ok(company.clone())
}
#[rhai_fn(name = "address", return_raw)]
pub fn set_address(company: &mut RhaiCompany, address: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.address(address);
Ok(company.clone())
}
#[rhai_fn(name = "business_type", return_raw)]
pub fn set_business_type(company: &mut RhaiCompany, business_type: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let b_type = match business_type.to_lowercase().as_str() {
"coop" => BusinessType::Coop,
"single" => BusinessType::Single,
"twin" => BusinessType::Twin,
"starter" => BusinessType::Starter,
"global" => BusinessType::Global,
_ => return Err(EvalAltResult::ErrorSystem("Invalid Business Type".to_string(), "Must be one of: Coop, Single, Twin, Starter, Global".into()).into()),
};
let owned_company = std::mem::take(company);
*company = owned_company.business_type(b_type);
Ok(company.clone())
}
#[rhai_fn(name = "industry", return_raw)]
pub fn set_industry(company: &mut RhaiCompany, industry: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.industry(industry);
Ok(company.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(company: &mut RhaiCompany, description: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let owned_company = std::mem::take(company);
*company = owned_company.description(description);
Ok(company.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(company: &mut RhaiCompany, status: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
let comp_status = match status.to_lowercase().as_str() {
"active" => CompanyStatus::Active,
"inactive" => CompanyStatus::Inactive,
"suspended" => CompanyStatus::Suspended,
_ => return Err(EvalAltResult::ErrorSystem("Invalid Status".to_string(), "Must be one of: Active, Inactive, Suspended".into()).into()),
};
let owned_company = std::mem::take(company);
*company = owned_company.status(comp_status);
Ok(company.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)]
pub fn get_id(company: &mut RhaiCompany) -> i64 {
company.base_data.id as i64
}
#[rhai_fn(get = "name", pure)]
pub fn get_name(company: &mut RhaiCompany) -> String {
company.name.clone()
}
#[rhai_fn(get = "registration_number", pure)]
pub fn get_registration_number(company: &mut RhaiCompany) -> String {
company.registration_number.clone()
}
#[rhai_fn(get = "incorporation_date", pure)]
pub fn get_incorporation_date(company: &mut RhaiCompany) -> i64 {
company.incorporation_date
}
#[rhai_fn(get = "fiscal_year_end", pure)]
pub fn get_fiscal_year_end(company: &mut RhaiCompany) -> String {
company.fiscal_year_end.clone()
}
#[rhai_fn(get = "email", pure)]
pub fn get_email(company: &mut RhaiCompany) -> String {
company.email.clone()
}
#[rhai_fn(get = "phone", pure)]
pub fn get_phone(company: &mut RhaiCompany) -> String {
company.phone.clone()
}
#[rhai_fn(get = "website", pure)]
pub fn get_website(company: &mut RhaiCompany) -> String {
company.website.clone()
}
#[rhai_fn(get = "address", pure)]
pub fn get_address(company: &mut RhaiCompany) -> String {
company.address.clone()
}
#[rhai_fn(get = "business_type", pure)]
pub fn get_business_type(company: &mut RhaiCompany) -> String {
format!("{:?}", company.business_type)
}
#[rhai_fn(get = "industry", pure)]
pub fn get_industry(company: &mut RhaiCompany) -> String {
company.industry.clone()
}
#[rhai_fn(get = "description", pure)]
pub fn get_description(company: &mut RhaiCompany) -> String {
company.description.clone()
}
#[rhai_fn(get = "status", pure)]
pub fn get_status(company: &mut RhaiCompany) -> String {
format!("{:?}", company.status)
}
#[rhai_fn(get = "created_at", pure)]
pub fn get_created_at(company: &mut RhaiCompany) -> i64 {
company.base_data.created_at
}
#[rhai_fn(get = "modified_at", pure)]
pub fn get_modified_at(company: &mut RhaiCompany) -> i64 {
company.base_data.modified_at
}
}
pub fn register_company_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_company_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_company",
resource_type_str: "Company",
rhai_return_rust_type: heromodels::models::biz::company::Company
);
engine.register_global_module(module.into());
println!("Successfully registered company Rhai module.");
}

161
src/dsl/src/contact.rs Normal file
View File

@ -0,0 +1,161 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Module};
use std::mem;
use std::sync::Arc;
use heromodels::models::contact::{Contact, Group};
type RhaiContact = Contact;
type RhaiGroup = Group;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
#[export_module]
mod rhai_contact_module {
use super::{RhaiContact, RhaiGroup};
// --- Contact Builder ---
#[rhai_fn(name = "new_contact", return_raw)]
pub fn new_contact() -> Result<RhaiContact, Box<EvalAltResult>> {
Ok(Contact::new())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_contact_name(contact: &mut RhaiContact, name: String) -> Result<RhaiContact, Box<EvalAltResult>> {
let owned = std::mem::take(contact);
*contact = owned.name(name);
Ok(contact.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_contact_description(contact: &mut RhaiContact, description: String) -> Result<RhaiContact, Box<EvalAltResult>> {
let owned = std::mem::take(contact);
*contact = owned.description(description);
Ok(contact.clone())
}
#[rhai_fn(name = "address", return_raw)]
pub fn set_contact_address(contact: &mut RhaiContact, address: String) -> Result<RhaiContact, Box<EvalAltResult>> {
let owned = std::mem::take(contact);
*contact = owned.address(address);
Ok(contact.clone())
}
#[rhai_fn(name = "phone", return_raw)]
pub fn set_contact_phone(contact: &mut RhaiContact, phone: String) -> Result<RhaiContact, Box<EvalAltResult>> {
let owned = std::mem::take(contact);
*contact = owned.phone(phone);
Ok(contact.clone())
}
#[rhai_fn(name = "email", return_raw)]
pub fn set_contact_email(contact: &mut RhaiContact, email: String) -> Result<RhaiContact, Box<EvalAltResult>> {
let owned = std::mem::take(contact);
*contact = owned.email(email);
Ok(contact.clone())
}
#[rhai_fn(name = "notes", return_raw)]
pub fn set_contact_notes(contact: &mut RhaiContact, notes: String) -> Result<RhaiContact, Box<EvalAltResult>> {
let owned = std::mem::take(contact);
*contact = owned.notes(notes);
Ok(contact.clone())
}
#[rhai_fn(name = "circle", return_raw)]
pub fn set_contact_circle(contact: &mut RhaiContact, circle: String) -> Result<RhaiContact, Box<EvalAltResult>> {
let owned = std::mem::take(contact);
*contact = owned.circle(circle);
Ok(contact.clone())
}
// --- Group Builder ---
#[rhai_fn(name = "new_group", return_raw)]
pub fn new_group() -> Result<RhaiGroup, Box<EvalAltResult>> {
Ok(Group::new())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_group_name(group: &mut RhaiGroup, name: String) -> Result<RhaiGroup, Box<EvalAltResult>> {
let owned = std::mem::take(group);
*group = owned.name(name);
Ok(group.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_group_description(group: &mut RhaiGroup, description: String) -> Result<RhaiGroup, Box<EvalAltResult>> {
let owned = std::mem::take(group);
*group = owned.description(description);
Ok(group.clone())
}
#[rhai_fn(name = "add_contact", return_raw)]
pub fn add_group_contact(group: &mut RhaiGroup, contact_id: i64) -> Result<RhaiGroup, Box<EvalAltResult>> {
let owned = std::mem::take(group);
*group = owned.add_contact(contact_id as u32);
Ok(group.clone())
}
// --- Getters ---
// Contact
#[rhai_fn(get = "id", pure)] pub fn get_contact_id(c: &mut RhaiContact) -> i64 { c.base_data.id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_contact_name(c: &mut RhaiContact) -> String { c.name.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_contact_description(c: &mut RhaiContact) -> Option<String> { c.description.clone() }
#[rhai_fn(get = "address", pure)] pub fn get_contact_address(c: &mut RhaiContact) -> String { c.address.clone() }
#[rhai_fn(get = "phone", pure)] pub fn get_contact_phone(c: &mut RhaiContact) -> String { c.phone.clone() }
#[rhai_fn(get = "email", pure)] pub fn get_contact_email(c: &mut RhaiContact) -> String { c.email.clone() }
#[rhai_fn(get = "notes", pure)] pub fn get_contact_notes(c: &mut RhaiContact) -> Option<String> { c.notes.clone() }
#[rhai_fn(get = "circle", pure)] pub fn get_contact_circle(c: &mut RhaiContact) -> String { c.circle.clone() }
// Group
#[rhai_fn(get = "id", pure)] pub fn get_group_id(g: &mut RhaiGroup) -> i64 { g.base_data.id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_group_name(g: &mut RhaiGroup) -> String { g.name.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_group_description(g: &mut RhaiGroup) -> Option<String> { g.description.clone() }
#[rhai_fn(get = "contacts", pure)] pub fn get_group_contacts(g: &mut RhaiGroup) -> Array { g.contacts.clone().into_iter().map(|id| Dynamic::from(id as i64)).collect() }
}
pub fn register_contact_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_contact_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_contact",
resource_type_str: "Contact",
rhai_return_rust_type: heromodels::models::contact::Contact
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_contact",
resource_type_str: "Contact",
rhai_return_rust_type: heromodels::models::contact::Contact
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_contact",
resource_type_str: "Contact",
rhai_return_rust_type: heromodels::models::contact::Contact
);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_group",
resource_type_str: "Group",
rhai_return_rust_type: heromodels::models::contact::Group
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_group",
resource_type_str: "Group",
rhai_return_rust_type: heromodels::models::contact::Group
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_group",
resource_type_str: "Group",
rhai_return_rust_type: heromodels::models::contact::Group
);
engine.register_global_module(module.into());
println!("Successfully registered contact Rhai module.");
}

View File

@ -0,0 +1,75 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Module, INT};
use std::mem;
use std::sync::Arc;
use heromodels::models::core::comment::Comment;
type RhaiComment = Comment;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
#[export_module]
mod rhai_comment_module {
use super::{RhaiComment, INT};
#[rhai_fn(name = "new_comment", return_raw)]
pub fn new_comment() -> Result<RhaiComment, Box<EvalAltResult>> {
Ok(Comment::new())
}
#[rhai_fn(name = "user_id", return_raw)]
pub fn set_user_id(comment: &mut RhaiComment, user_id: i64) -> Result<RhaiComment, Box<EvalAltResult>> {
let owned = std::mem::take(comment);
*comment = owned.user_id(user_id as u32);
Ok(comment.clone())
}
#[rhai_fn(name = "content", return_raw)]
pub fn set_content(comment: &mut RhaiComment, content: String) -> Result<RhaiComment, Box<EvalAltResult>> {
let owned = std::mem::take(comment);
*comment = owned.content(content);
Ok(comment.clone())
}
#[rhai_fn(name = "parent_comment_id", return_raw)]
pub fn set_parent_comment_id(comment: &mut RhaiComment, parent_id: i64) -> Result<RhaiComment, Box<EvalAltResult>> {
let owned = std::mem::take(comment);
let parent_id_option = if parent_id > 0 { Some(parent_id as u32) } else { None };
*comment = owned.parent_comment_id(parent_id_option);
Ok(comment.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(c: &mut RhaiComment) -> i64 { c.base_data.id as i64 }
#[rhai_fn(get = "user_id", pure)] pub fn get_user_id(c: &mut RhaiComment) -> i64 { c.user_id as i64 }
#[rhai_fn(get = "content", pure)] pub fn get_content(c: &mut RhaiComment) -> String { c.content.clone() }
#[rhai_fn(get = "parent_comment_id", pure)] pub fn get_parent_comment_id(c: &mut RhaiComment) -> Option<INT> { c.parent_comment_id.map(|id| id as INT) }
}
pub fn register_comment_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_comment_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_comment",
resource_type_str: "Comment",
rhai_return_rust_type: heromodels::models::core::comment::Comment
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_comment",
resource_type_str: "Comment",
rhai_return_rust_type: heromodels::models::core::comment::Comment
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_comment",
resource_type_str: "Comment",
rhai_return_rust_type: heromodels::models::core::comment::Comment
);
engine.register_global_module(module.into());
println!("Successfully registered comment Rhai module.");
}

7
src/dsl/src/core/mod.rs Normal file
View File

@ -0,0 +1,7 @@
use rhai::Engine;
pub mod comment;
pub fn register_core_rhai_module(engine: &mut Engine) {
comment::register_comment_rhai_module(engine);
}

View File

@ -0,0 +1,108 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Engine, EvalAltResult, Module, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::finance::account::Account;
type RhaiAccount = Account;
#[export_module]
mod rhai_account_module {
use super::{Array, RhaiAccount, INT};
#[rhai_fn(name = "new_account", return_raw)]
pub fn new_account() -> Result<RhaiAccount, Box<EvalAltResult>> {
Ok(Account::new())
}
// --- Setters ---
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(account: &mut RhaiAccount, name: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
let owned = std::mem::take(account);
*account = owned.name(name);
Ok(account.clone())
}
#[rhai_fn(name = "user_id", return_raw)]
pub fn set_user_id(account: &mut RhaiAccount, user_id: INT) -> Result<RhaiAccount, Box<EvalAltResult>> {
let owned = std::mem::take(account);
*account = owned.user_id(user_id as u32);
Ok(account.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(account: &mut RhaiAccount, description: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
let owned = std::mem::take(account);
*account = owned.description(description);
Ok(account.clone())
}
#[rhai_fn(name = "ledger", return_raw)]
pub fn set_ledger(account: &mut RhaiAccount, ledger: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
let owned = std::mem::take(account);
*account = owned.ledger(ledger);
Ok(account.clone())
}
#[rhai_fn(name = "address", return_raw)]
pub fn set_address(account: &mut RhaiAccount, address: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
let owned = std::mem::take(account);
*account = owned.address(address);
Ok(account.clone())
}
#[rhai_fn(name = "pubkey", return_raw)]
pub fn set_pubkey(account: &mut RhaiAccount, pubkey: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
let owned = std::mem::take(account);
*account = owned.pubkey(pubkey);
Ok(account.clone())
}
#[rhai_fn(name = "add_asset", return_raw)]
pub fn add_asset(account: &mut RhaiAccount, asset_id: INT) -> Result<RhaiAccount, Box<EvalAltResult>> {
let owned = std::mem::take(account);
*account = owned.add_asset(asset_id as u32);
Ok(account.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(a: &mut RhaiAccount) -> i64 { a.base_data.id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_name(a: &mut RhaiAccount) -> String { a.name.clone() }
#[rhai_fn(get = "user_id", pure)] pub fn get_user_id(a: &mut RhaiAccount) -> INT { a.user_id as INT }
#[rhai_fn(get = "description", pure)] pub fn get_description(a: &mut RhaiAccount) -> String { a.description.clone() }
#[rhai_fn(get = "ledger", pure)] pub fn get_ledger(a: &mut RhaiAccount) -> String { a.ledger.clone() }
#[rhai_fn(get = "address", pure)] pub fn get_address(a: &mut RhaiAccount) -> String { a.address.clone() }
#[rhai_fn(get = "pubkey", pure)] pub fn get_pubkey(a: &mut RhaiAccount) -> String { a.pubkey.clone() }
#[rhai_fn(get = "assets", pure)] pub fn get_assets(a: &mut RhaiAccount) -> Array { a.assets.clone().into_iter().map(|id| (id as INT).into()).collect() }
}
pub fn register_account_rhai_module(engine: &mut Engine) {
let mut account_module = exported_module!(rhai_account_module);
register_authorized_create_by_id_fn!(
module: &mut account_module,
rhai_fn_name: "save_account",
resource_type_str: "Account",
rhai_return_rust_type: heromodels::models::finance::account::Account
);
register_authorized_get_by_id_fn!(
module: &mut account_module,
rhai_fn_name: "get_account",
resource_type_str: "Account",
rhai_return_rust_type: heromodels::models::finance::account::Account
);
register_authorized_delete_by_id_fn!(
module: &mut account_module,
rhai_fn_name: "delete_account",
resource_type_str: "Account",
rhai_return_rust_type: heromodels::models::finance::account::Account
);
engine.register_global_module(account_module.into());
println!("Successfully registered account Rhai module.");
}

View File

@ -0,0 +1,121 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Engine, EvalAltResult, Position, FLOAT, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::finance::asset::{Asset, AssetType};
type RhaiAsset = Asset;
#[export_module]
mod asset_rhai_module {
use super::{RhaiAsset, AssetType, FLOAT, INT};
#[rhai_fn(name = "new_asset", return_raw)]
pub fn new_asset() -> Result<RhaiAsset, Box<EvalAltResult>> {
Ok(Asset::new())
}
// --- Setters ---
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(asset: &mut RhaiAsset, name: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
let owned = std::mem::take(asset);
*asset = owned.name(name);
Ok(asset.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(asset: &mut RhaiAsset, description: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
let owned = std::mem::take(asset);
*asset = owned.description(description);
Ok(asset.clone())
}
#[rhai_fn(name = "amount", return_raw)]
pub fn set_amount(asset: &mut RhaiAsset, amount: FLOAT) -> Result<RhaiAsset, Box<EvalAltResult>> {
let owned = std::mem::take(asset);
*asset = owned.amount(amount);
Ok(asset.clone())
}
#[rhai_fn(name = "address", return_raw)]
pub fn set_address(asset: &mut RhaiAsset, address: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
let owned = std::mem::take(asset);
*asset = owned.address(address);
Ok(asset.clone())
}
#[rhai_fn(name = "decimals", return_raw)]
pub fn set_decimals(asset: &mut RhaiAsset, decimals: INT) -> Result<RhaiAsset, Box<EvalAltResult>> {
let owned = std::mem::take(asset);
*asset = owned.decimals(decimals as u8);
Ok(asset.clone())
}
#[rhai_fn(name = "asset_type", return_raw)]
pub fn set_asset_type(asset: &mut RhaiAsset, type_str: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
let asset_type = match type_str.to_lowercase().as_str() {
"erc20" => AssetType::Erc20,
"erc721" => AssetType::Erc721,
"erc1155" => AssetType::Erc1155,
"native" => AssetType::Native,
_ => {
return Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Unknown asset type: {}", type_str).into(),
Position::NONE,
)));
}
};
let owned = std::mem::take(asset);
*asset = owned.asset_type(asset_type);
Ok(asset.clone())
}
// --- Methods ---
#[rhai_fn(name = "transfer", return_raw)]
pub fn transfer(source: &mut RhaiAsset, mut target: RhaiAsset, amount: FLOAT) -> Result<RhaiAsset, Box<EvalAltResult>> {
source.transfer_to(&mut target, amount).map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), Position::NONE)))?;
Ok(target)
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(a: &mut RhaiAsset) -> i64 { a.base_data.id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_name(a: &mut RhaiAsset) -> String { a.name.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_description(a: &mut RhaiAsset) -> String { a.description.clone() }
#[rhai_fn(get = "amount", pure)] pub fn get_amount(a: &mut RhaiAsset) -> FLOAT { a.amount }
#[rhai_fn(get = "address", pure)] pub fn get_address(a: &mut RhaiAsset) -> String { a.address.clone() }
#[rhai_fn(get = "decimals", pure)] pub fn get_decimals(a: &mut RhaiAsset) -> INT { a.decimals as INT }
#[rhai_fn(get = "asset_type", pure)] pub fn get_asset_type(a: &mut RhaiAsset) -> String { format!("{:?}", a.asset_type) }
#[rhai_fn(get = "formatted_amount", pure)] pub fn get_formatted_amount(a: &mut RhaiAsset) -> String { a.formatted_amount() }
}
pub fn register_asset_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiAsset>();
let mut module = exported_module!(asset_rhai_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_asset",
resource_type_str: "Asset",
rhai_return_rust_type: heromodels::models::finance::asset::Asset
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_asset",
resource_type_str: "Asset",
rhai_return_rust_type: heromodels::models::finance::asset::Asset
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_asset",
resource_type_str: "Asset",
rhai_return_rust_type: heromodels::models::finance::asset::Asset
);
engine.register_global_module(module.into());
println!("Successfully registered asset Rhai module.");
}

View File

@ -0,0 +1,327 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Module, Position, FLOAT, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::finance::asset::AssetType;
use heromodels::models::finance::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
use chrono::DateTime;
type RhaiListing = Listing;
type RhaiBid = Bid;
#[export_module]
mod rhai_bid_module {
use super::{RhaiBid, BidStatus, FLOAT, INT};
#[rhai_fn(name = "new_bid", return_raw)]
pub fn new_bid() -> Result<RhaiBid, Box<EvalAltResult>> {
Ok(Bid::new())
}
// --- Setters ---
#[rhai_fn(name = "listing_id", return_raw)]
pub fn set_listing_id(bid: &mut RhaiBid, listing_id: String) -> Result<RhaiBid, Box<EvalAltResult>> {
let owned = std::mem::take(bid);
*bid = owned.listing_id(listing_id);
Ok(bid.clone())
}
#[rhai_fn(name = "bidder_id", return_raw)]
pub fn set_bidder_id(bid: &mut RhaiBid, bidder_id: INT) -> Result<RhaiBid, Box<EvalAltResult>> {
let owned = std::mem::take(bid);
*bid = owned.bidder_id(bidder_id as u32);
Ok(bid.clone())
}
#[rhai_fn(name = "amount", return_raw)]
pub fn set_amount(bid: &mut RhaiBid, amount: FLOAT) -> Result<RhaiBid, Box<EvalAltResult>> {
let owned = std::mem::take(bid);
*bid = owned.amount(amount);
Ok(bid.clone())
}
#[rhai_fn(name = "currency", return_raw)]
pub fn set_currency(bid: &mut RhaiBid, currency: String) -> Result<RhaiBid, Box<EvalAltResult>> {
let owned = std::mem::take(bid);
*bid = owned.currency(currency);
Ok(bid.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(bid: &mut RhaiBid, status_str: String) -> Result<RhaiBid, Box<EvalAltResult>> {
let status = match status_str.to_lowercase().as_str() {
"active" => BidStatus::Active,
"accepted" => BidStatus::Accepted,
"rejected" => BidStatus::Rejected,
"cancelled" => BidStatus::Cancelled,
_ => {
return Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Invalid bid status: {}", status_str).into(),
Position::NONE,
)));
}
};
let owned = std::mem::take(bid);
*bid = owned.status(status);
Ok(bid.clone())
}
// --- Getters ---
#[rhai_fn(get = "listing_id", pure)] pub fn get_listing_id(b: &mut RhaiBid) -> String { b.listing_id.clone() }
#[rhai_fn(get = "bidder_id", pure)] pub fn get_bidder_id(b: &mut RhaiBid) -> INT { b.bidder_id as INT }
#[rhai_fn(get = "amount", pure)] pub fn get_amount(b: &mut RhaiBid) -> FLOAT { b.amount }
#[rhai_fn(get = "currency", pure)] pub fn get_currency(b: &mut RhaiBid) -> String { b.currency.clone() }
#[rhai_fn(get = "status", pure)] pub fn get_status(b: &mut RhaiBid) -> String { format!("{:?}", b.status) }
#[rhai_fn(get = "created_at", pure)] pub fn get_created_at(b: &mut RhaiBid) -> INT { b.created_at.timestamp() }
}
#[export_module]
mod rhai_listing_module {
use super::{Array, AssetType, DateTime, ListingStatus, ListingType, RhaiBid, RhaiListing, FLOAT, INT, Dynamic};
#[rhai_fn(name = "new_listing", return_raw)]
pub fn new_listing() -> Result<RhaiListing, Box<EvalAltResult>> {
Ok(Listing::new())
}
// --- Setters ---
#[rhai_fn(name = "title", return_raw)]
pub fn set_title(listing: &mut RhaiListing, title: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.title(title);
Ok(listing.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(listing: &mut RhaiListing, description: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.description(description);
Ok(listing.clone())
}
#[rhai_fn(name = "asset_id", return_raw)]
pub fn set_asset_id(listing: &mut RhaiListing, asset_id: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.asset_id(asset_id);
Ok(listing.clone())
}
#[rhai_fn(name = "seller_id", return_raw)]
pub fn set_seller_id(listing: &mut RhaiListing, seller_id: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.seller_id(seller_id);
Ok(listing.clone())
}
#[rhai_fn(name = "price", return_raw)]
pub fn set_price(listing: &mut RhaiListing, price: FLOAT) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.price(price);
Ok(listing.clone())
}
#[rhai_fn(name = "currency", return_raw)]
pub fn set_currency(listing: &mut RhaiListing, currency: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.currency(currency);
Ok(listing.clone())
}
#[rhai_fn(name = "asset_type", return_raw)]
pub fn set_asset_type(listing: &mut RhaiListing, type_str: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let asset_type = match type_str.to_lowercase().as_str() {
"erc20" => AssetType::Erc20,
"erc721" => AssetType::Erc721,
"erc1155" => AssetType::Erc1155,
"native" => AssetType::Native,
_ => return Err(EvalAltResult::ErrorSystem("Invalid asset type".into(), Box::new(EvalAltResult::ErrorRuntime(format!("Unknown type: {}", type_str).into(), Position::NONE))).into()),
};
let owned = std::mem::take(listing);
*listing = owned.asset_type(asset_type);
Ok(listing.clone())
}
#[rhai_fn(name = "listing_type", return_raw)]
pub fn set_listing_type(listing: &mut RhaiListing, type_str: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let listing_type = match type_str.to_lowercase().as_str() {
"fixedprice" => ListingType::FixedPrice,
"auction" => ListingType::Auction,
"exchange" => ListingType::Exchange,
_ => return Err(EvalAltResult::ErrorSystem("Invalid listing type".into(), Box::new(EvalAltResult::ErrorRuntime(format!("Unknown type: {}", type_str).into(), Position::NONE))).into()),
};
let owned = std::mem::take(listing);
*listing = owned.listing_type(listing_type);
Ok(listing.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(listing: &mut RhaiListing, status_str: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let status = match status_str.to_lowercase().as_str() {
"active" => ListingStatus::Active,
"sold" => ListingStatus::Sold,
"cancelled" => ListingStatus::Cancelled,
"expired" => ListingStatus::Expired,
_ => return Err(EvalAltResult::ErrorSystem("Invalid listing status".into(), Box::new(EvalAltResult::ErrorRuntime(format!("Unknown status: {}", status_str).into(), Position::NONE))).into()),
};
let owned = std::mem::take(listing);
*listing = owned.status(status);
Ok(listing.clone())
}
#[rhai_fn(name = "expires_at", return_raw)]
pub fn set_expires_at(listing: &mut RhaiListing, timestamp: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
let dt = if timestamp > 0 { Some(DateTime::from_timestamp(timestamp, 0).ok_or_else(|| EvalAltResult::ErrorSystem("Invalid timestamp".into(), "Cannot convert from timestamp".into()))?) } else { None };
let owned = std::mem::take(listing);
*listing = owned.expires_at(dt);
Ok(listing.clone())
}
#[rhai_fn(name = "image_url", return_raw)]
pub fn set_image_url(listing: &mut RhaiListing, url: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.image_url(Some(url));
Ok(listing.clone())
}
#[rhai_fn(name = "tags", return_raw)]
pub fn set_tags(listing: &mut RhaiListing, tags: Array) -> Result<RhaiListing, Box<EvalAltResult>> {
let rust_tags = tags.into_iter().map(|t| t.into_string().unwrap()).collect();
let mut owned = std::mem::take(listing);
owned.tags = rust_tags;
*listing = owned;
Ok(listing.clone())
}
#[rhai_fn(name = "add_tag", return_raw)]
pub fn add_tag(listing: &mut RhaiListing, tag: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.add_tag(tag);
Ok(listing.clone())
}
#[rhai_fn(name = "bids", return_raw)]
pub fn set_bids(listing: &mut RhaiListing, bids: Array) -> Result<RhaiListing, Box<EvalAltResult>> {
let rust_bids = bids.into_iter().filter_map(|b| b.try_cast::<RhaiBid>()).collect();
let mut owned = std::mem::take(listing);
owned.bids = rust_bids;
*listing = owned;
Ok(listing.clone())
}
#[rhai_fn(name = "buyer_id", return_raw)]
pub fn set_buyer_id(listing: &mut RhaiListing, buyer_id: String) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.buyer_id(buyer_id);
Ok(listing.clone())
}
#[rhai_fn(name = "sale_price", return_raw)]
pub fn set_sale_price(listing: &mut RhaiListing, sale_price: FLOAT) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.sale_price(sale_price);
Ok(listing.clone())
}
#[rhai_fn(name = "sold_at", return_raw)]
pub fn set_sold_at(listing: &mut RhaiListing, timestamp: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
let dt = if timestamp > 0 { Some(DateTime::from_timestamp(timestamp, 0).ok_or_else(|| EvalAltResult::ErrorSystem("Invalid timestamp".into(), "Cannot convert from timestamp".into()))?) } else { None };
let owned = std::mem::take(listing);
*listing = owned.sold_at(dt);
Ok(listing.clone())
}
// --- Methods ---
#[rhai_fn(name = "add_bid", return_raw)]
pub fn add_bid(listing: &mut RhaiListing, bid: RhaiBid) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
match owned.add_bid(bid) {
Ok(l) => { *listing = l; Ok(listing.clone()) },
Err(e) => Err(e.into()),
}
}
#[rhai_fn(name = "highest_bid", return_raw)]
pub fn highest_bid(listing: &mut RhaiListing) -> Result<Dynamic, Box<EvalAltResult>> {
Ok(Dynamic::from(listing.highest_bid().cloned()))
}
#[rhai_fn(name = "complete_sale", return_raw)]
pub fn complete_sale(listing: &mut RhaiListing) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
match owned.complete_sale() {
Ok(l) => { *listing = l; Ok(listing.clone()) },
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(e.into(), Position::NONE))),
}
}
#[rhai_fn(name = "cancel", return_raw)]
pub fn cancel(listing: &mut RhaiListing) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
match owned.cancel() {
Ok(l) => { *listing = l; Ok(listing.clone()) },
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(e.into(), Position::NONE))),
}
}
#[rhai_fn(name = "check_expiration", return_raw)]
pub fn check_expiration(listing: &mut RhaiListing) -> Result<RhaiListing, Box<EvalAltResult>> {
let owned = std::mem::take(listing);
*listing = owned.check_expiration();
Ok(listing.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(l: &mut RhaiListing) -> i64 { l.base_data.id as i64 }
#[rhai_fn(get = "title", pure)] pub fn get_title(l: &mut RhaiListing) -> String { l.title.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_description(l: &mut RhaiListing) -> String { l.description.clone() }
#[rhai_fn(get = "asset_id", pure)] pub fn get_asset_id(l: &mut RhaiListing) -> String { l.asset_id.clone() }
#[rhai_fn(get = "asset_type", pure)] pub fn get_asset_type(l: &mut RhaiListing) -> String { format!("{:?}", l.asset_type) }
#[rhai_fn(get = "seller_id", pure)] pub fn get_seller_id(l: &mut RhaiListing) -> String { l.seller_id.clone() }
#[rhai_fn(get = "price", pure)] pub fn get_price(l: &mut RhaiListing) -> FLOAT { l.price }
#[rhai_fn(get = "currency", pure)] pub fn get_currency(l: &mut RhaiListing) -> String { l.currency.clone() }
#[rhai_fn(get = "listing_type", pure)] pub fn get_listing_type(l: &mut RhaiListing) -> String { format!("{:?}", l.listing_type) }
#[rhai_fn(get = "status", pure)] pub fn get_status(l: &mut RhaiListing) -> String { format!("{:?}", l.status) }
#[rhai_fn(get = "expires_at", pure)] pub fn get_expires_at(l: &mut RhaiListing) -> Option<INT> { l.expires_at.map(|dt| dt.timestamp()) }
#[rhai_fn(get = "sold_at", pure)] pub fn get_sold_at(l: &mut RhaiListing) -> Option<INT> { l.sold_at.map(|dt| dt.timestamp()) }
#[rhai_fn(get = "buyer_id", pure)] pub fn get_buyer_id(l: &mut RhaiListing) -> Option<String> { l.buyer_id.clone() }
#[rhai_fn(get = "sale_price", pure)] pub fn get_sale_price(l: &mut RhaiListing) -> Option<FLOAT> { l.sale_price }
#[rhai_fn(get = "bids", pure)] pub fn get_bids(l: &mut RhaiListing) -> Array { l.bids.clone().into_iter().map(Dynamic::from).collect() }
#[rhai_fn(get = "tags", pure)] pub fn get_tags(l: &mut RhaiListing) -> Array { l.tags.clone().into_iter().map(Dynamic::from).collect() }
#[rhai_fn(get = "image_url", pure)] pub fn get_image_url(l: &mut RhaiListing) -> Option<String> { l.image_url.clone() }
}
pub fn register_marketplace_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiBid>();
engine.build_type::<RhaiListing>();
let bid_module = exported_module!(rhai_bid_module);
engine.register_global_module(bid_module.into());
let mut listing_module = exported_module!(rhai_listing_module);
register_authorized_create_by_id_fn!(
module: &mut listing_module,
rhai_fn_name: "save_listing",
resource_type_str: "Listing",
rhai_return_rust_type: heromodels::models::finance::marketplace::Listing
);
register_authorized_get_by_id_fn!(
module: &mut listing_module,
rhai_fn_name: "get_listing",
resource_type_str: "Listing",
rhai_return_rust_type: heromodels::models::finance::marketplace::Listing
);
register_authorized_delete_by_id_fn!(
module: &mut listing_module,
rhai_fn_name: "delete_listing",
resource_type_str: "Listing",
rhai_return_rust_type: heromodels::models::finance::marketplace::Listing
);
engine.register_global_module(listing_module.into());
println!("Successfully registered marketplace Rhai module.");
}

View File

@ -0,0 +1,11 @@
use rhai::Engine;
pub mod account;
pub mod asset;
pub mod marketplace;
pub fn register_finance_rhai_modules(engine: &mut Engine) {
account::register_account_rhai_module(engine);
asset::register_asset_rhai_module(engine);
marketplace::register_marketplace_rhai_module(engine);
}

80
src/dsl/src/flow/flow.rs Normal file
View File

@ -0,0 +1,80 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, Module, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::flow::flow::Flow;
use heromodels::models::flow::flow_step::FlowStep;
type RhaiFlow = Flow;
type RhaiFlowStep = FlowStep;
#[export_module]
mod rhai_flow_module {
use super::{RhaiFlow, RhaiFlowStep, Array, Dynamic, INT};
#[rhai_fn(name = "new_flow", return_raw)]
pub fn new_flow(flow_uuid: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
Ok(Flow::new(flow_uuid))
}
// --- Setters ---
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(flow: &mut RhaiFlow, name: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
let owned = std::mem::take(flow);
*flow = owned.name(name);
Ok(flow.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(flow: &mut RhaiFlow, status: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
let owned = std::mem::take(flow);
*flow = owned.status(status);
Ok(flow.clone())
}
#[rhai_fn(name = "add_step", return_raw)]
pub fn add_step(flow: &mut RhaiFlow, step: RhaiFlowStep) -> Result<RhaiFlow, Box<EvalAltResult>> {
let owned = std::mem::take(flow);
*flow = owned.add_step(step);
Ok(flow.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(f: &mut RhaiFlow) -> INT { f.base_data.id as INT }
#[rhai_fn(get = "flow_uuid", pure)] pub fn get_flow_uuid(f: &mut RhaiFlow) -> String { f.flow_uuid.clone() }
#[rhai_fn(get = "name", pure)] pub fn get_name(f: &mut RhaiFlow) -> String { f.name.clone() }
#[rhai_fn(get = "status", pure)] pub fn get_status(f: &mut RhaiFlow) -> String { f.status.clone() }
#[rhai_fn(get = "steps", pure)] pub fn get_steps(f: &mut RhaiFlow) -> Array { f.steps.clone().into_iter().map(Dynamic::from).collect() }
}
pub fn register_flow_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiFlow>();
let mut module = exported_module!(rhai_flow_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_flow",
resource_type_str: "Flow",
rhai_return_rust_type: heromodels::models::flow::flow::Flow
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_flow",
resource_type_str: "Flow",
rhai_return_rust_type: heromodels::models::flow::flow::Flow
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_flow",
resource_type_str: "Flow",
rhai_return_rust_type: heromodels::models::flow::flow::Flow
);
engine.register_global_module(module.into());
println!("Successfully registered flow Rhai module.");
}

View File

@ -0,0 +1,78 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Dynamic, Engine, EvalAltResult, Module, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::flow::flow_step::FlowStep;
type RhaiFlowStep = FlowStep;
#[export_module]
mod rhai_flow_step_module {
use super::{RhaiFlowStep, INT};
#[rhai_fn(name = "new_flow_step", return_raw)]
pub fn new_flow_step() -> Result<RhaiFlowStep, Box<EvalAltResult>> {
Ok(FlowStep::default())
}
// --- Setters ---
#[rhai_fn(name = "description", return_raw)]
pub fn set_description(step: &mut RhaiFlowStep, description: String) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
let owned = std::mem::take(step);
*step = owned.description(description);
Ok(step.clone())
}
#[rhai_fn(name = "step_order", return_raw)]
pub fn set_step_order(step: &mut RhaiFlowStep, step_order: INT) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
let mut owned = std::mem::take(step);
owned.step_order = step_order as u32;
*step = owned;
Ok(step.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(step: &mut RhaiFlowStep, status: String) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
let owned = std::mem::take(step);
*step = owned.status(status);
Ok(step.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(s: &mut RhaiFlowStep) -> INT { s.base_data.id as INT }
#[rhai_fn(get = "description", pure)] pub fn get_description(s: &mut RhaiFlowStep) -> Option<String> { s.description.clone() }
#[rhai_fn(get = "step_order", pure)] pub fn get_step_order(s: &mut RhaiFlowStep) -> INT { s.step_order as INT }
#[rhai_fn(get = "status", pure)] pub fn get_status(s: &mut RhaiFlowStep) -> String { s.status.clone() }
}
pub fn register_flow_step_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiFlowStep>();
let mut module = exported_module!(rhai_flow_step_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_flow_step",
resource_type_str: "FlowStep",
rhai_return_rust_type: heromodels::models::flow::flow_step::FlowStep
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_flow_step",
resource_type_str: "FlowStep",
rhai_return_rust_type: heromodels::models::flow::flow_step::FlowStep
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_flow_step",
resource_type_str: "FlowStep",
rhai_return_rust_type: heromodels::models::flow::flow_step::FlowStep
);
engine.register_global_module(module.into());
println!("Successfully registered flow_step Rhai module.");
}

12
src/dsl/src/flow/mod.rs Normal file
View File

@ -0,0 +1,12 @@
use rhai::Engine;
pub mod flow;
pub mod flow_step;
pub mod signature_requirement;
pub fn register_flow_rhai_modules(engine: &mut Engine) {
flow::register_flow_rhai_module(engine);
flow_step::register_flow_step_rhai_module(engine);
signature_requirement::register_signature_requirement_rhai_module(engine);
println!("Successfully registered flow Rhai modules.");
}

View File

@ -0,0 +1,104 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Dynamic, Engine, EvalAltResult, Module, INT};
use std::mem;
use std::sync::Arc;
use heromodels::db::hero::OurDB;
use heromodels::db::Collection;
use heromodels::models::flow::signature_requirement::SignatureRequirement;
type RhaiSignatureRequirement = SignatureRequirement;
#[export_module]
mod rhai_signature_requirement_module {
use super::{RhaiSignatureRequirement, INT};
#[rhai_fn(name = "new_signature_requirement", return_raw)]
pub fn new_signature_requirement() -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
Ok(SignatureRequirement::default())
}
// --- Setters ---
#[rhai_fn(name = "flow_step_id", return_raw)]
pub fn set_flow_step_id(sr: &mut RhaiSignatureRequirement, flow_step_id: INT) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let mut owned = std::mem::take(sr);
owned.flow_step_id = flow_step_id as u32;
*sr = owned;
Ok(sr.clone())
}
#[rhai_fn(name = "public_key", return_raw)]
pub fn set_public_key(sr: &mut RhaiSignatureRequirement, public_key: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let mut owned = std::mem::take(sr);
owned.public_key = public_key;
*sr = owned;
Ok(sr.clone())
}
#[rhai_fn(name = "message", return_raw)]
pub fn set_message(sr: &mut RhaiSignatureRequirement, message: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let mut owned = std::mem::take(sr);
owned.message = message;
*sr = owned;
Ok(sr.clone())
}
#[rhai_fn(name = "signed_by", return_raw)]
pub fn set_signed_by(sr: &mut RhaiSignatureRequirement, signed_by: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned = std::mem::take(sr);
*sr = owned.signed_by(signed_by);
Ok(sr.clone())
}
#[rhai_fn(name = "signature", return_raw)]
pub fn set_signature(sr: &mut RhaiSignatureRequirement, signature: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned = std::mem::take(sr);
*sr = owned.signature(signature);
Ok(sr.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(sr: &mut RhaiSignatureRequirement, status: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
let owned = std::mem::take(sr);
*sr = owned.status(status);
Ok(sr.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(s: &mut RhaiSignatureRequirement) -> INT { s.base_data.id as INT }
#[rhai_fn(get = "flow_step_id", pure)] pub fn get_flow_step_id(s: &mut RhaiSignatureRequirement) -> INT { s.flow_step_id as INT }
#[rhai_fn(get = "public_key", pure)] pub fn get_public_key(s: &mut RhaiSignatureRequirement) -> String { s.public_key.clone() }
#[rhai_fn(get = "message", pure)] pub fn get_message(s: &mut RhaiSignatureRequirement) -> String { s.message.clone() }
#[rhai_fn(get = "signed_by", pure)] pub fn get_signed_by(s: &mut RhaiSignatureRequirement) -> Option<String> { s.signed_by.clone() }
#[rhai_fn(get = "signature", pure)] pub fn get_signature(s: &mut RhaiSignatureRequirement) -> Option<String> { s.signature.clone() }
#[rhai_fn(get = "status", pure)] pub fn get_status(s: &mut RhaiSignatureRequirement) -> String { s.status.clone() }
}
pub fn register_signature_requirement_rhai_module(engine: &mut Engine) {
engine.build_type::<RhaiSignatureRequirement>();
let mut module = exported_module!(rhai_signature_requirement_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_signature_requirement",
resource_type_str: "SignatureRequirement",
rhai_return_rust_type: heromodels::models::flow::signature_requirement::SignatureRequirement
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_signature_requirement",
resource_type_str: "SignatureRequirement",
rhai_return_rust_type: heromodels::models::flow::signature_requirement::SignatureRequirement
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_signature_requirement",
resource_type_str: "SignatureRequirement",
rhai_return_rust_type: heromodels::models::flow::signature_requirement::SignatureRequirement
);
engine.register_global_module(module.into());
println!("Successfully registered signature_requirement Rhai module.");
}

View File

@ -1,6 +1,14 @@
use rhai::Engine; use rhai::Engine;
pub mod library;
pub mod access; pub mod access;
pub mod biz;
pub mod calendar;
pub mod circle;
pub mod company;
pub mod contact;
pub mod core;
pub mod finance;
pub mod flow;
pub mod library;
pub mod object; pub mod object;
pub use macros::register_authorized_get_by_id_fn; pub use macros::register_authorized_get_by_id_fn;
@ -10,7 +18,15 @@ pub use macros::id_from_i64_to_u32;
/// Register all Rhai modules with the engine /// Register all Rhai modules with the engine
pub fn register_dsl_modules(engine: &mut Engine) { pub fn register_dsl_modules(engine: &mut Engine) {
access::register_access_rhai_module(engine); access::register_access_rhai_module(engine);
biz::register_biz_rhai_module(engine);
calendar::register_calendar_rhai_module(engine);
circle::register_circle_rhai_module(engine);
company::register_company_rhai_module(engine);
contact::register_contact_rhai_module(engine);
core::register_core_rhai_module(engine);
finance::register_finance_rhai_modules(engine);
flow::register_flow_rhai_modules(engine);
library::register_library_rhai_module(engine); library::register_library_rhai_module(engine);
object::register_object_rhai_module(engine); object::register_object_fns(engine);
println!("Rhailib Domain Specific Language modules registered successfully."); println!("Rhailib Domain Specific Language modules registered successfully.");
} }

View File

@ -11,7 +11,7 @@ use std::sync::Arc;
use heromodels::models::library::collection::Collection as RhaiCollection; use heromodels::models::library::collection::Collection as RhaiCollection;
use heromodels::models::library::items::{ use heromodels::models::library::items::{
Book as RhaiBook, Image as RhaiImage, Markdown as RhaiMarkdown, Pdf as RhaiPdf, Book as RhaiBook, Image as RhaiImage, Markdown as RhaiMarkdown, Pdf as RhaiPdf,
Slides as RhaiSlides, TocEntry as RhaiTocEntry, Slide as RhaiSlide, Slideshow as RhaiSlideshow, TocEntry as RhaiTocEntry,
}; };
use heromodels::db::Collection as DbCollectionTrait; use heromodels::db::Collection as DbCollectionTrait;
use heromodels::db::hero::OurDB; use heromodels::db::hero::OurDB;
@ -55,8 +55,8 @@ pub struct RhaiMarkdownArray(pub Vec<RhaiMarkdown>);
pub struct RhaiBookArray(pub Vec<RhaiBook>); pub struct RhaiBookArray(pub Vec<RhaiBook>);
#[derive(Debug, Clone, Serialize, CustomType, FromVec)] #[derive(Debug, Clone, Serialize, CustomType, FromVec)]
#[rhai_type(name = "SlidesArray")] #[rhai_type(name = "SlideshowArray")]
pub struct RhaiSlidesArray(pub Vec<RhaiSlides>); pub struct RhaiSlideshowArray(pub Vec<RhaiSlideshow>);
#[derive(Debug, Clone, Serialize, CustomType, FromVec)] #[derive(Debug, Clone, Serialize, CustomType, FromVec)]
#[rhai_type(name = "TocEntryArray")] #[rhai_type(name = "TocEntryArray")]
@ -582,20 +582,20 @@ mod rhai_library_module {
// --- Slides Functions --- // --- Slides Functions ---
#[rhai_fn(name = "new_slides")] #[rhai_fn(name = "new_slides")]
pub fn new_slides() -> RhaiSlides { pub fn new_slides() -> RhaiSlideshow {
RhaiSlides::new() RhaiSlideshow::new()
} }
#[rhai_fn(get = "id", pure)] #[rhai_fn(get = "id", pure)]
pub fn get_slides_id(slides: &mut RhaiSlides) -> i64 { pub fn get_slides_id(slides: &mut RhaiSlideshow) -> i64 {
slides.base_data.id as i64 slides.base_data.id as i64
} }
#[rhai_fn(name = "title", return_raw, global, pure)] #[rhai_fn(name = "title", return_raw, global, pure)]
pub fn slides_title( pub fn slides_title(
slides: &mut RhaiSlides, slides: &mut RhaiSlideshow,
title: String, title: String,
) -> Result<RhaiSlides, Box<EvalAltResult>> { ) -> Result<RhaiSlideshow, Box<EvalAltResult>> {
let owned = mem::take(slides); let owned = mem::take(slides);
*slides = owned.title(title); *slides = owned.title(title);
Ok(slides.clone()) Ok(slides.clone())
@ -603,9 +603,9 @@ mod rhai_library_module {
#[rhai_fn(name = "description", return_raw, global, pure)] #[rhai_fn(name = "description", return_raw, global, pure)]
pub fn slides_description( pub fn slides_description(
slides: &mut RhaiSlides, slides: &mut RhaiSlideshow,
description: String, description: String,
) -> Result<RhaiSlides, Box<EvalAltResult>> { ) -> Result<RhaiSlideshow, Box<EvalAltResult>> {
let owned = mem::take(slides); let owned = mem::take(slides);
*slides = owned.description(description); *slides = owned.description(description);
Ok(slides.clone()) Ok(slides.clone())
@ -613,55 +613,39 @@ mod rhai_library_module {
#[rhai_fn(name = "add_slide", return_raw, global, pure)] #[rhai_fn(name = "add_slide", return_raw, global, pure)]
pub fn slides_add_slide( pub fn slides_add_slide(
slides: &mut RhaiSlides, slideshow: &mut RhaiSlideshow,
url: String, slide: RhaiSlide,
title: String, ) -> Result<RhaiSlideshow, Box<EvalAltResult>> {
) -> Result<RhaiSlides, Box<EvalAltResult>> { let owned = mem::take(slideshow);
let owned = mem::take(slides); *slideshow = owned.add_slide(slide);
let title_opt = if title.is_empty() { None } else { Some(title) }; Ok(slideshow.clone())
*slides = owned.add_slide(url, title_opt);
Ok(slides.clone())
}
#[rhai_fn(name = "add_slide", return_raw, global, pure)]
pub fn slides_add_slide_no_title(
slides: &mut RhaiSlides,
url: String,
) -> Result<RhaiSlides, Box<EvalAltResult>> {
let owned = mem::take(slides);
*slides = owned.add_slide(url, None);
Ok(slides.clone())
} }
#[rhai_fn(get = "created_at", pure)] #[rhai_fn(get = "created_at", pure)]
pub fn get_slides_created_at(slides: &mut RhaiSlides) -> i64 { pub fn get_slides_created_at(slides: &mut RhaiSlideshow) -> i64 {
slides.base_data.created_at slides.base_data.created_at
} }
#[rhai_fn(get = "modified_at", pure)] #[rhai_fn(get = "modified_at", pure)]
pub fn get_slides_modified_at(slides: &mut RhaiSlides) -> i64 { pub fn get_slides_modified_at(slides: &mut RhaiSlideshow) -> i64 {
slides.base_data.modified_at slides.base_data.modified_at
} }
#[rhai_fn(get = "title", pure)] #[rhai_fn(get = "title", pure)]
pub fn get_slides_title(slides: &mut RhaiSlides) -> String { pub fn get_slides_title(slides: &mut RhaiSlideshow) -> String {
slides.title.clone() slides.title.clone()
} }
#[rhai_fn(get = "description", pure)] #[rhai_fn(get = "description", pure)]
pub fn get_slides_description(slides: &mut RhaiSlides) -> Option<String> { pub fn get_slides_description(slides: &mut RhaiSlideshow) -> Option<String> {
slides.description.clone() slides.description.clone()
} }
#[rhai_fn(get = "slide_urls", pure)] #[rhai_fn(get = "slides", pure)]
pub fn get_slides_slide_urls(slides: &mut RhaiSlides) -> Vec<String> { pub fn get_slideshow_slides(slideshow: &mut RhaiSlideshow) -> Vec<RhaiSlide> {
slides.slide_urls.clone() slideshow.slides.clone()
} }
#[rhai_fn(get = "slide_titles", pure)]
pub fn get_slides_slide_titles(slides: &mut RhaiSlides) -> Vec<Option<String>> {
slides.slide_titles.clone()
}
} }
pub fn register_library_rhai_module(engine: &mut Engine) { pub fn register_library_rhai_module(engine: &mut Engine) {
@ -672,7 +656,7 @@ pub fn register_library_rhai_module(engine: &mut Engine) {
register_json_method::<RhaiPdf>(engine); register_json_method::<RhaiPdf>(engine);
register_json_method::<RhaiMarkdown>(engine); register_json_method::<RhaiMarkdown>(engine);
register_json_method::<RhaiBook>(engine); register_json_method::<RhaiBook>(engine);
register_json_method::<RhaiSlides>(engine); register_json_method::<RhaiSlideshow>(engine);
register_json_method::<RhaiTocEntry>(engine); register_json_method::<RhaiTocEntry>(engine);
register_json_method::<RhaiCollectionArray>(engine); register_json_method::<RhaiCollectionArray>(engine);
@ -818,29 +802,29 @@ pub fn register_library_rhai_module(engine: &mut Engine) {
module: &mut module, module: &mut module,
rhai_fn_name: "save_slides", rhai_fn_name: "save_slides",
resource_type_str: "Slides", resource_type_str: "Slides",
rhai_return_rust_type: heromodels::models::library::items::Slides rhai_return_rust_type: heromodels::models::library::items::Slideshow
); );
register_authorized_get_by_id_fn!( register_authorized_get_by_id_fn!(
module: &mut module, module: &mut module,
rhai_fn_name: "get_slides", rhai_fn_name: "get_slides",
resource_type_str: "Slides", resource_type_str: "Slides",
rhai_return_rust_type: heromodels::models::library::items::Slides rhai_return_rust_type: heromodels::models::library::items::Slideshow
); );
register_authorized_delete_by_id_fn!( register_authorized_delete_by_id_fn!(
module: &mut module, module: &mut module,
rhai_fn_name: "delete_slides", rhai_fn_name: "delete_slides",
resource_type_str: "Slides", resource_type_str: "Slides",
rhai_return_rust_type: heromodels::models::library::items::Slides rhai_return_rust_type: heromodels::models::library::items::Slideshow
); );
register_authorized_list_fn!( register_authorized_list_fn!(
module: &mut module, module: &mut module,
rhai_fn_name: "list_slides", rhai_fn_name: "list_slides",
resource_type_str: "Slides", resource_type_str: "Slides",
rhai_return_rust_type: heromodels::models::library::items::Slides, rhai_return_rust_type: heromodels::models::library::items::Slideshow,
rhai_return_wrapper_type: RhaiSlidesArray rhai_return_wrapper_type: RhaiSlideshowArray
); );
engine.register_global_module(module.into()); engine.register_global_module(module.into());

View File

@ -1,67 +1,29 @@
use heromodels::db::Db; use heromodels::db::{Collection, Db};
use heromodels::db::hero::OurDB;
use heromodels::models::object::object::object_rhai_dsl::generated_rhai_module;
use heromodels::models::object::Object;
use macros::{register_authorized_create_by_id_fn, register_authorized_get_by_id_fn}; use macros::{register_authorized_create_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*; use rhai::{exported_module, Engine, EvalAltResult, FuncRegistration, Module};
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc; use std::sync::Arc;
use heromodels::db::Collection; pub fn register_object_fns(engine: &mut Engine) {
use heromodels::models::object::Object; let mut module = Module::new();
type RhaiObject = Object; module.merge(&exported_module!(generated_rhai_module));
use heromodels::db::hero::OurDB;
#[export_module] register_authorized_get_by_id_fn!(
mod rhai_access_module { module: &mut module,
// --- Access Functions --- rhai_fn_name: "get_object_by_id",
#[rhai_fn(name = "new_object", return_raw)] resource_type_str: "Object",
pub fn new_object() -> Result<RhaiObject, Box<EvalAltResult>> { rhai_return_rust_type: heromodels::models::object::Object
let object = Object::new(); );
Ok(object)
}
#[rhai_fn(name = "id", return_raw, global, pure)]
pub fn object_id(object: &mut RhaiObject) -> Result<i64, Box<EvalAltResult>> {
Ok(object.id() as i64)
}
#[rhai_fn(name = "title", return_raw, global, pure)]
pub fn object_title(
object: &mut RhaiObject,
title: String,
) -> Result<RhaiObject, Box<EvalAltResult>> {
let owned_object = std::mem::take(object);
*object = owned_object.title(title);
Ok(object.clone())
}
/// Sets the access name
#[rhai_fn(name = "description", return_raw, global, pure)]
pub fn object_description(
object: &mut RhaiObject,
description: String,
) -> Result<RhaiObject, Box<EvalAltResult>> {
let owned_object = std::mem::take(object);
*object = owned_object.description(description);
Ok(object.clone())
}
}
pub fn register_object_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_access_module);
register_authorized_create_by_id_fn!( register_authorized_create_by_id_fn!(
module: &mut module, module: &mut module,
rhai_fn_name: "save_object", rhai_fn_name: "save_object",
resource_type_str: "Object", resource_type_str: "Object",
rhai_return_rust_type: heromodels::models::object::Object rhai_return_rust_type: heromodels::models::object::Object
); );
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_object",
resource_type_str: "Object",
rhai_return_rust_type: heromodels::models::object::Object
);
engine.register_global_module(module.into()); engine.register_global_module(module.into());
engine.register_type_with_name::<Object>("Object");
} }

177
src/dsl/src/product.rs Normal file
View File

@ -0,0 +1,177 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use heromodels::models::biz::product::{Product, ProductComponent, ProductStatus, ProductType};
type RhaiProduct = Product;
type RhaiProductComponent = ProductComponent;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
#[export_module]
mod rhai_product_module {
use super::{RhaiProduct, RhaiProductComponent, ProductStatus, ProductType};
// --- ProductComponent Builder ---
#[rhai_fn(name = "new_product_component", return_raw)]
pub fn new_product_component() -> Result<RhaiProductComponent, Box<EvalAltResult>> {
Ok(ProductComponent::new())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_component_name(component: &mut RhaiProductComponent, name: String) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned = std::mem::take(component);
*component = owned.name(name);
Ok(component.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_component_description(component: &mut RhaiProductComponent, description: String) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned = std::mem::take(component);
*component = owned.description(description);
Ok(component.clone())
}
#[rhai_fn(name = "quantity", return_raw)]
pub fn set_component_quantity(component: &mut RhaiProductComponent, quantity: i64) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
let owned = std::mem::take(component);
*component = owned.quantity(quantity as u32);
Ok(component.clone())
}
// --- Product Builder ---
#[rhai_fn(name = "new_product", return_raw)]
pub fn new_product() -> Result<RhaiProduct, Box<EvalAltResult>> {
Ok(Product::new())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_product_name(product: &mut RhaiProduct, name: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.name(name);
Ok(product.clone())
}
#[rhai_fn(name = "description", return_raw)]
pub fn set_product_description(product: &mut RhaiProduct, description: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.description(description);
Ok(product.clone())
}
#[rhai_fn(name = "price", return_raw)]
pub fn set_price(product: &mut RhaiProduct, price: f64) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.price(price);
Ok(product.clone())
}
#[rhai_fn(name = "type", return_raw)]
pub fn set_type(product: &mut RhaiProduct, type_str: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
let p_type = match type_str.to_lowercase().as_str() {
"product" => ProductType::Product,
"service" => ProductType::Service,
_ => return Err(EvalAltResult::ErrorSystem("Invalid Type".to_string(), "Must be one of: Product, Service".into()).into()),
};
let owned = std::mem::take(product);
*product = owned.type_(p_type);
Ok(product.clone())
}
#[rhai_fn(name = "category", return_raw)]
pub fn set_category(product: &mut RhaiProduct, category: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.category(category);
Ok(product.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_status(product: &mut RhaiProduct, status_str: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
let p_status = match status_str.to_lowercase().as_str() {
"available" => ProductStatus::Available,
"unavailable" => ProductStatus::Unavailable,
_ => return Err(EvalAltResult::ErrorSystem("Invalid Status".to_string(), "Must be one of: Available, Unavailable".into()).into()),
};
let owned = std::mem::take(product);
*product = owned.status(p_status);
Ok(product.clone())
}
#[rhai_fn(name = "max_amount", return_raw)]
pub fn set_max_amount(product: &mut RhaiProduct, max_amount: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.max_amount(max_amount as u16);
Ok(product.clone())
}
#[rhai_fn(name = "purchase_till", return_raw)]
pub fn set_purchase_till(product: &mut RhaiProduct, purchase_till: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.purchase_till(purchase_till);
Ok(product.clone())
}
#[rhai_fn(name = "active_till", return_raw)]
pub fn set_active_till(product: &mut RhaiProduct, active_till: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.active_till(active_till);
Ok(product.clone())
}
#[rhai_fn(name = "add_component", return_raw)]
pub fn add_component(product: &mut RhaiProduct, component: RhaiProductComponent) -> Result<RhaiProduct, Box<EvalAltResult>> {
let owned = std::mem::take(product);
*product = owned.add_component(component);
Ok(product.clone())
}
// --- Getters for Product ---
#[rhai_fn(get = "id", pure)] pub fn get_id(p: &mut RhaiProduct) -> i64 { p.base_data.id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_name(p: &mut RhaiProduct) -> String { p.name.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_description(p: &mut RhaiProduct) -> String { p.description.clone() }
#[rhai_fn(get = "price", pure)] pub fn get_price(p: &mut RhaiProduct) -> f64 { p.price }
#[rhai_fn(get = "type", pure)] pub fn get_type(p: &mut RhaiProduct) -> String { format!("{:?}", p.type_) }
#[rhai_fn(get = "category", pure)] pub fn get_category(p: &mut RhaiProduct) -> String { p.category.clone() }
#[rhai_fn(get = "status", pure)] pub fn get_status(p: &mut RhaiProduct) -> String { format!("{:?}", p.status) }
#[rhai_fn(get = "max_amount", pure)] pub fn get_max_amount(p: &mut RhaiProduct) -> i64 { p.max_amount as i64 }
#[rhai_fn(get = "purchase_till", pure)] pub fn get_purchase_till(p: &mut RhaiProduct) -> i64 { p.purchase_till }
#[rhai_fn(get = "active_till", pure)] pub fn get_active_till(p: &mut RhaiProduct) -> i64 { p.active_till }
#[rhai_fn(get = "components", pure)] pub fn get_components(p: &mut RhaiProduct) -> Array { p.components.clone().into_iter().map(Dynamic::from).collect() }
// --- Getters for ProductComponent ---
#[rhai_fn(get = "name", pure)] pub fn get_component_name(c: &mut RhaiProductComponent) -> String { c.name.clone() }
#[rhai_fn(get = "description", pure)] pub fn get_component_description(c: &mut RhaiProductComponent) -> String { c.description.clone() }
#[rhai_fn(get = "quantity", pure)] pub fn get_component_quantity(c: &mut RhaiProductComponent) -> i64 { c.quantity as i64 }
}
pub fn register_product_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_product_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_product",
resource_type_str: "Product",
rhai_return_rust_type: heromodels::models::biz::product::Product
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_product",
resource_type_str: "Product",
rhai_return_rust_type: heromodels::models::biz::product::Product
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_product",
resource_type_str: "Product",
rhai_return_rust_type: heromodels::models::biz::product::Product
);
engine.register_type_with_name::<RhaiProductComponent>("ProductComponent");
engine.register_global_module(module.into());
println!("Successfully registered product Rhai module.");
}

181
src/dsl/src/sale.rs Normal file
View File

@ -0,0 +1,181 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use heromodels::models::biz::sale::{Sale, SaleItem, SaleStatus};
type RhaiSale = Sale;
type RhaiSaleItem = SaleItem;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
#[export_module]
mod rhai_sale_module {
use super::{RhaiSale, RhaiSaleItem, SaleStatus};
// --- SaleItem Builder ---
#[rhai_fn(name = "new_sale_item", return_raw)]
pub fn new_sale_item() -> Result<RhaiSaleItem, Box<EvalAltResult>> {
Ok(SaleItem::new())
}
#[rhai_fn(name = "product_id", return_raw)]
pub fn set_item_product_id(item: &mut RhaiSaleItem, product_id: i64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.product_id(product_id as u32);
Ok(item.clone())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_item_name(item: &mut RhaiSaleItem, name: String) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.name(name);
Ok(item.clone())
}
#[rhai_fn(name = "quantity", return_raw)]
pub fn set_item_quantity(item: &mut RhaiSaleItem, quantity: i64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.quantity(quantity as i32);
Ok(item.clone())
}
#[rhai_fn(name = "unit_price", return_raw)]
pub fn set_item_unit_price(item: &mut RhaiSaleItem, unit_price: f64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.unit_price(unit_price);
Ok(item.clone())
}
#[rhai_fn(name = "subtotal", return_raw)]
pub fn set_item_subtotal(item: &mut RhaiSaleItem, subtotal: f64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.subtotal(subtotal);
Ok(item.clone())
}
#[rhai_fn(name = "service_active_until", return_raw)]
pub fn set_item_service_active_until(item: &mut RhaiSaleItem, service_active_until: i64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
let owned = std::mem::take(item);
*item = owned.service_active_until(Some(service_active_until));
Ok(item.clone())
}
// --- Sale Builder ---
#[rhai_fn(name = "new_sale", return_raw)]
pub fn new_sale() -> Result<RhaiSale, Box<EvalAltResult>> {
Ok(Sale::new())
}
#[rhai_fn(name = "company_id", return_raw)]
pub fn set_sale_company_id(sale: &mut RhaiSale, company_id: i64) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.company_id(company_id as u32);
Ok(sale.clone())
}
#[rhai_fn(name = "buyer_id", return_raw)]
pub fn set_sale_buyer_id(sale: &mut RhaiSale, buyer_id: i64) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.buyer_id(buyer_id as u32);
Ok(sale.clone())
}
#[rhai_fn(name = "transaction_id", return_raw)]
pub fn set_sale_transaction_id(sale: &mut RhaiSale, transaction_id: i64) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.transaction_id(transaction_id as u32);
Ok(sale.clone())
}
#[rhai_fn(name = "total_amount", return_raw)]
pub fn set_sale_total_amount(sale: &mut RhaiSale, total_amount: f64) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.total_amount(total_amount);
Ok(sale.clone())
}
#[rhai_fn(name = "status", return_raw)]
pub fn set_sale_status(sale: &mut RhaiSale, status_str: String) -> Result<RhaiSale, Box<EvalAltResult>> {
let s_status = match status_str.to_lowercase().as_str() {
"pending" => SaleStatus::Pending,
"completed" => SaleStatus::Completed,
"cancelled" => SaleStatus::Cancelled,
_ => return Err(EvalAltResult::ErrorSystem("Invalid Status".to_string(), "Must be one of: Pending, Completed, Cancelled".into()).into()),
};
let owned = std::mem::take(sale);
*sale = owned.status(s_status);
Ok(sale.clone())
}
#[rhai_fn(name = "sale_date", return_raw)]
pub fn set_sale_date(sale: &mut RhaiSale, sale_date: i64) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.sale_date(sale_date);
Ok(sale.clone())
}
#[rhai_fn(name = "add_item", return_raw)]
pub fn add_sale_item(sale: &mut RhaiSale, item: RhaiSaleItem) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.add_item(item);
Ok(sale.clone())
}
#[rhai_fn(name = "notes", return_raw)]
pub fn set_sale_notes(sale: &mut RhaiSale, notes: String) -> Result<RhaiSale, Box<EvalAltResult>> {
let owned = std::mem::take(sale);
*sale = owned.notes(notes);
Ok(sale.clone())
}
// --- Getters for Sale ---
#[rhai_fn(get = "id", pure)] pub fn get_id(s: &mut RhaiSale) -> i64 { s.base_data.id as i64 }
#[rhai_fn(get = "company_id", pure)] pub fn get_company_id(s: &mut RhaiSale) -> i64 { s.company_id as i64 }
#[rhai_fn(get = "buyer_id", pure)] pub fn get_buyer_id(s: &mut RhaiSale) -> i64 { s.buyer_id as i64 }
#[rhai_fn(get = "transaction_id", pure)] pub fn get_transaction_id(s: &mut RhaiSale) -> i64 { s.transaction_id as i64 }
#[rhai_fn(get = "total_amount", pure)] pub fn get_total_amount(s: &mut RhaiSale) -> f64 { s.total_amount }
#[rhai_fn(get = "status", pure)] pub fn get_status(s: &mut RhaiSale) -> String { format!("{:?}", s.status) }
#[rhai_fn(get = "sale_date", pure)] pub fn get_sale_date(s: &mut RhaiSale) -> i64 { s.sale_date }
#[rhai_fn(get = "items", pure)] pub fn get_items(s: &mut RhaiSale) -> Array { s.items.clone().into_iter().map(Dynamic::from).collect() }
#[rhai_fn(get = "notes", pure)] pub fn get_notes(s: &mut RhaiSale) -> String { s.notes.clone() }
// --- Getters for SaleItem ---
#[rhai_fn(get = "product_id", pure)] pub fn get_item_product_id(i: &mut RhaiSaleItem) -> i64 { i.product_id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_item_name(i: &mut RhaiSaleItem) -> String { i.name.clone() }
#[rhai_fn(get = "quantity", pure)] pub fn get_item_quantity(i: &mut RhaiSaleItem) -> i64 { i.quantity as i64 }
#[rhai_fn(get = "unit_price", pure)] pub fn get_item_unit_price(i: &mut RhaiSaleItem) -> f64 { i.unit_price }
#[rhai_fn(get = "subtotal", pure)] pub fn get_item_subtotal(i: &mut RhaiSaleItem) -> f64 { i.subtotal }
#[rhai_fn(get = "service_active_until", pure)] pub fn get_item_service_active_until(i: &mut RhaiSaleItem) -> i64 { i.service_active_until.unwrap_or(0) }
}
pub fn register_sale_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_sale_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_sale",
resource_type_str: "Sale",
rhai_return_rust_type: heromodels::models::biz::sale::Sale
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_sale",
resource_type_str: "Sale",
rhai_return_rust_type: heromodels::models::biz::sale::Sale
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_sale",
resource_type_str: "Sale",
rhai_return_rust_type: heromodels::models::biz::sale::Sale
);
engine.register_type_with_name::<RhaiSaleItem>("SaleItem");
engine.register_global_module(module.into());
println!("Successfully registered sale Rhai module.");
}

113
src/dsl/src/shareholder.rs Normal file
View File

@ -0,0 +1,113 @@
use heromodels::db::Db;
use macros::{register_authorized_create_by_id_fn, register_authorized_delete_by_id_fn, register_authorized_get_by_id_fn};
use rhai::plugin::*;
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
use std::mem;
use std::sync::Arc;
use heromodels::models::biz::shareholder::{Shareholder, ShareholderType};
type RhaiShareholder = Shareholder;
use heromodels::db::Collection;
use heromodels::db::hero::OurDB;
#[export_module]
mod rhai_shareholder_module {
use super::{RhaiShareholder, ShareholderType};
#[rhai_fn(name = "new_shareholder", return_raw)]
pub fn new_shareholder() -> Result<RhaiShareholder, Box<EvalAltResult>> {
Ok(Shareholder::new())
}
#[rhai_fn(name = "company_id", return_raw)]
pub fn set_company_id(shareholder: &mut RhaiShareholder, company_id: i64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.company_id(company_id as u32);
Ok(shareholder.clone())
}
#[rhai_fn(name = "user_id", return_raw)]
pub fn set_user_id(shareholder: &mut RhaiShareholder, user_id: i64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.user_id(user_id as u32);
Ok(shareholder.clone())
}
#[rhai_fn(name = "name", return_raw)]
pub fn set_name(shareholder: &mut RhaiShareholder, name: String) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.name(name);
Ok(shareholder.clone())
}
#[rhai_fn(name = "shares", return_raw)]
pub fn set_shares(shareholder: &mut RhaiShareholder, shares: f64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.shares(shares);
Ok(shareholder.clone())
}
#[rhai_fn(name = "percentage", return_raw)]
pub fn set_percentage(shareholder: &mut RhaiShareholder, percentage: f64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.percentage(percentage);
Ok(shareholder.clone())
}
#[rhai_fn(name = "type", return_raw)]
pub fn set_type(shareholder: &mut RhaiShareholder, type_str: String) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let s_type = match type_str.to_lowercase().as_str() {
"individual" => ShareholderType::Individual,
"corporate" => ShareholderType::Corporate,
_ => return Err(EvalAltResult::ErrorSystem("Invalid Type".to_string(), "Must be one of: Individual, Corporate".into()).into()),
};
let owned = std::mem::take(shareholder);
*shareholder = owned.type_(s_type);
Ok(shareholder.clone())
}
#[rhai_fn(name = "since", return_raw)]
pub fn set_since(shareholder: &mut RhaiShareholder, since: i64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
let owned = std::mem::take(shareholder);
*shareholder = owned.since(since);
Ok(shareholder.clone())
}
// --- Getters ---
#[rhai_fn(get = "id", pure)] pub fn get_id(s: &mut RhaiShareholder) -> i64 { s.base_data.id as i64 }
#[rhai_fn(get = "company_id", pure)] pub fn get_company_id(s: &mut RhaiShareholder) -> i64 { s.company_id as i64 }
#[rhai_fn(get = "user_id", pure)] pub fn get_user_id(s: &mut RhaiShareholder) -> i64 { s.user_id as i64 }
#[rhai_fn(get = "name", pure)] pub fn get_name(s: &mut RhaiShareholder) -> String { s.name.clone() }
#[rhai_fn(get = "shares", pure)] pub fn get_shares(s: &mut RhaiShareholder) -> f64 { s.shares }
#[rhai_fn(get = "percentage", pure)] pub fn get_percentage(s: &mut RhaiShareholder) -> f64 { s.percentage }
#[rhai_fn(get = "type", pure)] pub fn get_type(s: &mut RhaiShareholder) -> String { format!("{:?}", s.type_) }
#[rhai_fn(get = "since", pure)] pub fn get_since(s: &mut RhaiShareholder) -> i64 { s.since }
}
pub fn register_shareholder_rhai_module(engine: &mut Engine) {
let mut module = exported_module!(rhai_shareholder_module);
register_authorized_create_by_id_fn!(
module: &mut module,
rhai_fn_name: "save_shareholder",
resource_type_str: "Shareholder",
rhai_return_rust_type: heromodels::models::biz::shareholder::Shareholder
);
register_authorized_get_by_id_fn!(
module: &mut module,
rhai_fn_name: "get_shareholder",
resource_type_str: "Shareholder",
rhai_return_rust_type: heromodels::models::biz::shareholder::Shareholder
);
register_authorized_delete_by_id_fn!(
module: &mut module,
rhai_fn_name: "delete_shareholder",
resource_type_str: "Shareholder",
rhai_return_rust_type: heromodels::models::biz::shareholder::Shareholder
);
engine.register_global_module(module.into());
println!("Successfully registered shareholder Rhai module.");
}

View File

@ -1,8 +1,6 @@
use rhai::{Engine, EvalAltResult, Scope, AST}; // Added EvalAltResult use rhai::{Engine, EvalAltResult, Scope, AST}; // Added EvalAltResult
use std::sync::Arc;
// use std::sync::Mutex; // Unused // use std::sync::Mutex; // Unused
// use std::collections::HashMap; // Unused // use std::collections::HashMap; // Unused
use heromodels::db::hero::OurDB;
use std::fs; // For file operations use std::fs; // For file operations
use std::path::Path; // For path handling use std::path::Path; // For path handling
use rhailib_dsl; use rhailib_dsl;

View File

@ -14,7 +14,7 @@
//! 2. The macros internally use `can_access_resource` for authorization checks. //! 2. The macros internally use `can_access_resource` for authorization checks.
//! 3. Ensure `CALLER_PUBLIC_KEY` is set in the Rhai engine's scope before calling authorized functions. //! 3. Ensure `CALLER_PUBLIC_KEY` is set in the Rhai engine's scope before calling authorized functions.
use rhai::{EvalAltResult, Position, FuncRegistration}; use rhai::{EvalAltResult, Position};
use std::convert::TryFrom; use std::convert::TryFrom;
/// Extracts the `CALLER_PUBLIC_KEY` string constant from the Rhai `NativeCallContext`. /// Extracts the `CALLER_PUBLIC_KEY` string constant from the Rhai `NativeCallContext`.
@ -188,11 +188,17 @@ macro_rules! register_authorized_create_by_id_fn {
let caller_pk_str = pk_dynamic.clone().into_string()?; let caller_pk_str = pk_dynamic.clone().into_string()?;
if circle_pk != caller_pk_str { if circle_pk != caller_pk_str {
// TODO: check if caller pk is member of circle let is_circle_member = heromodels::models::access::access::is_circle_member(
return Err(Box::new(EvalAltResult::ErrorRuntime( db.clone(),
format!("Insufficient authorization. Caller public key {} does not match circle public key {}", caller_pk_str, circle_pk).into(), &caller_pk_str,
context.position(), );
))); if !is_circle_member {
// TODO: check if caller pk is member of circle
return Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Insufficient authorization. Caller public key {} does not match circle public key {}", caller_pk_str, circle_pk).into(),
context.position(),
)));
}
} }
let result = db.set(&object).map_err(|e| { let result = db.set(&object).map_err(|e| {
@ -246,20 +252,17 @@ macro_rules! register_authorized_delete_by_id_fn {
let caller_pk_str = pk_dynamic.clone().into_string()?; let caller_pk_str = pk_dynamic.clone().into_string()?;
if circle_pk != caller_pk_str { if circle_pk != caller_pk_str {
// Use the standalone can_access_resource function from heromodels let is_circle_member = heromodels::models::access::access::is_circle_member(
let has_access = heromodels::models::access::access::can_access_resource(
db.clone(), db.clone(),
&caller_pk_str, &caller_pk_str,
actual_id,
$resource_type_str,
); );
if !is_circle_member {
if !has_access { // TODO: check if caller pk is member of circle
return Err(Box::new(EvalAltResult::ErrorRuntime( return Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Access denied for public key: {}", caller_pk_str).into(), format!("Insufficient authorization. Caller public key {} does not match circle public key {}", caller_pk_str, circle_pk).into(),
context.position(), context.position(),
))); )));
} }
} }
let result = db let result = db

View File

@ -1,5 +1,5 @@
use anyhow::Context; use anyhow::Context;
use rhai_client::{RhaiClient, RhaiClientError, RhaiTaskDetails}; use rhai_client::{RhaiClient, RhaiClientBuilder, RhaiClientError};
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::{Config, DefaultEditor, EditMode}; use rustyline::{Config, DefaultEditor, EditMode};
use std::env; use std::env;
@ -25,7 +25,11 @@ async fn execute_script(client: &RhaiClient, circle_name: &str, script_content:
let timeout = Duration::from_secs(DEFAULT_SCRIPT_TIMEOUT_SECONDS); let timeout = Duration::from_secs(DEFAULT_SCRIPT_TIMEOUT_SECONDS);
match client match client
.submit_script_and_await_result(circle_name, script_content, None, timeout) .new_play_request()
.recipient_id(circle_name)
.script(&script_content)
.timeout(timeout)
.await_response()
.await .await
{ {
Ok(task_details) => { Ok(task_details) => {
@ -77,7 +81,10 @@ async fn run_repl(redis_url: String, circle_name: String) -> anyhow::Result<()>
circle_name, redis_url circle_name, redis_url
); );
let client = RhaiClient::new(&redis_url) let client = RhaiClientBuilder::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 RhaiClient 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_client, connection is handled per-operation or pooled.

View File

@ -1,8 +1,6 @@
use clap::Parser; use clap::Parser;
use engine::create_heromodels_engine; use rhailib_engine::create_heromodels_engine;
use heromodels::db::hero::OurDB;
use rhailib_worker::spawn_rhai_worker; use rhailib_worker::spawn_rhai_worker;
use std::sync::Arc;
use tokio::sync::mpsc; use tokio::sync::mpsc;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -23,6 +21,10 @@ struct Args {
/// Preserve task details after completion (for benchmarking) /// Preserve task details after completion (for benchmarking)
#[arg(long, default_value = "false")] #[arg(long, default_value = "false")]
preserve_tasks: bool, preserve_tasks: bool,
/// Root directory for engine database
#[arg(long, default_value = "worker_rhai_temp_db")]
db_path: String,
} }
#[tokio::main] #[tokio::main]
@ -39,13 +41,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
args.redis_url args.redis_url
); );
// Initialize database with OurDB for the Rhai engine let mut engine = create_heromodels_engine();
// Using a temporary/in-memory like database for the worker
let db = Arc::new(
OurDB::new("worker_rhai_temp_db", true)
.expect("Failed to create temporary DB for Rhai engine"),
);
let mut engine = create_heromodels_engine(db);
// Performance optimizations for benchmarking // Performance optimizations for benchmarking
engine.set_max_operations(0); // Unlimited operations for performance testing engine.set_max_operations(0); // Unlimited operations for performance testing
@ -64,8 +60,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Spawn the worker // Spawn the worker
let worker_handle = spawn_rhai_worker( let worker_handle = spawn_rhai_worker(
1, // circle_id (not used but required)
args.circle_public_key, args.circle_public_key,
args.db_path,
engine, engine,
args.redis_url, args.redis_url,
shutdown_rx, shutdown_rx,