remove rhai dsls
This commit is contained in:
parent
f2dfde5e8f
commit
149c177def
42
heromodels/Cargo.lock
generated
42
heromodels/Cargo.lock
generated
@ -146,6 +146,14 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||
|
||||
[[package]]
|
||||
name = "derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
@ -181,6 +189,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
"derive",
|
||||
"heromodels-derive",
|
||||
"heromodels_core",
|
||||
"ourdb",
|
||||
@ -200,7 +209,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -410,7 +419,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rhai",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -421,7 +430,7 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -463,7 +472,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -529,7 +538,18 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -566,7 +586,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -658,7 +678,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -680,7 +700,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -715,7 +735,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -726,7 +746,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -779,5 +799,5 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
@ -14,6 +14,7 @@ ourdb = { path = "../ourdb" }
|
||||
tst = { path = "../tst" }
|
||||
heromodels-derive = { path = "../heromodels-derive" }
|
||||
heromodels_core = { path = "../heromodels_core" }
|
||||
rhailib_derive = { package = "derive", path = "../../rhailib/src/derive" }
|
||||
rhai = { version = "1.21.0", features = ["std", "sync", "decimal", "internals"] } # Added "decimal" feature, sync for Arc<Mutex<>>
|
||||
rhai_client_macros = { path = "../rhai_client_macros" }
|
||||
strum = "0.26"
|
||||
|
@ -1,275 +0,0 @@
|
||||
// Hero Models - Biz Rhai Example
|
||||
|
||||
print("Hero Models - Biz Rhai Example");
|
||||
print("===============================");
|
||||
print("DB instance will be implicitly passed to DB functions.");
|
||||
|
||||
// --- Enum Constants ---
|
||||
print("\n--- Enum Constants ---");
|
||||
print(`CompanyStatus Active: ${CompanyStatusConstants::Active}`);
|
||||
print(`CompanyStatus Inactive: ${CompanyStatusConstants::Inactive}`);
|
||||
print(`BusinessType Coop: ${BusinessTypeConstants::Coop}`);
|
||||
print(`BusinessType Global: ${BusinessTypeConstants::Global}`);
|
||||
|
||||
// --- Testing Company Model ---
|
||||
print("\n--- Testing Company Model ---");
|
||||
|
||||
let company1_uuid = "comp-uuid-alpha-001";
|
||||
let company1_name = "Innovatech Solutions Ltd.";
|
||||
let company1_reg = "REG-ISL-2024";
|
||||
let company1_inc_date = 1672531200; // Jan 1, 2023
|
||||
|
||||
print(`Creating a new company (UUID: ${company1_uuid})...`);
|
||||
let company1 = new_company(company1_name, company1_reg, company1_inc_date)
|
||||
.email("contact@innovatech.com")
|
||||
.phone("+1-555-0100")
|
||||
.website("https://innovatech.com")
|
||||
.address("123 Innovation Drive, Tech City, TC 54321")
|
||||
.business_type(BusinessTypeConstants::Global)
|
||||
.industry("Technology")
|
||||
.description("Leading provider of innovative tech solutions.")
|
||||
.status(CompanyStatusConstants::Active)
|
||||
.fiscal_year_end("12-31")
|
||||
.set_base_created_at(1672531200)
|
||||
.set_base_modified_at(1672531205);
|
||||
|
||||
print(`Company 1 Name: ${company1.name}, Status: ${company1.status}`);
|
||||
print(`Company 1 Email: ${company1.email}, Industry: ${company1.industry}`);
|
||||
// Save the company to the database
|
||||
print("\nSaving company1 to database...");
|
||||
company1 = set_company(company1); // Capture the company with the DB-assigned ID
|
||||
print("Company1 saved.");
|
||||
|
||||
// Retrieve the company
|
||||
print(`\nRetrieving company by ID (${company1.id})...`);
|
||||
let retrieved_company = get_company_by_id(company1.id);
|
||||
print(`Retrieved Company: ${retrieved_company.name}, Status: ${retrieved_company.status}`);
|
||||
print(`Retrieved Company Reg No: ${retrieved_company.registration_number}, Website: ${retrieved_company.website}`);
|
||||
|
||||
// --- Testing Shareholder Model ---
|
||||
print("\n--- Testing Shareholder Model ---");
|
||||
|
||||
|
||||
let sh1_user_id = 3001; // Example user ID
|
||||
let sh1_name = "Alice Wonderland";
|
||||
let sh1_since = 1672617600; // Example timestamp (Jan 2, 2023)
|
||||
|
||||
print(`Creating shareholder 1 for company ${company1.id}...`);
|
||||
let shareholder1 = new_shareholder()
|
||||
.company_id(company1.id)
|
||||
.user_id(sh1_user_id)
|
||||
.name(sh1_name)
|
||||
.shares(1000.0)
|
||||
.percentage(10.0) // CALCULATED
|
||||
.type_("Individual")
|
||||
.since(sh1_since)
|
||||
.set_base_created_at(1672617600);
|
||||
|
||||
shareholder1 = set_shareholder(shareholder1);
|
||||
print("Shareholder 1 saved.");
|
||||
|
||||
let retrieved_sh1 = get_shareholder_by_id(shareholder1.id);
|
||||
print(`Retrieved Shareholder 1: ${retrieved_sh1.name}, Type: ${retrieved_sh1.type_}, Shares: ${retrieved_sh1.shares}`);
|
||||
|
||||
|
||||
let sh2_entity_id = 4001; // Example corporate entity ID
|
||||
let sh2_name = "Mad Hatter Inc.";
|
||||
let sh2_since = 1672704000; // Example timestamp (Jan 3, 2023)
|
||||
|
||||
print(`\nCreating shareholder 2 for company ${company1.id}...`);
|
||||
let shareholder2 = new_shareholder()
|
||||
.company_id(company1.id)
|
||||
.user_id(sh2_entity_id) // Using user_id field for entity_id for simplicity in example
|
||||
.name(sh2_name)
|
||||
.shares(5000.0)
|
||||
.percentage(50.0)
|
||||
.type_(ShareholderTypeConstants::Corporate)
|
||||
.since(sh2_since)
|
||||
.set_base_created_at(1672704000);
|
||||
|
||||
shareholder2 = set_shareholder(shareholder2);
|
||||
print("Shareholder 2 saved.");
|
||||
|
||||
let retrieved_sh2 = get_shareholder_by_id(shareholder2.id);
|
||||
print(`Retrieved Shareholder 2: ${retrieved_sh2.name}, Type: ${retrieved_sh2.type_}, Percentage: ${retrieved_sh2.percentage}`);
|
||||
|
||||
// --- Testing Update for Company (Example - if setters were fully implemented for complex updates) ---
|
||||
print("\n--- Testing Update for Company ---");
|
||||
let updated_company = retrieved_company
|
||||
.description("Leading global provider of cutting-edge technology solutions and services.")
|
||||
.status(CompanyStatusConstants::Active)
|
||||
.phone("+1-555-0199"); // Assume modified_at would be updated by set_company
|
||||
|
||||
print(`Updated Company - Name: ${updated_company.name}, New Phone: ${updated_company.phone}`);
|
||||
set_company(updated_company);
|
||||
print("Updated Company saved.");
|
||||
|
||||
let final_retrieved_company = get_company_by_id(company1.id);
|
||||
print(`Final Retrieved Company - Description: '${final_retrieved_company.description}', Phone: ${final_retrieved_company.phone}`);
|
||||
|
||||
print("\n--- Testing Product Model ---");
|
||||
|
||||
// Print ProductType constants
|
||||
print("\n--- ProductType Constants ---");
|
||||
print(`Product Type Product: ${ProductTypeConstants::Product}`);
|
||||
print(`Product Type Service: ${ProductTypeConstants::Service}`);
|
||||
|
||||
// Print ProductStatus constants
|
||||
print("\n--- ProductStatus Constants ---");
|
||||
print(`Product Status Available: ${ProductStatusConstants::Available}`);
|
||||
print(`Product Status Unavailable: ${ProductStatusConstants::Unavailable}`);
|
||||
|
||||
// Create a product component
|
||||
let component1 = new_product_component("Super Capacitor")
|
||||
.description("High-capacity energy storage unit")
|
||||
.quantity(2);
|
||||
print(`\nCreated Product Component: ${component1.name}, Qty: ${component1.quantity}`);
|
||||
|
||||
// Create Product 1 (a physical product with a component)
|
||||
|
||||
let product1_name = "Advanced Gadget X";
|
||||
let product1_creation_time = 1672876800; // Example timestamp (Jan 5, 2023)
|
||||
|
||||
print(`\nCreating Product 1: ${product1_name}...`);
|
||||
let product1 = new_product()
|
||||
.name(product1_name)
|
||||
.description("A revolutionary gadget with cutting-edge features.")
|
||||
.price(299.99)
|
||||
.type_(ProductTypeConstants::Product)
|
||||
.category("Electronics")
|
||||
.status(ProductStatusConstants::Available)
|
||||
.max_amount(1000)
|
||||
.purchase_till(1704067199) // Dec 31, 2023, 23:59:59
|
||||
.active_till(1735689599) // Dec 31, 2024, 23:59:59
|
||||
.add_component(component1)
|
||||
.set_base_created_at(product1_creation_time);
|
||||
|
||||
print("Saving Product 1...");
|
||||
product1 = set_product(product1);
|
||||
print("Product 1 saved.");
|
||||
|
||||
print(`\nRetrieving Product 1 (ID: ${product1.id})...`);
|
||||
let retrieved_product1 = get_product_by_id(product1.id);
|
||||
print(`Retrieved Product 1: ${retrieved_product1.name}, Price: ${retrieved_product1.price}, Type: ${retrieved_product1.type_}`);
|
||||
if retrieved_product1.components.len() > 0 {
|
||||
print(`Product 1 Component 1: ${retrieved_product1.components[0].name}, Desc: ${retrieved_product1.components[0].description}`);
|
||||
} else {
|
||||
print("Product 1 has no components.");
|
||||
}
|
||||
|
||||
// Create Product 2 (a service)
|
||||
|
||||
let product2_name = "Cloud Backup Service - Pro Plan";
|
||||
let product2_creation_time = 1672963200; // Example timestamp (Jan 6, 2023)
|
||||
|
||||
print(`\nCreating Product 2: ${product2_name}...`);
|
||||
let product2 = new_product()
|
||||
.name(product2_name)
|
||||
.description("Unlimited cloud backup with 24/7 support.")
|
||||
.price(19.99) // Monthly price
|
||||
.type_(ProductTypeConstants::Service)
|
||||
.category("Cloud Services")
|
||||
.status(ProductStatusConstants::Available)
|
||||
.active_till(1735689599) // Valid for a long time, or represents subscription cycle end
|
||||
.set_base_created_at(product2_creation_time);
|
||||
|
||||
print("Saving Product 2...");
|
||||
product2 = set_product(product2);
|
||||
print("Product 2 saved.");
|
||||
|
||||
print(`\nRetrieving Product 2 (ID: ${product2.id})...`);
|
||||
let retrieved_product2 = get_product_by_id(product2.id);
|
||||
print(`Retrieved Product 2: ${retrieved_product2.name}, Category: ${retrieved_product2.category}, Status: ${retrieved_product2.status}`);
|
||||
if retrieved_product2.components.len() > 0 {
|
||||
print(`Product 2 has ${retrieved_product2.components.len()} components.`);
|
||||
} else {
|
||||
print("Product 2 has no components (as expected for a service).");
|
||||
}
|
||||
|
||||
|
||||
// --- Testing Sale Model ---
|
||||
print("\n--- Testing Sale Model ---");
|
||||
|
||||
// Print SaleStatus constants
|
||||
print("\n--- SaleStatus Constants ---");
|
||||
print(`Sale Status Pending: ${SaleStatusConstants::Pending}`);
|
||||
print(`Sale Status Completed: ${SaleStatusConstants::Completed}`);
|
||||
print(`Sale Status Cancelled: ${SaleStatusConstants::Cancelled}`);
|
||||
|
||||
// Create SaleItem 1 (using product1 from above)
|
||||
let sale_item1_product_id = product1.id; // Using product1.id after it's set // Using product1_id from product example
|
||||
let sale_item1_name = retrieved_product1.name; // Using name from retrieved product1
|
||||
let sale_item1_qty = 2;
|
||||
let sale_item1_unit_price = retrieved_product1.price;
|
||||
let sale_item1_subtotal = sale_item1_qty * sale_item1_unit_price;
|
||||
|
||||
print(`\nCreating SaleItem 1 for Product ID: ${sale_item1_product_id}, Name: ${sale_item1_name}...`);
|
||||
let sale_item1 = new_sale_item(sale_item1_product_id, sale_item1_name, sale_item1_qty, sale_item1_unit_price, sale_item1_subtotal);
|
||||
print(`SaleItem 1: Product ID ${sale_item1.product_id}, Qty: ${sale_item1.quantity}, Subtotal: ${sale_item1.subtotal}`);
|
||||
|
||||
// Create SaleItem 2 (using product2 from above)
|
||||
let sale_item2_product_id = product2.id; // Using product2.id after it's set // Using product2_id from product example
|
||||
let sale_item2_name = retrieved_product2.name;
|
||||
let sale_item2_qty = 1;
|
||||
let sale_item2_unit_price = retrieved_product2.price;
|
||||
let sale_item2_subtotal = sale_item2_qty * sale_item2_unit_price;
|
||||
|
||||
print(`\nCreating SaleItem 2 for Product ID: ${sale_item2_product_id}, Name: ${sale_item2_name}...`);
|
||||
let sale_item2 = new_sale_item(sale_item2_product_id, sale_item2_name, sale_item2_qty, sale_item2_unit_price, sale_item2_subtotal);
|
||||
print(`SaleItem 2: Product ID ${sale_item2.product_id}, Qty: ${sale_item2.quantity}, Subtotal: ${sale_item2.subtotal}`);
|
||||
|
||||
// Create a Sale
|
||||
|
||||
let sale1_customer_id = company1.id; // Example: company1 is the customer
|
||||
let sale1_date = 1673049600; // Example timestamp (Jan 7, 2023)
|
||||
let sale1_total_amount = sale_item1.subtotal + sale_item2.subtotal;
|
||||
|
||||
print(`\nCreating Sale 1 for Customer ID: ${sale1_customer_id}...`);
|
||||
let sale1 = new_sale(
|
||||
sale1_customer_id, // for company_id_i64 in Rhai registration
|
||||
"Temp Buyer Name", // for buyer_name in Rhai registration
|
||||
"temp@buyer.com", // for buyer_email in Rhai registration
|
||||
0.0, // for total_amount (will be overridden by builder)
|
||||
SaleStatusConstants::Pending, // for status (will be overridden by builder)
|
||||
0 // for sale_date (will be overridden by builder)
|
||||
)
|
||||
.customer_id(sale1_customer_id) // Actual field on Sale struct
|
||||
.status(SaleStatusConstants::Pending)
|
||||
.sale_date(sale1_date)
|
||||
.add_item(sale_item1) // Add item one by one
|
||||
.add_item(sale_item2)
|
||||
// Alternatively, to set all items at once (if items were already in an array):
|
||||
// .items([sale_item1, sale_item2])
|
||||
.total_amount(sale1_total_amount)
|
||||
.notes("First major sale of the year. Includes Advanced Gadget X and Cloud Backup Pro.")
|
||||
.set_base_created_at(sale1_date)
|
||||
.set_base_modified_at(sale1_date + 300) // 5 mins later
|
||||
.add_base_comment(1) // Example comment ID
|
||||
.add_base_comment(2);
|
||||
|
||||
print(`Sale 1 Created: ID ${sale1.id}, Customer ID: ${sale1.customer_id}, Status: ${sale1.status}, Total: ${sale1.total_amount}`);
|
||||
print(`Sale 1 Notes: ${sale1.notes}`);
|
||||
print(`Sale 1 Item Count: ${sale1.items.len()}`);
|
||||
if sale1.items.len() > 0 {
|
||||
print(`Sale 1 Item 1: ${sale1.items[0].name}, Qty: ${sale1.items[0].quantity}`);
|
||||
}
|
||||
if sale1.items.len() > 1 {
|
||||
print(`Sale 1 Item 2: ${sale1.items[1].name}, Qty: ${sale1.items[1].quantity}`);
|
||||
}
|
||||
print(`Sale 1 Base Comments: ${sale1.comments}`);
|
||||
|
||||
// Save Sale 1 to database
|
||||
print("\nSaving Sale 1 to database...");
|
||||
sale1 = set_sale(sale1);
|
||||
print("Sale 1 saved.");
|
||||
|
||||
// Retrieve Sale 1 from database
|
||||
print(`\nRetrieving Sale 1 by ID (${sale1.id})...`);
|
||||
let retrieved_sale1 = get_sale_by_id(sale1.id);
|
||||
print(`Retrieved Sale 1: ID ${retrieved_sale1.id}, Customer: ${retrieved_sale1.customer_id}, Status: ${retrieved_sale1.status}`);
|
||||
print(`Retrieved Sale 1 Total: ${retrieved_sale1.total_amount}, Notes: '${retrieved_sale1.notes}'`);
|
||||
if retrieved_sale1.items.len() > 0 {
|
||||
print(`Retrieved Sale 1 Item 1: ${retrieved_sale1.items[0].name} (Product ID: ${retrieved_sale1.items[0].product_id})`);
|
||||
}
|
||||
|
||||
print("\nBiz Rhai example script finished.");
|
@ -1,45 +0,0 @@
|
||||
use heromodels::db::hero::OurDB; // Corrected path for OurDB
|
||||
use heromodels::models::biz::register_biz_rhai_module; // Corrected path
|
||||
use rhai::{Engine, EvalAltResult, Scope};
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
println!("Executing Rhai script: examples/biz_rhai/biz.rhai");
|
||||
|
||||
// Create a new Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Create an Arc<Mutex<OurDB>> instance
|
||||
// For this example, we'll use an in-memory DB.
|
||||
// The actual DB path or configuration might come from elsewhere in a real app.
|
||||
let db_instance = Arc::new(OurDB::new(".", false).expect("Failed to create DB")); // Corrected OurDB::new args and removed Mutex
|
||||
|
||||
// Register the biz module with the engine
|
||||
register_biz_rhai_module(&mut engine, Arc::clone(&db_instance));
|
||||
|
||||
// Read the Rhai script from file
|
||||
let script_path = "examples/biz_rhai/biz.rhai";
|
||||
let script_content = fs::read_to_string(script_path).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
format!("Cannot read script file: {}", script_path),
|
||||
e.into(),
|
||||
))
|
||||
})?;
|
||||
|
||||
// Create a new scope
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Execute the script
|
||||
match engine.run_with_scope(&mut scope, &script_content) {
|
||||
Ok(_) => {
|
||||
println!("Rhai script executed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Rhai script execution failed: {}", e);
|
||||
println!("Details: {:?}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
// Get the database instance
|
||||
let db = get_db();
|
||||
|
||||
// Create a new calendar using the constructor and builder methods
|
||||
print("Creating a new calendar (ID will be DB-assigned) via registered constructor...");
|
||||
let calendar = new_calendar("My first calendar"). // ID removed
|
||||
name("My First Calendar").
|
||||
description("A calendar for testing Rhai integration");
|
||||
|
||||
let event = new_event(). // ID removed
|
||||
title("My First Event").
|
||||
description("An event for testing Rhai integration")
|
||||
.add_attendee(new_attendee(1)); // new_attendee(contact_id), not Attendee ID
|
||||
|
||||
// Add event's ID to calendar. event.id will be 0 if not saved separately.
|
||||
// Calendar::add_event returns the modified calendar, so we re-assign.
|
||||
calendar = calendar.add_event(event.id);
|
||||
|
||||
print("Type of calendar object: " + type_of(calendar));
|
||||
print("Created calendar: " + calendar.name);
|
||||
|
||||
// Save the calendar to the database and capture the result with DB-assigned ID
|
||||
let calendar = set_calendar(db, calendar); // Capture result
|
||||
print("Calendar saved to database");
|
||||
|
||||
// Check if calendar exists and retrieve it
|
||||
if calendar_exists(db, calendar.id) { // Use calendar.id from the saved object
|
||||
let retrieved_calendar = get_calendar_by_id(db, calendar.id); // Use calendar.id
|
||||
print("Retrieved calendar ID: " + retrieved_calendar.id + ", Name: " + retrieved_calendar.name);
|
||||
// Access the 'description' field directly.
|
||||
// Note: 'description' is Option<String>. Rhai handles options.
|
||||
// You might want to check for 'is_some()' or 'is_none()' or use 'unwrap_or()' pattern if needed.
|
||||
let desc = retrieved_calendar.description;
|
||||
if desc != () && desc != "" { // Check against '()' for None and empty string
|
||||
print("Description: " + desc);
|
||||
} else {
|
||||
print("No description available or it's None");
|
||||
}
|
||||
} else {
|
||||
print("Failed to retrieve calendar with ID " + calendar.id);
|
||||
}
|
||||
|
||||
// Create another calendar
|
||||
print("Creating another new calendar (ID will be DB-assigned) using builder methods...");
|
||||
let calendar2 = new_calendar(). // ID removed
|
||||
name("My Second Calendar").
|
||||
description("Another calendar for testing");
|
||||
|
||||
let calendar2 = set_calendar(db, calendar2); // Capture result
|
||||
print("Second calendar saved with ID: " + calendar2.id);
|
||||
|
||||
// Get all calendars
|
||||
let all_calendars = get_all_calendars(db);
|
||||
print("Total calendars: " + all_calendars.len());
|
||||
|
||||
for cal_item in all_calendars { // Renamed loop variable to avoid conflict if 'calendar' is still in scope
|
||||
// Access 'base_data.id' and 'name' fields directly
|
||||
print("Calendar ID: " + cal_item.base_data.id + ", Name: " + cal_item.name);
|
||||
}
|
||||
|
||||
// Delete a calendar
|
||||
delete_calendar_by_id(db, calendar.id); // Use ID from the first calendar object
|
||||
print("Attempted to delete calendar with ID " + calendar.id);
|
||||
|
||||
// Verify deletion
|
||||
if !calendar_exists(db, calendar.id) { // Use ID from the first calendar object
|
||||
print("Calendar with ID " + calendar.id + " was successfully deleted");
|
||||
} else {
|
||||
print("Failed to delete calendar with ID " + calendar.id + ", or it was not the one intended.");
|
||||
}
|
||||
|
||||
// Count remaining calendars
|
||||
let remaining_calendars = get_all_calendars(db);
|
||||
print("Remaining calendars: " + remaining_calendars.len());
|
@ -1,98 +0,0 @@
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::calendar::rhai::register_rhai_engine_functions;
|
||||
use heromodels::models::calendar::{AttendanceStatus, Attendee, Calendar, Event};
|
||||
use rhai::Engine;
|
||||
use rhai_wrapper::wrap_vec_return;
|
||||
use std::sync::Arc;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Initialize database with OurDB
|
||||
let db = Arc::new(OurDB::new("temp_calendar_db", true).expect("Failed to create database"));
|
||||
|
||||
// Register the Calendar type with Rhai
|
||||
// This function is generated by the #[rhai_model_export] attribute
|
||||
Calendar::register_rhai_bindings_for_calendar(&mut engine, db.clone());
|
||||
register_rhai_engine_functions(&mut engine, db.clone());
|
||||
|
||||
// Register a function to get the database instance
|
||||
engine.register_fn("get_db", move || db.clone());
|
||||
|
||||
// Register a calendar builder function
|
||||
engine.register_fn("calendar__builder", |id: i64| {
|
||||
let id_option = if id <= 0 { None } else { Some(id as u32) };
|
||||
Calendar::new(id_option, "New Calendar")
|
||||
});
|
||||
|
||||
// Register setter methods for Calendar properties
|
||||
engine.register_fn(
|
||||
"set_description",
|
||||
|calendar: &mut Calendar, desc: String| {
|
||||
calendar.description = Some(desc);
|
||||
},
|
||||
);
|
||||
|
||||
// Register getter methods for Calendar properties
|
||||
engine.register_fn("get_description", |calendar: Calendar| -> String {
|
||||
calendar.description.clone().unwrap_or_default()
|
||||
});
|
||||
|
||||
// Register getter for base_data.id
|
||||
engine.register_fn("get_id", |calendar: Calendar| -> i64 {
|
||||
calendar.base_data.id as i64
|
||||
});
|
||||
|
||||
// Register additional functions needed by the script
|
||||
engine.register_fn("set_calendar", |_db: Arc<OurDB>, _calendar: Calendar| {
|
||||
// In a real implementation, this would save the calendar to the database
|
||||
println!("Calendar saved: {}", _calendar.name);
|
||||
});
|
||||
|
||||
engine.register_fn(
|
||||
"get_calendar_by_id",
|
||||
|_db: Arc<OurDB>, id: i64| -> Calendar {
|
||||
// In a real implementation, this would retrieve the calendar from the database
|
||||
Calendar::new(Some(id as u32), "Retrieved Calendar")
|
||||
},
|
||||
);
|
||||
|
||||
// Register a function to check if a calendar exists
|
||||
engine.register_fn("calendar_exists", |_db: Arc<OurDB>, id: i64| -> bool {
|
||||
// In a real implementation, this would check if the calendar exists in the database
|
||||
id == 1 || id == 2
|
||||
});
|
||||
|
||||
// Define the function separately to use with the wrap_vec_return macro
|
||||
fn get_all_calendars(_db: Arc<OurDB>) -> Vec<Calendar> {
|
||||
// In a real implementation, this would retrieve all calendars from the database
|
||||
vec![
|
||||
Calendar::new(Some(1), "Calendar 1"),
|
||||
Calendar::new(Some(2), "Calendar 2"),
|
||||
]
|
||||
}
|
||||
|
||||
// Register the function with the wrap_vec_return macro
|
||||
engine.register_fn(
|
||||
"get_all_calendars",
|
||||
wrap_vec_return!(get_all_calendars, Arc<OurDB> => Calendar),
|
||||
);
|
||||
|
||||
engine.register_fn("delete_calendar_by_id", |_db: Arc<OurDB>, _id: i64| {
|
||||
// In a real implementation, this would delete the calendar from the database
|
||||
println!("Calendar deleted with ID: {}", _id);
|
||||
});
|
||||
|
||||
// Load and evaluate the Rhai script
|
||||
let script_path = Path::new("examples/calendar_rhai/calendar.rhai");
|
||||
let script = fs::read_to_string(script_path)?;
|
||||
|
||||
match engine.eval::<()>(&script) {
|
||||
Ok(_) => println!("Script executed successfully!"),
|
||||
Err(e) => eprintln!("Script execution failed: {}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
use rhai::{Engine, EvalAltResult, Scope};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
// Import the models and the registration function
|
||||
use heromodels::models::finance::account::Account;
|
||||
use heromodels::models::finance::asset::Asset;
|
||||
use heromodels::models::finance::marketplace::Listing;
|
||||
use heromodels::models::finance::rhai::register_rhai_engine_functions;
|
||||
|
||||
// Define a simple in-memory mock database for the example
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MockDb {
|
||||
pub accounts: Arc<Mutex<HashMap<u32, Account>>>,
|
||||
pub assets: Arc<Mutex<HashMap<u32, Asset>>>,
|
||||
pub listings: Arc<Mutex<HashMap<u32, Listing>>>,
|
||||
// Bids are often part of Listings, so a separate HashMap for Bids might not be needed
|
||||
// unless we want to query bids globally by a unique bid ID.
|
||||
}
|
||||
|
||||
impl MockDb {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
accounts: Arc::new(Mutex::new(HashMap::new())),
|
||||
assets: Arc::new(Mutex::new(HashMap::new())),
|
||||
listings: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
println!("--- Finance Rhai Example ---");
|
||||
|
||||
let mut engine = Engine::new();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
let mock_db = Arc::new(MockDb::new());
|
||||
|
||||
// Register finance functions and types with the engine
|
||||
register_rhai_engine_functions(
|
||||
&mut engine,
|
||||
Arc::clone(&mock_db.accounts),
|
||||
Arc::clone(&mock_db.assets),
|
||||
Arc::clone(&mock_db.listings),
|
||||
);
|
||||
println!("Rhai functions registered.");
|
||||
|
||||
scope.push("db_instance", mock_db.clone());
|
||||
|
||||
let script_path = "examples/finance_rhai/finance.rhai";
|
||||
println!("Loading script: {}", script_path);
|
||||
let script = match fs::read_to_string(script_path) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("Error reading script file '{}': {}", script_path, e);
|
||||
return Err(Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to read script".to_string(),
|
||||
Box::new(e),
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
println!("Executing script...");
|
||||
match engine.run_with_scope(&mut scope, &script) {
|
||||
Ok(_) => println!("Script executed successfully!"),
|
||||
Err(e) => {
|
||||
eprintln!("Script execution failed: {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Print final state of Accounts
|
||||
let final_accounts = mock_db.accounts.lock().unwrap();
|
||||
println!("\n--- Final Mock DB State (Accounts) ---");
|
||||
if final_accounts.is_empty() {
|
||||
println!("No accounts in mock DB.");
|
||||
}
|
||||
for (id, account) in final_accounts.iter() {
|
||||
println!(
|
||||
"Account ID: {}, Name: '{}', User ID: {}, Assets: {}",
|
||||
id,
|
||||
account.name,
|
||||
account.user_id,
|
||||
account.assets.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Print final state of Assets
|
||||
let final_assets = mock_db.assets.lock().unwrap();
|
||||
println!("\n--- Final Mock DB State (Assets) ---");
|
||||
if final_assets.is_empty() {
|
||||
println!("No assets in mock DB.");
|
||||
}
|
||||
for (id, asset) in final_assets.iter() {
|
||||
println!(
|
||||
"Asset ID: {}, Name: '{}', Amount: {}, Type: {:?}",
|
||||
id, asset.name, asset.amount, asset.asset_type
|
||||
);
|
||||
}
|
||||
|
||||
// Print final state of Listings
|
||||
let final_listings = mock_db.listings.lock().unwrap();
|
||||
println!("\n--- Final Mock DB State (Listings) ---");
|
||||
if final_listings.is_empty() {
|
||||
println!("No listings in mock DB.");
|
||||
}
|
||||
for (id, listing) in final_listings.iter() {
|
||||
println!(
|
||||
"Listing ID: {}, Title: '{}', Type: {:?}, Status: {:?}, Price: {}, Bids: {}",
|
||||
id,
|
||||
listing.title,
|
||||
listing.listing_type,
|
||||
listing.status,
|
||||
listing.price,
|
||||
listing.bids.len()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
// Finance Rhai Script Example
|
||||
|
||||
print("--- Starting Finance Rhai Script ---");
|
||||
|
||||
// 1. Create an Account using the builder pattern
|
||||
let user1_id = 1; // Assuming this user_id is a separate concept, e.g. from an auth system
|
||||
let acc1 = new_account()
|
||||
.set_name("User1 Main Account")
|
||||
.set_user_id(user1_id) // user_id is i64 in Rhai, u32 in Rust. Conversion handled by setter.
|
||||
.set_description("Primary account for User 1")
|
||||
.set_ledger("LedgerX")
|
||||
.set_address("0x123MainSt")
|
||||
.set_pubkey("pubkeyUser1");
|
||||
print(`Created account (pre-save): ${acc1.name} with temp ID ${acc1.id}`);
|
||||
|
||||
// 2. Save Account to Mock DB and get the version with DB-assigned ID
|
||||
let acc1 = set_account(acc1); // Shadowing acc1 with the returned instance
|
||||
print(`Account ${acc1.name} saved to DB with ID ${acc1.id}.`);
|
||||
|
||||
// 3. Retrieve Account from Mock DB (using the new ID)
|
||||
let fetched_acc1 = get_account_by_id(acc1.id); // Use the ID from the saved acc1
|
||||
print(`Fetched account from DB: ${fetched_acc1.name}, User ID: ${fetched_acc1.user_id}`);
|
||||
|
||||
// 4. Create an Asset using the builder pattern
|
||||
let asset1 = new_asset()
|
||||
.set_name("HeroCoin")
|
||||
.set_description("Utility token for Hero Platform")
|
||||
.set_amount(1000.0)
|
||||
.set_address("0xTokenContract")
|
||||
.set_asset_type("Erc20") // Setter handles string to enum
|
||||
.set_decimals(18);
|
||||
print(`Created asset (pre-save): ${asset1.name} (temp ID: ${asset1.id}), Amount: ${asset1.amount}, Type: ${asset1.asset_type_str}`);
|
||||
|
||||
// 5. Save Asset to Mock DB and get the version with DB-assigned ID
|
||||
let asset1 = set_asset(asset1); // Shadowing asset1
|
||||
print(`Asset ${asset1.name} (ID: ${asset1.id}) saved to DB.`);
|
||||
|
||||
// 6. Retrieve Asset from Mock DB (using the new ID)
|
||||
let fetched_asset1 = get_asset_by_id(asset1.id); // Use the ID from the saved asset1
|
||||
print(`Fetched asset from DB: ${fetched_asset1.name}, Address: ${fetched_asset1.address}`);
|
||||
|
||||
// 7. Add Asset to Account
|
||||
// We have 'acc1' and 'asset1' from previous steps, both saved to DB and have their IDs.
|
||||
print(`Attempting to add asset ${asset1.id} to account ${acc1.id}`);
|
||||
|
||||
// Fetch the latest version of the account before modifying
|
||||
// let mut acc1_for_update = get_account_by_id(acc1.get_id());
|
||||
// // Fetch the asset to add (or use fetched_asset1 if it's the correct one)
|
||||
// let asset_to_add = get_asset_by_id(asset1.get_id());
|
||||
//
|
||||
// try {
|
||||
// acc1_for_update = acc1_for_update.add_asset(asset_to_add); // add_asset returns the modified account
|
||||
// acc1_for_update = set_account(acc1_for_update); // Save the account with the new asset
|
||||
// print(`Asset '${asset_to_add.name}' added to account '${acc1_for_update.name}'.`);
|
||||
// print(`Account now has ${acc1_for_update.get_assets_cloned().len()} assets.`);
|
||||
// // Verify the asset is there
|
||||
// if (acc1_for_update.get_assets_cloned().len() > 0) {
|
||||
// let first_asset_in_account = acc1_for_update.get_assets_cloned()[0];
|
||||
// print(`First asset in account: ${first_asset_in_account.name} (ID: ${first_asset_in_account.id})`);
|
||||
// }
|
||||
// } catch (err) {
|
||||
// print(`Error adding asset to account: ${err}`);
|
||||
// }
|
||||
|
||||
// 8. Create a Listing for the Asset using the builder pattern
|
||||
let current_timestamp = timestamp(); // Rhai's built-in for current unix timestamp (seconds)
|
||||
let expires_at_ts = current_timestamp + (24 * 60 * 60 * 7); // Expires in 7 days
|
||||
|
||||
let listing1 = new_listing()
|
||||
.set_title("Rare HeroCoin Batch")
|
||||
.set_description("100 HeroCoins for sale")
|
||||
.set_asset_id(asset1.id.to_string()) // Use ID from the saved asset1
|
||||
.set_asset_type_str("Erc20") // asset_type as string
|
||||
.set_seller_id(user1_id.to_string()) // seller_id as string (using the predefined user1_id)
|
||||
.set_price(50.0) // price
|
||||
.set_currency("USD") // currency
|
||||
.set_listing_type("FixedPrice") // listing_type as string
|
||||
.set_tags(["token", "herocoin", "sale"]); // tags as array of strings
|
||||
// image_url is None by default from new_listing(), so no need to call set_image_url_opt for None
|
||||
|
||||
print(`Created listing (pre-save): ${listing1.title} (temp ID: ${listing1.id}), Price: ${listing1.price} ${listing1.currency}`);
|
||||
print(`Listing type: ${listing1.listing_type}, Status: ${listing1.status}`);
|
||||
print(`Listing expires_at_ts_opt: ${listing1.expires_at_ts_opt}`);
|
||||
|
||||
// 9. Save Listing to Mock DB and get the version with DB-assigned ID
|
||||
let listing1 = set_listing(listing1); // Shadowing listing1
|
||||
print(`Listing ${listing1.get_title()} (ID: ${listing1.get_id()}) saved to DB.`);
|
||||
|
||||
// 10. Retrieve Listing from Mock DB (using the new ID)
|
||||
let fetched_listing1 = get_listing_by_id(listing1.get_id()); // Use the ID from the saved listing1
|
||||
print(`Fetched listing from DB: ${fetched_listing1.get_title()}, Seller ID: ${fetched_listing1.get_seller_id()}`);
|
||||
print(`Fetched listing asset_id: ${fetched_listing1.get_asset_id()}, asset_type: ${fetched_listing1.get_asset_type_str()}`);
|
||||
|
||||
// 11. Demonstrate an auction listing using the builder pattern
|
||||
let auction_listing = new_listing()
|
||||
.set_title("Vintage Hero Figurine")
|
||||
.set_description("Rare collectible, starting bid low!")
|
||||
.set_asset_id("asset_nft_123") // Mock asset ID for an NFT - this asset isn't created/saved in script
|
||||
.set_asset_type("Erc721")
|
||||
.set_seller_id(user1_id.to_string()) // Using the predefined user1_id
|
||||
.set_price(10.0) // Starting price
|
||||
.set_currency("USD")
|
||||
.set_listing_type("Auction")
|
||||
// expires_at_ts_opt is None by default
|
||||
.set_tags(["collectible", "rare", "auction"])
|
||||
.set_image_url_opt("http://example.com/figurine.png");
|
||||
|
||||
// Save Auction Listing to Mock DB and get the version with DB-assigned ID
|
||||
let auction_listing = set_listing(auction_listing); // Shadowing auction_listing
|
||||
print(`Created auction listing: ${auction_listing.get_title()} (ID: ${auction_listing.get_id()})`);
|
||||
|
||||
// 12. Create a Bid for the auction listing (Bid model not using builder pattern in this refactor)
|
||||
let bid1 = new_bid(auction_listing.get_id().to_string(), 2, 12.0, "USD"); // User 2 bids 12 USD
|
||||
print(`Created bid for listing ${bid1.listing_id} by bidder ${bid1.bidder_id} for ${bid1.amount} ${bid1.currency}`);
|
||||
print(`Bid status: ${bid1.status_str}, Created at: ${bid1.created_at_ts}`);
|
||||
|
||||
// 13. Add bid to listing
|
||||
let auction_listing_for_bid = get_listing_by_id(auction_listing.get_id());
|
||||
// print(`Listing '${auction_listing_for_bid.get_title()}' fetched for bidding. Current price: ${auction_listing_for_bid.get_price()}, Bids: ${auction_listing_for_bid.get_bids_cloned().len()}`);
|
||||
|
||||
try {
|
||||
let updated_listing_after_bid = auction_listing_for_bid.add_listing_bid(bid1);
|
||||
print(`Bid added to '${updated_listing_after_bid.get_title()}'. New bid count: ${updated_listing_after_bid.get_bids_cloned().len()}, New price: ${updated_listing_after_bid.get_price()};`);
|
||||
set_listing(updated_listing_after_bid); // Save updated listing to DB
|
||||
print("Auction listing with new bid saved to DB;");
|
||||
} catch (err) {
|
||||
print(`Error adding bid: ${err}`);
|
||||
}
|
||||
|
||||
// 14. Try to complete sale for the fixed price listing
|
||||
let listing_to_sell = get_listing_by_id(listing1.id);
|
||||
let buyer_user_id = 3;
|
||||
print(`Attempting to complete sale for listing: ${listing_to_sell.title} by buyer ${buyer_user_id}`);
|
||||
try {
|
||||
let sold_listing = listing_to_sell.complete_listing_sale(buyer_user_id.to_string(), listing_to_sell.price);
|
||||
print(`Sale completed for listing ${sold_listing.id}. New status: ${sold_listing.status}`);
|
||||
print(`Buyer ID: ${sold_listing.buyer_id_opt}, Sale Price: ${sold_listing.sale_price_opt}`);
|
||||
set_listing(sold_listing); // Save updated listing
|
||||
} catch (err) {
|
||||
print(`Error completing sale: ${err}`);
|
||||
}
|
||||
|
||||
print("--- Finance Rhai Script Finished ---");
|
@ -1,41 +0,0 @@
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::flow::register_flow_rhai_module;
|
||||
use rhai::Engine;
|
||||
use std::sync::Arc;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Initialize database with OurDB
|
||||
// Using a temporary/in-memory database for the example
|
||||
let db = Arc::new(OurDB::new("temp_flow_rhai_db", true).expect("Failed to create database"));
|
||||
|
||||
// Register flow Rhai module functions
|
||||
register_flow_rhai_module(&mut engine, db.clone());
|
||||
|
||||
// Load and evaluate the Rhai script
|
||||
let script_path_str = "examples/flow_rhai/flow.rhai";
|
||||
let script_path = Path::new(script_path_str);
|
||||
if !script_path.exists() {
|
||||
eprintln!("Error: Rhai script not found at {}", script_path_str);
|
||||
eprintln!(
|
||||
"Please ensure the script 'flow.rhai' exists in the 'examples/flow_rhai/' directory."
|
||||
);
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
format!("Rhai script not found: {}", script_path_str),
|
||||
)));
|
||||
}
|
||||
|
||||
println!("Executing Rhai script: {}", script_path_str);
|
||||
let script = fs::read_to_string(script_path)?;
|
||||
|
||||
match engine.eval::<()>(&script) {
|
||||
Ok(_) => println!("\nRhai script executed successfully!"),
|
||||
Err(e) => eprintln!("\nRhai script execution failed: {}\nDetails: {:#?}", e, e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
// Hero Models - Flow Rhai Example
|
||||
print("Hero Models - Flow Rhai Example");
|
||||
print("=============================");
|
||||
|
||||
// Helper to format Option<String> (Dynamic in Rhai: String or ()) for printing
|
||||
fn format_optional(val, placeholder) {
|
||||
if val == () {
|
||||
placeholder
|
||||
} else {
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
// The database instance is now implicitly passed to DB functions.
|
||||
print("DB instance will be implicitly passed.");
|
||||
|
||||
// --- Test Flow Model ---
|
||||
print("\n--- Testing Flow Model ---");
|
||||
// Create a new flow using the constructor and builder methods
|
||||
print("Creating a new flow (ID: 1, UUID: flow-uuid-001)...");
|
||||
let flow1 = new_flow(1, "flow-uuid-001")
|
||||
.name("Document Approval Workflow")
|
||||
.status("Active");
|
||||
|
||||
print("Flow object created: " + flow1);
|
||||
print("Flow ID: " + flow1.id);
|
||||
print("Flow UUID: " + flow1.flow_uuid);
|
||||
print("Flow Name: " + flow1.name);
|
||||
print("Flow Status: " + flow1.status);
|
||||
|
||||
// Save the flow to the database
|
||||
set_flow(flow1);
|
||||
print("Flow saved to database.");
|
||||
|
||||
// Retrieve the flow
|
||||
let retrieved_flow = get_flow_by_id(1);
|
||||
print("Retrieved Flow by ID (1): " + retrieved_flow.name + ", Status: " + retrieved_flow.status);
|
||||
|
||||
// --- Test FlowStep Model (as part of Flow) ---
|
||||
print("\n--- Testing FlowStep Model (as part of Flow) ---");
|
||||
// Create FlowSteps
|
||||
print("Creating flow steps and adding to flow...");
|
||||
let step1 = new_flow_step(101, 1) // id, step_order
|
||||
.description("Initial Review by Manager")
|
||||
.status("Pending");
|
||||
|
||||
let step2 = new_flow_step(102, 2) // id, step_order. Note: FlowStep ID 102 will be used for sig_req1 & sig_req2
|
||||
.description("Legal Team Sign-off")
|
||||
.status("Pending");
|
||||
|
||||
// Add steps to the flow created earlier
|
||||
flow1 = flow1.add_step(step1);
|
||||
flow1 = flow1.add_step(step2);
|
||||
|
||||
print("Flow now has " + flow1.steps.len() + " steps.");
|
||||
print("First step description: " + format_optional(flow1.steps[0].description, "[No Description]"));
|
||||
|
||||
// Re-save the flow with its steps
|
||||
set_flow(flow1);
|
||||
print("Flow with steps saved to database.");
|
||||
|
||||
// Retrieve the flow and check its steps
|
||||
let retrieved_flow_with_steps = get_flow_by_id(1);
|
||||
print("Retrieved Flow by ID (1) has " + retrieved_flow_with_steps.steps.len() + " step(s).");
|
||||
if retrieved_flow_with_steps.steps.len() > 0 {
|
||||
print("First step of retrieved flow: " + format_optional(retrieved_flow_with_steps.steps[0].description, "[No Description]"));
|
||||
}
|
||||
|
||||
// --- Test SignatureRequirement Model ---
|
||||
print("\n--- Testing SignatureRequirement Model ---");
|
||||
// Create SignatureRequirements (referencing FlowStep ID 102, which is step2)
|
||||
print("Creating signature requirements for step with ID 102...");
|
||||
let sig_req1 = new_signature_requirement(201, 102, "pubkey_legal_lead", "Legal Lead: Approve terms.")
|
||||
.status("Required");
|
||||
|
||||
let sig_req2 = new_signature_requirement(202, 102, "pubkey_general_counsel", "General Counsel: Final Approval.")
|
||||
.status("Required"); // signed_by and signature will default to None (Rust) / () (Rhai)
|
||||
|
||||
print("SigReq 1: " + sig_req1.message + " for PubKey: " + sig_req1.public_key + " (Status: " + sig_req1.status + ")");
|
||||
if sig_req2.signed_by == () {
|
||||
print("SigReq 2: " + sig_req2.message + " for PubKey: " + sig_req2.public_key + " (Status: " + sig_req2.status + ", Not signed yet)");
|
||||
} else {
|
||||
print("SigReq 2: " + sig_req2.message + " for PubKey: " + sig_req2.public_key + " (Status: " + sig_req2.status + ", Signed by: " + format_optional(sig_req2.signed_by, "[Not Signed Yet]") + ")");
|
||||
}
|
||||
|
||||
|
||||
// Save signature requirements
|
||||
set_signature_requirement(sig_req1);
|
||||
set_signature_requirement(sig_req2);
|
||||
print("SignatureRequirements saved to database.");
|
||||
|
||||
// Retrieve a signature requirement
|
||||
let retrieved_sig_req = get_signature_requirement_by_id(201);
|
||||
print("Retrieved SignatureRequirement by ID (201): " + retrieved_sig_req.message);
|
||||
|
||||
// --- Test updating a SignatureRequirement ---
|
||||
print("\n--- Testing Update for SignatureRequirement ---");
|
||||
let updated_sig_req = retrieved_sig_req
|
||||
.status("Signed")
|
||||
.signed_by("pubkey_legal_lead_actual_signer_id")
|
||||
.signature("base64_encoded_signature_data_here");
|
||||
|
||||
print("Updated SigReq 1 - Status: " + updated_sig_req.status + ", Signed By: " + format_optional(updated_sig_req.signed_by, "[Not Signed Yet]") + ", Signature: " + format_optional(updated_sig_req.signature, "[No Signature]"));
|
||||
set_signature_requirement(updated_sig_req); // Save updated
|
||||
print("Updated SignatureRequirement saved.");
|
||||
|
||||
print("\nFlow Rhai example script finished.");
|
@ -1,222 +0,0 @@
|
||||
// Governance Example - Demonstrates using the governance models with embedded types
|
||||
// This example shows how to create, retrieve, and manipulate governance entities with embedded types
|
||||
|
||||
// Get the database instance
|
||||
let db = get_db();
|
||||
println("Hero Models - Governance Example");
|
||||
println("================================");
|
||||
|
||||
// Create a company with a business type using the fluent builder pattern
|
||||
println("Creating a company...");
|
||||
// Create a business type directly
|
||||
let business_type = #{
|
||||
type_name: "Corporation",
|
||||
description: "A corporation is a legal entity that is separate and distinct from its owners"
|
||||
};
|
||||
|
||||
let company = company__builder(1)
|
||||
.name("Acme Corporation")
|
||||
.registration_number("REG123456")
|
||||
.incorporation_date(now())
|
||||
.fiscal_year_end("December 31")
|
||||
.email("info@acme.com")
|
||||
.phone("+1 555-123-4567")
|
||||
.website("https://acme.com")
|
||||
.address("123 Main St, Anytown, USA")
|
||||
.business_type(business_type)
|
||||
.industry("Technology")
|
||||
.description("A leading technology company")
|
||||
.status("Active")
|
||||
.build();
|
||||
|
||||
// Save the company to the database
|
||||
set_company(db, company);
|
||||
println("Company created successfully!");
|
||||
println("");
|
||||
|
||||
// Create a committee with members using the fluent builder pattern
|
||||
println("Creating a committee...");
|
||||
let committee = committee__builder(1)
|
||||
.company_id(1)
|
||||
.name("Board of Directors")
|
||||
.description("The main governing body of the company")
|
||||
.add_member(
|
||||
committee_member()
|
||||
.id(1)
|
||||
.user_id(101)
|
||||
.name("John Smith")
|
||||
.role(CommitteeRole::Chair)
|
||||
)
|
||||
.add_member(
|
||||
committee_member()
|
||||
.id(2)
|
||||
.user_id(102)
|
||||
.name("Jane Doe")
|
||||
.role(CommitteeRole::Secretary)
|
||||
)
|
||||
.add_member(
|
||||
committee_member()
|
||||
.id(3)
|
||||
.user_id(103)
|
||||
.name("Bob Johnson")
|
||||
.role(CommitteeRole::Member)
|
||||
)
|
||||
.build();
|
||||
|
||||
// Save the committee to the database
|
||||
set_committee(db, committee);
|
||||
println("Committee created successfully!");
|
||||
println("");
|
||||
|
||||
// Create a meeting with attendees using the fluent builder pattern
|
||||
println("Creating a meeting...");
|
||||
let meeting = meeting__builder(1)
|
||||
.company_id(1)
|
||||
.title("Q2 Board Meeting")
|
||||
.date(now() + days(7))
|
||||
.location("Conference Room A")
|
||||
.description("Quarterly board meeting to review financial performance")
|
||||
.add_attendee(
|
||||
attendee()
|
||||
.id(1)
|
||||
.user_id(101)
|
||||
.name("John Smith")
|
||||
.role(AttendeeRole::Coordinator)
|
||||
.status(AttendeeStatus::Confirmed)
|
||||
)
|
||||
.add_attendee(
|
||||
attendee()
|
||||
.id(2)
|
||||
.user_id(102)
|
||||
.name("Jane Doe")
|
||||
.role(AttendeeRole::Secretary)
|
||||
.status(AttendeeStatus::Confirmed)
|
||||
)
|
||||
.add_attendee(
|
||||
attendee()
|
||||
.id(3)
|
||||
.user_id(103)
|
||||
.name("Bob Johnson")
|
||||
.role(AttendeeRole::Member)
|
||||
.status(AttendeeStatus::Pending)
|
||||
)
|
||||
.build();
|
||||
|
||||
// Save the meeting to the database
|
||||
set_meeting(db, meeting);
|
||||
println("Meeting created successfully!");
|
||||
println("");
|
||||
|
||||
// Create a resolution with approvals using the fluent builder pattern
|
||||
println("Creating a resolution...");
|
||||
let resolution = resolution__builder(1)
|
||||
.company_id(1)
|
||||
.title("New Product Line Resolution")
|
||||
.description("Resolution to approve the development of a new product line")
|
||||
.resolution_type(ResolutionType::Ordinary)
|
||||
.status(ResolutionStatus::Proposed)
|
||||
.proposed_date(now())
|
||||
.effective_date(now() + days(30))
|
||||
.expiry_date(now() + days(365))
|
||||
.add_approval("John Smith")
|
||||
.add_approval("Jane Doe")
|
||||
.build();
|
||||
|
||||
// Save the resolution to the database
|
||||
set_resolution(db, resolution);
|
||||
println("Resolution created successfully!");
|
||||
println("");
|
||||
|
||||
// Create a vote with ballots using the fluent builder pattern
|
||||
println("Creating a vote...");
|
||||
let vote = vote__builder(1)
|
||||
.company_id(1)
|
||||
.resolution_id(1)
|
||||
.title("Vote on New Product Line")
|
||||
.description("Vote to approve the development of a new product line")
|
||||
.status(VoteStatus::Open)
|
||||
.add_ballot(
|
||||
ballot()
|
||||
.id(1)
|
||||
.user_id(101)
|
||||
.user_name("John Smith")
|
||||
.option(VoteOption::Yes)
|
||||
.comments("Strongly support this initiative")
|
||||
)
|
||||
.add_ballot(
|
||||
ballot()
|
||||
.id(2)
|
||||
.user_id(102)
|
||||
.user_name("Jane Doe")
|
||||
.option(VoteOption::Yes)
|
||||
.comments("Agree with the proposal")
|
||||
)
|
||||
.add_ballot(
|
||||
ballot()
|
||||
.id(3)
|
||||
.user_id(103)
|
||||
.user_name("Bob Johnson")
|
||||
.option(VoteOption::Abstain)
|
||||
.comments("Need more information")
|
||||
)
|
||||
.start_date(now())
|
||||
.end_date(now() + days(7))
|
||||
.build();
|
||||
|
||||
// Save the vote to the database
|
||||
set_vote(db, vote);
|
||||
println("Vote created successfully!");
|
||||
println("");
|
||||
|
||||
// Retrieve and display the company
|
||||
println("Retrieving company...");
|
||||
let retrieved_company = get_company_by_id(db, 1);
|
||||
println("Company: " + retrieved_company.name);
|
||||
println("Business Type: " + retrieved_company.business_type.type_name);
|
||||
println("");
|
||||
|
||||
// Retrieve and display the committee
|
||||
println("Retrieving committee...");
|
||||
let retrieved_committee = get_committee_by_id(db, 1);
|
||||
println("Committee: " + retrieved_committee.name);
|
||||
println("Members: " + retrieved_committee.members.len());
|
||||
for member in retrieved_committee.members {
|
||||
println("- " + member.name + " (" + member.role + ")");
|
||||
}
|
||||
println("");
|
||||
|
||||
// Retrieve and display the meeting
|
||||
println("Retrieving meeting...");
|
||||
let retrieved_meeting = get_meeting_by_id(db, 1);
|
||||
println("Meeting: " + retrieved_meeting.title);
|
||||
println("Date: " + retrieved_meeting.date);
|
||||
println("Attendees: " + retrieved_meeting.attendees.len());
|
||||
for attendee in retrieved_meeting.attendees {
|
||||
println("- " + attendee.name + " (" + attendee.role + ", " + attendee.status + ")");
|
||||
}
|
||||
println("");
|
||||
|
||||
// Retrieve and display the resolution
|
||||
println("Retrieving resolution...");
|
||||
let retrieved_resolution = get_resolution_by_id(db, 1);
|
||||
println("Resolution: " + retrieved_resolution.title);
|
||||
println("Status: " + retrieved_resolution.status);
|
||||
println("Approvals: " + retrieved_resolution.approvals.len());
|
||||
for approval in retrieved_resolution.approvals {
|
||||
println("- " + approval);
|
||||
}
|
||||
println("");
|
||||
|
||||
// Retrieve and display the vote
|
||||
println("Retrieving vote...");
|
||||
let retrieved_vote = get_vote_by_id(db, 1);
|
||||
println("Vote: " + retrieved_vote.title);
|
||||
println("Status: " + retrieved_vote.status);
|
||||
println("Ballots: " + retrieved_vote.ballots.len());
|
||||
for ballot in retrieved_vote.ballots {
|
||||
println("- " + ballot.user_name + ": " + ballot.option);
|
||||
}
|
||||
println("");
|
||||
|
||||
println("Governance example completed successfully!");
|
||||
()
|
@ -1,57 +0,0 @@
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::register_db_functions;
|
||||
use rhai::Engine;
|
||||
use std::sync::Arc;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use chrono::{DateTime, Utc, Duration};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create a temporary directory for the database
|
||||
let temp_dir = std::env::temp_dir().join("heromodels_gov_example");
|
||||
if temp_dir.exists() {
|
||||
fs::remove_dir_all(&temp_dir)?;
|
||||
}
|
||||
fs::create_dir_all(&temp_dir)?;
|
||||
|
||||
println!("Using temporary database at: {}", temp_dir.display());
|
||||
|
||||
// Create a new OurDB instance
|
||||
let db = OurDB::new(&temp_dir, true)?;
|
||||
let db = Arc::new(db);
|
||||
|
||||
// Create a new Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Register the auto-generated DB functions with the engine
|
||||
register_db_functions(&mut engine);
|
||||
|
||||
// Register print functions
|
||||
engine.register_fn("println", |s: &str| println!("{}", s));
|
||||
engine.register_fn("print", |s: &str| print!("{}", s));
|
||||
|
||||
// Register date/time functions
|
||||
engine.register_fn("now", || Utc::now());
|
||||
engine.register_fn("days", |n: i64| Duration::days(n));
|
||||
engine.register_fn("+", |dt: DateTime<Utc>, duration: Duration| dt + duration);
|
||||
|
||||
// Add the DB instance to the engine's scope
|
||||
engine.register_fn("get_db", move || -> Arc<OurDB> { db.clone() });
|
||||
|
||||
// Load and execute the Rhai script
|
||||
let script_path = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("examples")
|
||||
.join("gov_example.rhai");
|
||||
|
||||
println!("Loading Rhai script from: {}", script_path.display());
|
||||
|
||||
match engine.eval_file::<()>(script_path) {
|
||||
Ok(_) => println!("Script executed successfully"),
|
||||
Err(e) => eprintln!("Error executing script: {}", e),
|
||||
}
|
||||
|
||||
// Clean up the temporary directory
|
||||
fs::remove_dir_all(&temp_dir)?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,290 +0,0 @@
|
||||
use chrono::{Duration, Utc};
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::governance::{
|
||||
Ballot, Proposal, ProposalStatus, VoteEventStatus, VoteOption,
|
||||
};
|
||||
use rhai::Engine;
|
||||
use rhai_wrapper::wrap_vec_return;
|
||||
use std::sync::Arc;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Initialize database
|
||||
let db = Arc::new(OurDB::new("temp_governance_db", true).expect("Failed to create database"));
|
||||
|
||||
// Register the Proposal type with Rhai
|
||||
// This function is generated by the #[rhai_model_export] attribute
|
||||
Proposal::register_rhai_bindings_for_proposal(&mut engine, db.clone());
|
||||
|
||||
// Register the Ballot type with Rhai
|
||||
Ballot::register_rhai_bindings_for_ballot(&mut engine, db.clone());
|
||||
|
||||
// Register a function to get the database instance
|
||||
engine.register_fn("get_db", move || db.clone());
|
||||
|
||||
// Register builder functions for Proposal and related types
|
||||
engine.register_fn(
|
||||
"create_proposal",
|
||||
|id: i64, creator_id: String, creator_name: String, title: String, description: String| {
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
let id_option = if id <= 0 { None } else { Some(id as u32) };
|
||||
Proposal::new(
|
||||
id_option,
|
||||
creator_id,
|
||||
creator_name,
|
||||
title,
|
||||
description,
|
||||
ProposalStatus::Draft,
|
||||
Utc::now(),
|
||||
Utc::now(),
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("create_vote_option", |id: i64, text: String| {
|
||||
VoteOption::new(id as u8, text, Some("This is an optional comment"))
|
||||
});
|
||||
|
||||
engine.register_fn(
|
||||
"create_ballot",
|
||||
|id: i64, user_id: i64, vote_option_id: i64, shares_count: i64| {
|
||||
let id_option = if id <= 0 { None } else { Some(id as u32) };
|
||||
Ballot::new(
|
||||
id_option,
|
||||
user_id as u32,
|
||||
vote_option_id as u8,
|
||||
shares_count,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
// Register getter and setter methods for Proposal properties
|
||||
engine.register_fn("get_title", |proposal: Proposal| -> String {
|
||||
proposal.title.clone()
|
||||
});
|
||||
|
||||
engine.register_fn("get_description", |proposal: Proposal| -> String {
|
||||
proposal.description.clone()
|
||||
});
|
||||
|
||||
engine.register_fn("get_creator_id", |proposal: Proposal| -> String {
|
||||
proposal.creator_id.clone()
|
||||
});
|
||||
|
||||
engine.register_fn("get_id", |proposal: Proposal| -> i64 {
|
||||
proposal.base_data.id as i64
|
||||
});
|
||||
|
||||
engine.register_fn("get_status", |proposal: Proposal| -> String {
|
||||
format!("{:?}", proposal.status)
|
||||
});
|
||||
|
||||
engine.register_fn("get_vote_status", |proposal: Proposal| -> String {
|
||||
format!("{:?}", proposal.vote_status)
|
||||
});
|
||||
|
||||
// Register methods for proposal operations
|
||||
engine.register_fn(
|
||||
"add_option_to_proposal",
|
||||
|mut proposal: Proposal, option_id: i64, option_text: String| -> Proposal {
|
||||
proposal.add_option(
|
||||
option_id as u8,
|
||||
option_text,
|
||||
Some("This is an optional comment".to_string()),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"cast_vote_on_proposal",
|
||||
|mut proposal: Proposal,
|
||||
ballot_id: i64,
|
||||
user_id: i64,
|
||||
option_id: i64,
|
||||
shares: i64|
|
||||
-> Proposal {
|
||||
let ballot_id_option = if ballot_id <= 0 {
|
||||
None
|
||||
} else {
|
||||
Some(ballot_id as u32)
|
||||
};
|
||||
proposal.cast_vote(ballot_id_option, user_id as u32, option_id as u8, shares)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"change_proposal_status",
|
||||
|mut proposal: Proposal, status_str: String| -> Proposal {
|
||||
let new_status = match status_str.as_str() {
|
||||
"Draft" => ProposalStatus::Draft,
|
||||
"Active" => ProposalStatus::Active,
|
||||
"Approved" => ProposalStatus::Approved,
|
||||
"Rejected" => ProposalStatus::Rejected,
|
||||
"Cancelled" => ProposalStatus::Cancelled,
|
||||
_ => ProposalStatus::Draft,
|
||||
};
|
||||
proposal.change_proposal_status(new_status)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"change_vote_event_status",
|
||||
|mut proposal: Proposal, status_str: String| -> Proposal {
|
||||
let new_status = match status_str.as_str() {
|
||||
"Open" => VoteEventStatus::Open,
|
||||
"Closed" => VoteEventStatus::Closed,
|
||||
"Cancelled" => VoteEventStatus::Cancelled,
|
||||
_ => VoteEventStatus::Open,
|
||||
};
|
||||
proposal.change_vote_event_status(new_status)
|
||||
},
|
||||
);
|
||||
|
||||
// Register functions for database operations
|
||||
engine.register_fn("save_proposal", |_db: Arc<OurDB>, proposal: Proposal| {
|
||||
println!("Proposal saved: {}", proposal.title);
|
||||
});
|
||||
|
||||
engine.register_fn(
|
||||
"get_proposal_by_id",
|
||||
|_db: Arc<OurDB>, id: i64| -> Proposal {
|
||||
// In a real implementation, this would retrieve the proposal from the database
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
Proposal::new(
|
||||
Some(id as u32),
|
||||
"Retrieved Creator",
|
||||
"Retrieved Creator Name",
|
||||
"Retrieved Proposal",
|
||||
"Retrieved Description",
|
||||
ProposalStatus::Draft,
|
||||
Utc::now(),
|
||||
Utc::now(),
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
// Register a function to check if a proposal exists
|
||||
engine.register_fn("proposal_exists", |_db: Arc<OurDB>, id: i64| -> bool {
|
||||
// In a real implementation, this would check if the proposal exists in the database
|
||||
id == 1 || id == 2
|
||||
});
|
||||
|
||||
// Define the function for get_all_proposals
|
||||
fn get_all_proposals(_db: Arc<OurDB>) -> Vec<Proposal> {
|
||||
// In a real implementation, this would retrieve all proposals from the database
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
vec![
|
||||
Proposal::new(
|
||||
Some(1),
|
||||
"Creator 1",
|
||||
"Creator Name 1",
|
||||
"Proposal 1",
|
||||
"Description 1",
|
||||
ProposalStatus::Draft,
|
||||
Utc::now(),
|
||||
Utc::now(),
|
||||
start_date,
|
||||
end_date,
|
||||
),
|
||||
Proposal::new(
|
||||
Some(2),
|
||||
"Creator 2",
|
||||
"Creator Name 2",
|
||||
"Proposal 2",
|
||||
"Description 2",
|
||||
ProposalStatus::Draft,
|
||||
Utc::now(),
|
||||
Utc::now(),
|
||||
start_date,
|
||||
end_date,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
// Register the function with the wrap_vec_return macro
|
||||
engine.register_fn(
|
||||
"get_all_proposals",
|
||||
wrap_vec_return!(get_all_proposals, Arc<OurDB> => Proposal),
|
||||
);
|
||||
|
||||
engine.register_fn("delete_proposal_by_id", |_db: Arc<OurDB>, _id: i64| {
|
||||
// In a real implementation, this would delete the proposal from the database
|
||||
println!("Proposal deleted with ID: {}", _id);
|
||||
});
|
||||
|
||||
// Register helper functions for accessing proposal options and ballots
|
||||
engine.register_fn("get_option_count", |proposal: Proposal| -> i64 {
|
||||
proposal.options.len() as i64
|
||||
});
|
||||
|
||||
engine.register_fn(
|
||||
"get_option_at",
|
||||
|proposal: Proposal, index: i64| -> VoteOption {
|
||||
if index >= 0 && index < proposal.options.len() as i64 {
|
||||
proposal.options[index as usize].clone()
|
||||
} else {
|
||||
VoteOption::new(
|
||||
0,
|
||||
"Invalid Option",
|
||||
Some("This is an invalid option".to_string()),
|
||||
)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("get_option_text", |option: VoteOption| -> String {
|
||||
option.text.clone()
|
||||
});
|
||||
|
||||
engine.register_fn("get_option_votes", |option: VoteOption| -> i64 {
|
||||
option.count
|
||||
});
|
||||
|
||||
engine.register_fn("get_ballot_count", |proposal: Proposal| -> i64 {
|
||||
proposal.ballots.len() as i64
|
||||
});
|
||||
|
||||
engine.register_fn(
|
||||
"get_ballot_at",
|
||||
|proposal: Proposal, index: i64| -> Ballot {
|
||||
if index >= 0 && index < proposal.ballots.len() as i64 {
|
||||
proposal.ballots[index as usize].clone()
|
||||
} else {
|
||||
Ballot::new(None, 0, 0, 0)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("get_ballot_user_id", |ballot: Ballot| -> i64 {
|
||||
ballot.user_id as i64
|
||||
});
|
||||
|
||||
engine.register_fn("get_ballot_option_id", |ballot: Ballot| -> i64 {
|
||||
ballot.vote_option_id as i64
|
||||
});
|
||||
|
||||
engine.register_fn("get_ballot_shares", |ballot: Ballot| -> i64 {
|
||||
ballot.shares_count
|
||||
});
|
||||
|
||||
// Load and evaluate the Rhai script
|
||||
let script_path = Path::new("examples/governance_rhai/governance.rhai");
|
||||
let script = fs::read_to_string(script_path)?;
|
||||
|
||||
match engine.eval::<()>(&script) {
|
||||
Ok(_) => println!("Script executed successfully!"),
|
||||
Err(e) => eprintln!("Script execution failed: {}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
// Get the database instance
|
||||
let db = get_db();
|
||||
|
||||
// Create a new proposal with auto-generated ID (pass 0 for auto-generated ID)
|
||||
let proposal = create_proposal(0, "user_creator_123", "Community Fund Allocation for Q3",
|
||||
"Proposal to allocate funds for community projects in the third quarter.");
|
||||
|
||||
print("Created Proposal: '" + get_title(proposal) + "' (ID: " + get_id(proposal) + ")");
|
||||
print("Status: " + get_status(proposal) + ", Vote Status: " + get_vote_status(proposal));
|
||||
|
||||
// Add vote options
|
||||
let proposal_with_options = add_option_to_proposal(proposal, 1, "Approve Allocation");
|
||||
proposal_with_options = add_option_to_proposal(proposal_with_options, 2, "Reject Allocation");
|
||||
proposal_with_options = add_option_to_proposal(proposal_with_options, 3, "Abstain");
|
||||
|
||||
print("\nAdded Vote Options:");
|
||||
let option_count = get_option_count(proposal_with_options);
|
||||
for i in range(0, option_count) {
|
||||
let option = get_option_at(proposal_with_options, i);
|
||||
print("- Option ID: " + i + ", Text: '" + get_option_text(option) + "', Votes: " + get_option_votes(option));
|
||||
}
|
||||
|
||||
// Save the proposal to the database
|
||||
save_proposal(db, proposal_with_options);
|
||||
print("\nProposal saved to database");
|
||||
|
||||
// Simulate casting votes
|
||||
print("\nSimulating Votes...");
|
||||
// User 1 votes for 'Approve Allocation' with 100 shares (with explicit ballot ID)
|
||||
let proposal_with_votes = cast_vote_on_proposal(proposal_with_options, 101, 1, 1, 100);
|
||||
// User 2 votes for 'Reject Allocation' with 50 shares (with explicit ballot ID)
|
||||
proposal_with_votes = cast_vote_on_proposal(proposal_with_votes, 102, 2, 2, 50);
|
||||
// User 3 votes for 'Approve Allocation' with 75 shares (with auto-generated ballot ID)
|
||||
proposal_with_votes = cast_vote_on_proposal(proposal_with_votes, 0, 3, 1, 75);
|
||||
// User 4 abstains with 20 shares (with auto-generated ballot ID)
|
||||
proposal_with_votes = cast_vote_on_proposal(proposal_with_votes, 0, 4, 3, 20);
|
||||
|
||||
print("\nVote Counts After Simulation:");
|
||||
option_count = get_option_count(proposal_with_votes);
|
||||
for i in range(0, option_count) {
|
||||
let option = get_option_at(proposal_with_votes, i);
|
||||
print("- Option ID: " + i + ", Text: '" + get_option_text(option) + "', Votes: " + get_option_votes(option));
|
||||
}
|
||||
|
||||
print("\nBallots Cast:");
|
||||
let ballot_count = get_ballot_count(proposal_with_votes);
|
||||
for i in range(0, ballot_count) {
|
||||
let ballot = get_ballot_at(proposal_with_votes, i);
|
||||
print("- Ballot ID: " + i + ", User ID: " + get_ballot_user_id(ballot) +
|
||||
", Option ID: " + get_ballot_option_id(ballot) + ", Shares: " + get_ballot_shares(ballot));
|
||||
}
|
||||
|
||||
// Change proposal status
|
||||
let active_proposal = change_proposal_status(proposal_with_votes, "Active");
|
||||
print("\nChanged Proposal Status to: " + get_status(active_proposal));
|
||||
|
||||
// Simulate closing the vote
|
||||
let closed_proposal = change_vote_event_status(active_proposal, "Closed");
|
||||
print("Changed Vote Event Status to: " + get_vote_status(closed_proposal));
|
||||
|
||||
// Final proposal state
|
||||
print("\nFinal Proposal State:");
|
||||
print("Title: '" + get_title(closed_proposal) + "'");
|
||||
print("Status: " + get_status(closed_proposal));
|
||||
print("Vote Status: " + get_vote_status(closed_proposal));
|
||||
print("Options:");
|
||||
option_count = get_option_count(closed_proposal);
|
||||
for i in range(0, option_count) {
|
||||
let option = get_option_at(closed_proposal, i);
|
||||
print(" - " + i + ": " + get_option_text(option) + " (Votes: " + get_option_votes(option) + ")");
|
||||
}
|
||||
print("Total Ballots: " + get_ballot_count(closed_proposal));
|
||||
|
||||
// Get all proposals from the database
|
||||
let all_proposals = get_all_proposals(db);
|
||||
print("\nTotal Proposals in Database: " + all_proposals.len());
|
||||
for proposal in all_proposals {
|
||||
print("Proposal ID: " + get_id(proposal) + ", Title: '" + get_title(proposal) + "'");
|
||||
}
|
||||
|
||||
// Delete a proposal
|
||||
delete_proposal_by_id(db, 1);
|
||||
print("\nDeleted proposal with ID 1");
|
||||
|
||||
print("\nGovernance Proposal Example Finished.");
|
@ -1,457 +0,0 @@
|
||||
use chrono::{Duration, Utc};
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::governance::{
|
||||
Ballot, Proposal, ProposalStatus, VoteEventStatus, VoteOption,
|
||||
};
|
||||
use rhai::Engine;
|
||||
use rhai_client_macros::rhai;
|
||||
use rhai_wrapper::wrap_vec_return;
|
||||
use std::sync::Arc;
|
||||
|
||||
// Define the functions we want to expose to Rhai
|
||||
// We'll only use the #[rhai] attribute on functions with simple types
|
||||
|
||||
// Create a proposal (returns a complex type, but takes simple parameters)
|
||||
fn create_proposal(id: i64, creator_id: String, title: String, description: String) -> Proposal {
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
Proposal::new(
|
||||
id as u32,
|
||||
creator_id,
|
||||
title,
|
||||
description,
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
}
|
||||
|
||||
// Getter functions for Proposal properties
|
||||
fn get_title(proposal: &Proposal) -> String {
|
||||
proposal.title.clone()
|
||||
}
|
||||
|
||||
fn get_description(proposal: &Proposal) -> String {
|
||||
proposal.description.clone()
|
||||
}
|
||||
|
||||
fn get_creator_id(proposal: &Proposal) -> String {
|
||||
proposal.creator_id.clone()
|
||||
}
|
||||
|
||||
fn get_id(proposal: &Proposal) -> i64 {
|
||||
proposal.base_data.id as i64
|
||||
}
|
||||
|
||||
fn get_status(proposal: &Proposal) -> String {
|
||||
format!("{:?}", proposal.status)
|
||||
}
|
||||
|
||||
fn get_vote_status(proposal: &Proposal) -> String {
|
||||
format!("{:?}", proposal.vote_status)
|
||||
}
|
||||
|
||||
// Functions that operate on Proposal objects
|
||||
fn add_option_to_proposal(proposal: Proposal, option_id: i64, option_text: String) -> Proposal {
|
||||
proposal.add_option(option_id as u8, option_text)
|
||||
}
|
||||
|
||||
fn cast_vote_on_proposal(
|
||||
proposal: Proposal,
|
||||
ballot_id: i64,
|
||||
user_id: i64,
|
||||
option_id: i64,
|
||||
shares: i64,
|
||||
) -> Proposal {
|
||||
proposal.cast_vote(ballot_id as u32, user_id as u32, option_id as u8, shares)
|
||||
}
|
||||
|
||||
fn change_proposal_status(proposal: Proposal, status_str: String) -> Proposal {
|
||||
let new_status = match status_str.as_str() {
|
||||
"Draft" => ProposalStatus::Draft,
|
||||
"Active" => ProposalStatus::Active,
|
||||
"Approved" => ProposalStatus::Approved,
|
||||
"Rejected" => ProposalStatus::Rejected,
|
||||
"Cancelled" => ProposalStatus::Cancelled,
|
||||
_ => ProposalStatus::Draft,
|
||||
};
|
||||
proposal.change_proposal_status(new_status)
|
||||
}
|
||||
|
||||
fn change_vote_event_status(proposal: Proposal, status_str: String) -> Proposal {
|
||||
let new_status = match status_str.as_str() {
|
||||
"Open" => VoteEventStatus::Open,
|
||||
"Closed" => VoteEventStatus::Closed,
|
||||
"Cancelled" => VoteEventStatus::Cancelled,
|
||||
_ => VoteEventStatus::Open,
|
||||
};
|
||||
proposal.change_vote_event_status(new_status)
|
||||
}
|
||||
|
||||
// Functions for accessing proposal options and ballots
|
||||
fn get_option_count(proposal: &Proposal) -> i64 {
|
||||
proposal.options.len() as i64
|
||||
}
|
||||
|
||||
fn get_option_at(proposal: &Proposal, index: i64) -> VoteOption {
|
||||
if index >= 0 && index < proposal.options.len() as i64 {
|
||||
proposal.options[index as usize].clone()
|
||||
} else {
|
||||
VoteOption::new(0, "Invalid Option")
|
||||
}
|
||||
}
|
||||
|
||||
fn get_option_text(option: &VoteOption) -> String {
|
||||
option.text.clone()
|
||||
}
|
||||
|
||||
fn get_option_votes(option: &VoteOption) -> i64 {
|
||||
option.count
|
||||
}
|
||||
|
||||
fn get_ballot_count(proposal: &Proposal) -> i64 {
|
||||
proposal.ballots.len() as i64
|
||||
}
|
||||
|
||||
fn get_ballot_at(proposal: &Proposal, index: i64) -> Ballot {
|
||||
if index >= 0 && index < proposal.ballots.len() as i64 {
|
||||
proposal.ballots[index as usize].clone()
|
||||
} else {
|
||||
Ballot::new(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ballot_user_id(ballot: &Ballot) -> i64 {
|
||||
ballot.user_id as i64
|
||||
}
|
||||
|
||||
fn get_ballot_option_id(ballot: &Ballot) -> i64 {
|
||||
ballot.vote_option_id as i64
|
||||
}
|
||||
|
||||
fn get_ballot_shares(ballot: &Ballot) -> i64 {
|
||||
ballot.shares_count
|
||||
}
|
||||
|
||||
// Simple functions that we can use with the #[rhai] attribute
|
||||
#[rhai]
|
||||
fn create_proposal_wrapper(
|
||||
id: i64,
|
||||
creator_id: String,
|
||||
title: String,
|
||||
description: String,
|
||||
) -> String {
|
||||
let proposal = create_proposal(id, creator_id, title, description);
|
||||
format!("Created proposal with ID: {}", proposal.base_data.id)
|
||||
}
|
||||
|
||||
#[rhai]
|
||||
fn add_option_wrapper(id: i64, option_id: i64, option_text: String) -> String {
|
||||
let proposal = create_proposal(
|
||||
id,
|
||||
"user".to_string(),
|
||||
"title".to_string(),
|
||||
"description".to_string(),
|
||||
);
|
||||
let updated = add_option_to_proposal(proposal, option_id, option_text.clone());
|
||||
format!("Added option '{}' to proposal {}", option_text, id)
|
||||
}
|
||||
|
||||
// Database operations
|
||||
fn save_proposal(_db: Arc<OurDB>, proposal: Proposal) {
|
||||
println!("Proposal saved: {}", proposal.title);
|
||||
}
|
||||
|
||||
fn get_all_proposals(_db: Arc<OurDB>) -> Vec<Proposal> {
|
||||
// In a real implementation, this would retrieve all proposals from the database
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
vec![
|
||||
Proposal::new(
|
||||
1,
|
||||
"Creator 1",
|
||||
"Proposal 1",
|
||||
"Description 1",
|
||||
start_date,
|
||||
end_date,
|
||||
),
|
||||
Proposal::new(
|
||||
2,
|
||||
"Creator 2",
|
||||
"Proposal 2",
|
||||
"Description 2",
|
||||
start_date,
|
||||
end_date,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
fn delete_proposal_by_id(_db: Arc<OurDB>, id: i64) {
|
||||
// In a real implementation, this would delete the proposal from the database
|
||||
println!("Proposal deleted with ID: {}", id);
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Initialize database
|
||||
let db = Arc::new(OurDB::new("temp_governance_db", true).expect("Failed to create database"));
|
||||
|
||||
// Register the Proposal type with Rhai
|
||||
// This function is generated by the #[rhai_model_export] attribute
|
||||
Proposal::register_rhai_bindings_for_proposal(&mut engine, db.clone());
|
||||
|
||||
// Register the Ballot type with Rhai
|
||||
Ballot::register_rhai_bindings_for_ballot(&mut engine, db.clone());
|
||||
|
||||
// Create a clone of db for use in the get_db function
|
||||
let db_for_get_db = db.clone();
|
||||
|
||||
// Register a function to get the database instance
|
||||
engine.register_fn("get_db", move || db_for_get_db.clone());
|
||||
|
||||
// Register builder functions for Proposal and related types
|
||||
engine.register_fn(
|
||||
"create_proposal",
|
||||
|id: i64, creator_id: String, title: String, description: String| {
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
Proposal::new(
|
||||
id as u32,
|
||||
creator_id,
|
||||
title,
|
||||
description,
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("create_vote_option", |id: i64, text: String| {
|
||||
VoteOption::new(id as u8, text)
|
||||
});
|
||||
|
||||
engine.register_fn(
|
||||
"create_ballot",
|
||||
|id: i64, user_id: i64, vote_option_id: i64, shares_count: i64| {
|
||||
Ballot::new(
|
||||
id as u32,
|
||||
user_id as u32,
|
||||
vote_option_id as u8,
|
||||
shares_count,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
// Register getter and setter methods for Proposal properties
|
||||
engine.register_fn("get_title", get_title);
|
||||
engine.register_fn("get_description", get_description);
|
||||
engine.register_fn("get_creator_id", get_creator_id);
|
||||
engine.register_fn("get_id", get_id);
|
||||
engine.register_fn("get_status", get_status);
|
||||
engine.register_fn("get_vote_status", get_vote_status);
|
||||
|
||||
// Register methods for proposal operations
|
||||
engine.register_fn("add_option_to_proposal", add_option_to_proposal);
|
||||
engine.register_fn("cast_vote_on_proposal", cast_vote_on_proposal);
|
||||
engine.register_fn("change_proposal_status", change_proposal_status);
|
||||
engine.register_fn("change_vote_event_status", change_vote_event_status);
|
||||
|
||||
// Register functions for database operations
|
||||
engine.register_fn("save_proposal", save_proposal);
|
||||
|
||||
engine.register_fn(
|
||||
"get_proposal_by_id",
|
||||
|_db: Arc<OurDB>, id: i64| -> Proposal {
|
||||
// In a real implementation, this would retrieve the proposal from the database
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
Proposal::new(
|
||||
id as u32,
|
||||
"Retrieved Creator",
|
||||
"Retrieved Proposal",
|
||||
"Retrieved Description",
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
// Register a function to check if a proposal exists
|
||||
engine.register_fn("proposal_exists", |_db: Arc<OurDB>, id: i64| -> bool {
|
||||
// In a real implementation, this would check if the proposal exists in the database
|
||||
id == 1 || id == 2
|
||||
});
|
||||
|
||||
// Register the function with the wrap_vec_return macro
|
||||
engine.register_fn(
|
||||
"get_all_proposals",
|
||||
wrap_vec_return!(get_all_proposals, Arc<OurDB> => Proposal),
|
||||
);
|
||||
|
||||
engine.register_fn("delete_proposal_by_id", delete_proposal_by_id);
|
||||
|
||||
// Register helper functions for accessing proposal options and ballots
|
||||
engine.register_fn("get_option_count", get_option_count);
|
||||
engine.register_fn("get_option_at", get_option_at);
|
||||
engine.register_fn("get_option_text", get_option_text);
|
||||
engine.register_fn("get_option_votes", get_option_votes);
|
||||
engine.register_fn("get_ballot_count", get_ballot_count);
|
||||
engine.register_fn("get_ballot_at", get_ballot_at);
|
||||
engine.register_fn("get_ballot_user_id", get_ballot_user_id);
|
||||
engine.register_fn("get_ballot_option_id", get_ballot_option_id);
|
||||
engine.register_fn("get_ballot_shares", get_ballot_shares);
|
||||
|
||||
// Register our wrapper functions that use the #[rhai] attribute
|
||||
engine.register_fn("create_proposal_wrapper", create_proposal_wrapper);
|
||||
engine.register_fn("add_option_wrapper", add_option_wrapper);
|
||||
|
||||
// Now instead of loading and evaluating a Rhai script, we'll use direct function calls
|
||||
// to implement the same functionality
|
||||
|
||||
// Use the database instance
|
||||
|
||||
// Create a new proposal
|
||||
let proposal = create_proposal(
|
||||
1,
|
||||
"user_creator_123".to_string(),
|
||||
"Community Fund Allocation for Q3".to_string(),
|
||||
"Proposal to allocate funds for community projects in the third quarter.".to_string(),
|
||||
);
|
||||
|
||||
println!(
|
||||
"Created Proposal: '{}' (ID: {})",
|
||||
get_title(&proposal),
|
||||
get_id(&proposal)
|
||||
);
|
||||
println!(
|
||||
"Status: {}, Vote Status: {}",
|
||||
get_status(&proposal),
|
||||
get_vote_status(&proposal)
|
||||
);
|
||||
|
||||
// Add vote options
|
||||
let mut proposal_with_options =
|
||||
add_option_to_proposal(proposal, 1, "Approve Allocation".to_string());
|
||||
proposal_with_options =
|
||||
add_option_to_proposal(proposal_with_options, 2, "Reject Allocation".to_string());
|
||||
proposal_with_options = add_option_to_proposal(proposal_with_options, 3, "Abstain".to_string());
|
||||
|
||||
println!("\nAdded Vote Options:");
|
||||
let option_count = get_option_count(&proposal_with_options);
|
||||
for i in 0..option_count {
|
||||
let option = get_option_at(&proposal_with_options, i);
|
||||
println!(
|
||||
"- Option ID: {}, Text: '{}', Votes: {}",
|
||||
i,
|
||||
get_option_text(&option),
|
||||
get_option_votes(&option)
|
||||
);
|
||||
}
|
||||
|
||||
// Save the proposal to the database
|
||||
save_proposal(db.clone(), proposal_with_options.clone());
|
||||
println!("\nProposal saved to database");
|
||||
|
||||
// Simulate casting votes
|
||||
println!("\nSimulating Votes...");
|
||||
// User 1 votes for 'Approve Allocation' with 100 shares
|
||||
let mut proposal_with_votes = cast_vote_on_proposal(proposal_with_options, 101, 1, 1, 100);
|
||||
// User 2 votes for 'Reject Allocation' with 50 shares
|
||||
proposal_with_votes = cast_vote_on_proposal(proposal_with_votes, 102, 2, 2, 50);
|
||||
// User 3 votes for 'Approve Allocation' with 75 shares
|
||||
proposal_with_votes = cast_vote_on_proposal(proposal_with_votes, 103, 3, 1, 75);
|
||||
// User 4 abstains with 20 shares
|
||||
proposal_with_votes = cast_vote_on_proposal(proposal_with_votes, 104, 4, 3, 20);
|
||||
|
||||
println!("\nVote Counts After Simulation:");
|
||||
let option_count = get_option_count(&proposal_with_votes);
|
||||
for i in 0..option_count {
|
||||
let option = get_option_at(&proposal_with_votes, i);
|
||||
println!(
|
||||
"- Option ID: {}, Text: '{}', Votes: {}",
|
||||
i,
|
||||
get_option_text(&option),
|
||||
get_option_votes(&option)
|
||||
);
|
||||
}
|
||||
|
||||
println!("\nBallots Cast:");
|
||||
let ballot_count = get_ballot_count(&proposal_with_votes);
|
||||
for i in 0..ballot_count {
|
||||
let ballot = get_ballot_at(&proposal_with_votes, i);
|
||||
println!(
|
||||
"- Ballot ID: {}, User ID: {}, Option ID: {}, Shares: {}",
|
||||
i,
|
||||
get_ballot_user_id(&ballot),
|
||||
get_ballot_option_id(&ballot),
|
||||
get_ballot_shares(&ballot)
|
||||
);
|
||||
}
|
||||
|
||||
// Change proposal status
|
||||
let active_proposal = change_proposal_status(proposal_with_votes, "Active".to_string());
|
||||
println!(
|
||||
"\nChanged Proposal Status to: {}",
|
||||
get_status(&active_proposal)
|
||||
);
|
||||
|
||||
// Simulate closing the vote
|
||||
let closed_proposal = change_vote_event_status(active_proposal, "Closed".to_string());
|
||||
println!(
|
||||
"Changed Vote Event Status to: {}",
|
||||
get_vote_status(&closed_proposal)
|
||||
);
|
||||
|
||||
// Final proposal state
|
||||
println!("\nFinal Proposal State:");
|
||||
println!("Title: '{}'", get_title(&closed_proposal));
|
||||
println!("Status: {}", get_status(&closed_proposal));
|
||||
println!("Vote Status: {}", get_vote_status(&closed_proposal));
|
||||
println!("Options:");
|
||||
let option_count = get_option_count(&closed_proposal);
|
||||
for i in 0..option_count {
|
||||
let option = get_option_at(&closed_proposal, i);
|
||||
println!(
|
||||
" - {}: {} (Votes: {})",
|
||||
i,
|
||||
get_option_text(&option),
|
||||
get_option_votes(&option)
|
||||
);
|
||||
}
|
||||
println!("Total Ballots: {}", get_ballot_count(&closed_proposal));
|
||||
|
||||
// Get all proposals from the database
|
||||
let all_proposals = get_all_proposals(db.clone());
|
||||
println!("\nTotal Proposals in Database: {}", all_proposals.len());
|
||||
for proposal in all_proposals {
|
||||
println!(
|
||||
"Proposal ID: {}, Title: '{}'",
|
||||
get_id(&proposal),
|
||||
get_title(&proposal)
|
||||
);
|
||||
}
|
||||
|
||||
// Delete a proposal
|
||||
delete_proposal_by_id(db.clone(), 1);
|
||||
println!("\nDeleted proposal with ID 1");
|
||||
|
||||
// Demonstrate the use of Rhai client functions for simple types
|
||||
println!("\nUsing Rhai client functions for simple types:");
|
||||
let create_result = create_proposal_wrapper_rhai_client(
|
||||
&engine,
|
||||
2,
|
||||
"rhai_user".to_string(),
|
||||
"Rhai Proposal".to_string(),
|
||||
"This proposal was created using a Rhai client function".to_string(),
|
||||
);
|
||||
println!("{}", create_result);
|
||||
|
||||
let add_option_result =
|
||||
add_option_wrapper_rhai_client(&engine, 2, 4, "Rhai Option".to_string());
|
||||
println!("{}", add_option_result);
|
||||
|
||||
println!("\nGovernance Proposal Example Finished.");
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::legal::register_legal_rhai_module;
|
||||
use rhai::Engine;
|
||||
use std::sync::Arc;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Initialize database with OurDB
|
||||
// Using a temporary/in-memory database for the example (creates files in current dir)
|
||||
let db_name = "temp_legal_rhai_db";
|
||||
let db = Arc::new(OurDB::new(db_name, true).expect("Failed to create database"));
|
||||
|
||||
// Register legal Rhai module functions
|
||||
register_legal_rhai_module(&mut engine, db.clone());
|
||||
|
||||
// Load and evaluate the Rhai script
|
||||
let script_path_str = "examples/legal_rhai/legal.rhai";
|
||||
let script_path = Path::new(script_path_str);
|
||||
|
||||
if !script_path.exists() {
|
||||
eprintln!("Error: Rhai script not found at {}", script_path_str);
|
||||
eprintln!(
|
||||
"Please ensure the script 'legal.rhai' exists in the 'examples/legal_rhai/' directory."
|
||||
);
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
format!("Rhai script not found: {}", script_path_str),
|
||||
)));
|
||||
}
|
||||
|
||||
println!("Executing Rhai script: {}", script_path_str);
|
||||
let script = fs::read_to_string(script_path)?;
|
||||
|
||||
match engine.eval::<()>(&script) {
|
||||
Ok(_) => println!("\nRhai script executed successfully!"),
|
||||
Err(e) => {
|
||||
eprintln!("\nRhai script execution failed: {}\nDetails: {:#?}", e, e);
|
||||
// No explicit cleanup in this example, similar to flow_rhai_example
|
||||
return Err(e.into()); // Propagate the Rhai error
|
||||
}
|
||||
}
|
||||
|
||||
// No explicit cleanup in this example, similar to flow_rhai_example
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
// heromodels - Legal Module Rhai Example
|
||||
print("Hero Models - Legal Rhai Example");
|
||||
print("===============================");
|
||||
|
||||
// Helper to format Option<String> (Dynamic in Rhai: String or ()) for printing
|
||||
fn format_optional_string(val, placeholder) {
|
||||
if val == () {
|
||||
placeholder
|
||||
} else {
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to format Option<i64> (Dynamic in Rhai: i64 or ()) for printing
|
||||
fn format_optional_int(val, placeholder) {
|
||||
if val == () {
|
||||
placeholder
|
||||
} else {
|
||||
"" + val // Convert int to string for concatenation
|
||||
}
|
||||
}
|
||||
|
||||
print("DB instance will be implicitly passed to DB functions.");
|
||||
|
||||
// --- Using Enum Constants ---
|
||||
print(`\n--- Enum Constants ---`);
|
||||
print(`ContractStatus Draft: ${ContractStatusConstants::Draft}`);
|
||||
print(`ContractStatus Active: ${ContractStatusConstants::Active}`);
|
||||
print(`SignerStatus Pending: ${SignerStatusConstants::Pending}`);
|
||||
print(`SignerStatus Signed: ${SignerStatusConstants::Signed}`);
|
||||
|
||||
// --- Test ContractSigner Model ---
|
||||
print("\n--- Testing ContractSigner Model ---");
|
||||
let signer1_id = "signer-uuid-001";
|
||||
let signer1 = new_contract_signer(signer1_id, "Alice Wonderland", "alice@example.com")
|
||||
.status(SignerStatusConstants::Pending)
|
||||
.comments("Alice is the primary signatory.");
|
||||
|
||||
print(`Signer 1 ID: ${signer1.id}, Name: ${signer1.name}, Email: ${signer1.email}`);
|
||||
print(`Signer 1 Status: ${signer1.status}, Comments: ${format_optional_string(signer1.comments, "N/A")}`);
|
||||
print(`Signer 1 Signed At: ${format_optional_int(signer1.signed_at, "Not signed")}`);
|
||||
|
||||
let signer2_id = "signer-uuid-002";
|
||||
let signer2 = new_contract_signer(signer2_id, "Bob The Builder", "bob@example.com")
|
||||
.status(SignerStatusConstants::Signed)
|
||||
.signed_at(1678886400) // Example timestamp
|
||||
.comments("Bob has already signed.");
|
||||
|
||||
print(`Signer 2 ID: ${signer2.id}, Name: ${signer2.name}, Status: ${signer2.status}, Signed At: ${format_optional_int(signer2.signed_at, "N/A")}`);
|
||||
|
||||
// --- Test ContractRevision Model ---
|
||||
print("\n--- Testing ContractRevision Model ---");
|
||||
let revision1_version = 1;
|
||||
let revision1 = new_contract_revision(revision1_version, "Initial draft content for the agreement, version 1.", 1678880000, "user-admin-01")
|
||||
.comments("First version of the contract.");
|
||||
|
||||
print(`Revision 1 Version: ${revision1.version}, Content: '${revision1.content}', Created At: ${revision1.created_at}, By: ${revision1.created_by}`);
|
||||
print(`Revision 1 Comments: ${format_optional_string(revision1.comments, "N/A")}`);
|
||||
|
||||
let revision2_version = 2;
|
||||
let revision2 = new_contract_revision(revision2_version, "Updated content with new clauses, version 2.", 1678882200, "user-legal-02");
|
||||
|
||||
// --- Test Contract Model ---
|
||||
print("\n--- Testing Contract Model ---");
|
||||
let contract1_base_id = 101;
|
||||
let contract1_uuid = "contract-uuid-xyz-001";
|
||||
|
||||
print(`Creating a new contract (ID: ${contract1_base_id}, UUID: ${contract1_uuid})...`);
|
||||
let contract1 = new_contract(contract1_base_id, contract1_uuid)
|
||||
.title("Master Service Agreement")
|
||||
.description("MSA between ACME Corp and Client Inc.")
|
||||
.contract_type("Services")
|
||||
.status(ContractStatusConstants::Draft)
|
||||
.created_by("user-admin-01")
|
||||
.terms_and_conditions("Standard terms and conditions apply. See Appendix A.")
|
||||
.start_date(1678900000)
|
||||
.current_version(revision1.version)
|
||||
.add_signer(signer1)
|
||||
.add_revision(revision1);
|
||||
|
||||
print(`Contract 1 Title: ${contract1.title}, Status: ${contract1.status}`);
|
||||
print(`Contract 1 Signers: ${contract1.signers.len()}, Revisions: ${contract1.revisions.len()}`);
|
||||
|
||||
// Add more data
|
||||
contract1 = contract1.add_signer(signer2).add_revision(revision2).current_version(revision2.version);
|
||||
print(`Contract 1 Updated Signers: ${contract1.signers.len()}, Revisions: ${contract1.revisions.len()}, Current Version: ${contract1.current_version}`);
|
||||
|
||||
// Save the contract to the database
|
||||
print("Saving contract1 to database...");
|
||||
set_contract(contract1);
|
||||
print("Contract1 saved.");
|
||||
|
||||
// Retrieve the contract
|
||||
print(`Retrieving contract by ID (${contract1_base_id})...`);
|
||||
let retrieved_contract = get_contract_by_id(contract1_base_id);
|
||||
print(`Retrieved Contract: ${retrieved_contract.title}, Status: ${retrieved_contract.status}`);
|
||||
print(`Retrieved Contract Signers: ${retrieved_contract.signers.len()}, Revisions: ${retrieved_contract.revisions.len()}`);
|
||||
if retrieved_contract.signers.len() > 0 {
|
||||
print(`First signer of retrieved contract: ${retrieved_contract.signers[0].name}`);
|
||||
}
|
||||
if retrieved_contract.revisions.len() > 0 {
|
||||
print(`First revision content of retrieved contract: '${retrieved_contract.revisions[0].content}'`);
|
||||
}
|
||||
|
||||
// --- Test updating a Contract ---
|
||||
print("\n--- Testing Update for Contract ---");
|
||||
let updated_contract = retrieved_contract
|
||||
.status(ContractStatusConstants::Active)
|
||||
.end_date(1700000000)
|
||||
.description("MSA (Active) between ACME Corp and Client Inc. with new addendum.");
|
||||
|
||||
print(`Updated Contract - Title: ${updated_contract.title}, Status: ${updated_contract.status}, End Date: ${format_optional_int(updated_contract.end_date, "N/A")}`);
|
||||
set_contract(updated_contract); // Save updated
|
||||
print("Updated Contract saved.");
|
||||
|
||||
let final_retrieved_contract = get_contract_by_id(contract1_base_id);
|
||||
print(`Final Retrieved Contract - Status: ${final_retrieved_contract.status}, Description: '${final_retrieved_contract.description}'`);
|
||||
|
||||
print("\nLegal Rhai example script finished.");
|
@ -1,37 +0,0 @@
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::register_library_rhai_module;
|
||||
use rhai::Engine;
|
||||
use std::sync::Arc;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Initialize database with OurDB
|
||||
let db_path = "temp_library_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_library_rhai_module(&mut engine, db.clone());
|
||||
|
||||
// Load and evaluate the Rhai script
|
||||
let script_path = Path::new(file!()).parent().unwrap().join("library.rhai");
|
||||
let script = fs::read_to_string(&script_path)?;
|
||||
|
||||
println!("--- Running Library 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(())
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
// heromodels/examples/library_rhai/library.rhai
|
||||
|
||||
print("--- Testing Library Rhai Module ---");
|
||||
|
||||
// --- Image ---
|
||||
print("\n1. Creating and saving an image...");
|
||||
let my_image = new_image()
|
||||
.title("A Beautiful Sunset")
|
||||
.description("A photo taken from a drone.")
|
||||
.url("https://example.com/sunset.jpg")
|
||||
.width(1920)
|
||||
.height(1080);
|
||||
|
||||
let saved_image = save_image(my_image);
|
||||
print(" > Saved image with ID: " + saved_image.id);
|
||||
let image_id = saved_image.id;
|
||||
|
||||
// --- PDF ---
|
||||
print("\n2. Creating and saving a PDF...");
|
||||
let my_pdf = new_pdf()
|
||||
.title("Rust Programming Guide")
|
||||
.description("A comprehensive guide to Rust.")
|
||||
.url("https://example.com/rust.pdf")
|
||||
.page_count(500);
|
||||
|
||||
let saved_pdf = save_pdf(my_pdf);
|
||||
print(" > Saved PDF with ID: " + saved_pdf.id);
|
||||
let pdf_id = saved_pdf.id;
|
||||
|
||||
// --- Markdown ---
|
||||
print("\n3. Creating and saving a Markdown document...");
|
||||
let my_markdown = new_markdown()
|
||||
.title("Meeting Notes")
|
||||
.description("Notes from the weekly sync.")
|
||||
.content("# Meeting Notes\n\n- Discussed project status.\n- Planned next sprint.");
|
||||
|
||||
let saved_markdown = save_markdown(my_markdown);
|
||||
print(" > Saved Markdown with ID: " + saved_markdown.id);
|
||||
let markdown_id = saved_markdown.id;
|
||||
|
||||
// --- Collection ---
|
||||
print("\n4. Creating a collection and adding items...");
|
||||
let my_collection = new_collection()
|
||||
.title("My Awesome Collection")
|
||||
.description("A collection of various media.")
|
||||
.add_image(image_id)
|
||||
.add_pdf(pdf_id)
|
||||
.add_markdown(markdown_id);
|
||||
|
||||
let saved_collection = save_collection(my_collection);
|
||||
print(" > Saved collection with ID: " + saved_collection.id);
|
||||
let collection_id = saved_collection.id;
|
||||
|
||||
// --- Verification ---
|
||||
print("\n5. Verifying saved data...");
|
||||
let fetched_collection = get_collection(collection_id);
|
||||
print(" > Fetched collection: '" + fetched_collection.title + "'");
|
||||
print(" > Collection contains " + fetched_collection.images + " image(s).");
|
||||
print(" > Collection contains " + fetched_collection.pdfs + " pdf(s).");
|
||||
print(" > Collection contains " + fetched_collection.markdowns + " markdown(s).");
|
||||
|
||||
let fetched_image = get_image(image_id);
|
||||
print(" > Fetched image title: '" + fetched_image.title + "'");
|
||||
if (fetched_image.url != "https://example.com/sunset.jpg") {
|
||||
throw "Image URL mismatch!";
|
||||
}
|
||||
print(" > Image URL verified.");
|
||||
|
||||
// --- Deletion ---
|
||||
print("\n6. Cleaning up database...");
|
||||
delete_image(image_id);
|
||||
print(" > Deleted image with ID: " + image_id);
|
||||
delete_pdf(pdf_id);
|
||||
print(" > Deleted PDF with ID: " + pdf_id);
|
||||
delete_markdown(markdown_id);
|
||||
print(" > Deleted Markdown with ID: " + markdown_id);
|
||||
delete_collection(collection_id);
|
||||
print(" > Deleted collection with ID: " + collection_id);
|
@ -1,43 +0,0 @@
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::projects::register_projects_rhai_module;
|
||||
use rhai::{Engine, EvalAltResult, Scope};
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
println!("Executing Rhai script: examples/project_rhai/project_test.rhai");
|
||||
|
||||
// Create a new Rhai engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Create an Arc<OurDB> instance
|
||||
let db_instance = Arc::new(OurDB::new(".", false).expect("Failed to create DB"));
|
||||
|
||||
// Register the projects module with the engine
|
||||
register_projects_rhai_module(&mut engine, Arc::clone(&db_instance));
|
||||
|
||||
// Read the Rhai script from file
|
||||
let script_path = "examples/project_rhai/project_test.rhai";
|
||||
let script_content = fs::read_to_string(script_path).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
format!("Cannot read script file: {}", script_path),
|
||||
e.into(),
|
||||
))
|
||||
})?;
|
||||
|
||||
// Create a new scope
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Execute the script
|
||||
match engine.run_with_scope(&mut scope, &script_content) {
|
||||
Ok(_) => {
|
||||
println!("Rhai script executed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Rhai script execution failed: {}", e);
|
||||
println!("Details: {:?}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
// Test script for Project Rhai integration
|
||||
|
||||
print("--- Testing Project Rhai Integration ---");
|
||||
|
||||
// Create a new project
|
||||
let p1 = new_project()
|
||||
.name("Project Alpha")
|
||||
.description("This is the first test project.")
|
||||
.owner_id(101)
|
||||
.add_member_id(102)
|
||||
.add_member_id(103)
|
||||
.member_ids([201, 202, 203]) // Test setting multiple IDs
|
||||
.add_tag("important")
|
||||
.add_tag("rhai_test")
|
||||
.tags(["core", "feature_test"]) // Test setting multiple tags
|
||||
.status(Status::InProgress)
|
||||
.priority(Priority::High)
|
||||
.item_type(ItemType::Feature)
|
||||
.add_base_comment(1001);
|
||||
|
||||
print("Created project p1: " + p1);
|
||||
print("p1.name: " + p1.name);
|
||||
print("p1.description: " + p1.description);
|
||||
print("p1.owner_id: " + p1.owner_id);
|
||||
print("p1.member_ids: " + p1.member_ids);
|
||||
print("p1.tags: " + p1.tags);
|
||||
print(`p1.status: ${p1.status.to_string()}`);
|
||||
print(`p1.priority: ${p1.priority.to_string()}`);
|
||||
print(`p1.item_type: ${p1.item_type.to_string()}`);
|
||||
print("p1.id: " + p1.id);
|
||||
print("p1.created_at: " + p1.created_at);
|
||||
print("p1.modified_at: " + p1.modified_at);
|
||||
print("p1.comments: " + p1.comments);
|
||||
|
||||
// Save to DB
|
||||
try {
|
||||
set_project(p1);
|
||||
print("Project p1 saved successfully.");
|
||||
} catch (err) {
|
||||
print("Error saving project p1: " + err);
|
||||
}
|
||||
|
||||
// Retrieve from DB
|
||||
try {
|
||||
let retrieved_p1 = get_project_by_id(1);
|
||||
if retrieved_p1 != () { // Check if Some(project) was returned (None becomes '()')
|
||||
print("Retrieved project by ID 1: " + retrieved_p1);
|
||||
print("Retrieved project name: " + retrieved_p1.name);
|
||||
print("Retrieved project tags: " + retrieved_p1.tags);
|
||||
} else {
|
||||
print("Project with ID 1 not found.");
|
||||
}
|
||||
} catch (err) {
|
||||
print("Error retrieving project by ID 1: " + err);
|
||||
}
|
||||
|
||||
// Test non-existent project
|
||||
try {
|
||||
let non_existent_project = get_project_by_id(999);
|
||||
if non_existent_project != () {
|
||||
print("Error: Found non-existent project 999: " + non_existent_project);
|
||||
} else {
|
||||
print("Correctly did not find project with ID 999.");
|
||||
}
|
||||
} catch (err) {
|
||||
print("Error checking for non-existent project: " + err);
|
||||
}
|
||||
|
||||
print("--- Project Rhai Integration Test Complete ---");
|
@ -1,9 +0,0 @@
|
||||
[build]
|
||||
# Set the default build target for this project
|
||||
target = "wasm32-unknown-unknown"
|
||||
|
||||
# Configuration for the wasm32-unknown-unknown target
|
||||
[target.wasm32-unknown-unknown]
|
||||
# Pass --cfg=wasm_js to rustc when compiling for this target.
|
||||
# This is required by the getrandom crate.
|
||||
rustflags = ["--cfg=wasm_js"] # For getrandom v0.3.x WASM support (required by rhai via ahash)
|
@ -1,20 +0,0 @@
|
||||
[package]
|
||||
name = "project_rhai_wasm_example"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
heromodels = { path = "../..", features = ["rhai"] } # Match heromodels main crate
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["console"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
js-sys = "0.3"
|
||||
getrandom = { version = "0.3.3", features = ["js"] } # Align with rhai's dependency
|
||||
|
||||
[profile.release]
|
||||
# Tell `rustc` to optimize for small code size.
|
||||
lto = true
|
||||
opt-level = 's'
|
@ -1,52 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HeroModels Project Rhai WASM Test</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; margin: 20px; background-color: #f4f4f4; color: #333; }
|
||||
#output { margin-top: 20px; padding: 10px; border: 1px solid #ccc; background-color: #fff; white-space: pre-wrap; }
|
||||
button { padding: 10px 15px; font-size: 16px; cursor: pointer; background-color: #007bff; color: white; border: none; border-radius: 5px; }
|
||||
button:hover { background-color: #0056b3; }
|
||||
h1 { color: #0056b3; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>HeroModels Project Rhai WASM Test</h1>
|
||||
<p>Open your browser's developer console to see detailed logs from the Rhai script.</p>
|
||||
<button id="runScriptButton">Run Rhai Script in WASM</button>
|
||||
|
||||
<script type="module">
|
||||
// Import the WASM module
|
||||
import init, { run_project_script_wasm } from './pkg/project_rhai_wasm_example.js';
|
||||
|
||||
async function main() {
|
||||
// Initialize the WASM module
|
||||
await init();
|
||||
console.log("WASM module initialized.");
|
||||
|
||||
const runButton = document.getElementById('runScriptButton');
|
||||
runButton.onclick = () => {
|
||||
console.log("Button clicked, attempting to run script...");
|
||||
try {
|
||||
run_project_script_wasm();
|
||||
console.log("run_project_script_wasm called.");
|
||||
} catch (e) {
|
||||
console.error("Error calling run_project_script_wasm:", e);
|
||||
}
|
||||
};
|
||||
// Automatically run the script on load if desired
|
||||
// console.log("Attempting to run script on load...");
|
||||
// try {
|
||||
// run_project_script_wasm();
|
||||
// console.log("run_project_script_wasm called on load.");
|
||||
// } catch (e) {
|
||||
// console.error("Error calling run_project_script_wasm on load:", e);
|
||||
// }
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,126 +0,0 @@
|
||||
use heromodels::db::{OurDB, Db}; // Import Db trait
|
||||
use heromodels::models::projects::rhai::register_projects_rhai_module;
|
||||
use rhai::{Engine, Scope, Dynamic, EvalAltResult, Position};
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console;
|
||||
|
||||
// Called once when the WASM module is instantiated.
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn main_wasm() -> Result<(), JsValue> {
|
||||
// For better panic messages in the browser console
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run_project_script_wasm() -> Result<(), JsValue> {
|
||||
console::log_1(&"Starting Rhai script execution in WASM...".into());
|
||||
|
||||
let script = r#"
|
||||
// Test script for Project Rhai integration
|
||||
|
||||
print("--- Testing Project Rhai Integration (WASM) ---");
|
||||
|
||||
// Create a new project
|
||||
let p1 = new_project()
|
||||
.name("Project Alpha WASM")
|
||||
.description("This is the first test project in WASM.")
|
||||
.owner_id(101)
|
||||
.add_member_id(102)
|
||||
.add_member_id(103)
|
||||
.member_ids([201, 202, 203])
|
||||
.add_tag("important")
|
||||
.add_tag("rhai_test")
|
||||
.add_tag("wasm")
|
||||
.tags(["core", "feature_test", "wasm_run"])
|
||||
.status(Status::InProgress)
|
||||
.priority(Priority::High)
|
||||
.item_type(ItemType::Feature)
|
||||
.add_base_comment(1001);
|
||||
|
||||
print("Created project p1: " + p1);
|
||||
print("p1.name: " + p1.name);
|
||||
print("p1.description: " + p1.description);
|
||||
print("p1.owner_id: " + p1.owner_id);
|
||||
print("p1.member_ids: " + p1.member_ids);
|
||||
print("p1.tags: " + p1.tags);
|
||||
print(`p1.status: ${p1.status.to_string()}`);
|
||||
print(`p1.priority: ${p1.priority.to_string()}`);
|
||||
print(`p1.item_type: ${p1.item_type.to_string()}`);
|
||||
print("p1.id: " + p1.id);
|
||||
print("p1.created_at: " + p1.created_at);
|
||||
print("p1.modified_at: " + p1.modified_at);
|
||||
print("p1.comments: " + p1.comments);
|
||||
|
||||
// Save to DB
|
||||
try {
|
||||
set_project(p1);
|
||||
print("Project p1 saved successfully.");
|
||||
} catch (err) {
|
||||
print("Error saving project p1: " + err);
|
||||
}
|
||||
|
||||
// Retrieve from DB
|
||||
try {
|
||||
let retrieved_p1 = get_project_by_id(1);
|
||||
if retrieved_p1 != () { // Check if Some(project) was returned (None becomes '()')
|
||||
print("Retrieved project by ID 1: " + retrieved_p1);
|
||||
print("Retrieved project name: " + retrieved_p1.name);
|
||||
print("Retrieved project tags: " + retrieved_p1.tags);
|
||||
} else {
|
||||
print("Project with ID 1 not found.");
|
||||
}
|
||||
} catch (err) {
|
||||
print("Error retrieving project by ID 1: " + err);
|
||||
}
|
||||
|
||||
// Test non-existent project
|
||||
try {
|
||||
let non_existent_project = get_project_by_id(999);
|
||||
if non_existent_project != () {
|
||||
print("Error: Found non-existent project 999: " + non_existent_project);
|
||||
} else {
|
||||
print("Correctly did not find project with ID 999.");
|
||||
}
|
||||
} catch (err) {
|
||||
print("Error checking for non-existent project: " + err);
|
||||
}
|
||||
|
||||
print("--- Project Rhai Integration Test Complete (WASM) ---");
|
||||
"#;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Redirect Rhai's print to browser console
|
||||
engine.on_print(|text| {
|
||||
console::log_1(&text.into());
|
||||
});
|
||||
|
||||
// Attempt to initialize OurDB. Sled's behavior in WASM with paths is experimental.
|
||||
// It might work as an in-memory/temporary DB, or it might fail.
|
||||
// Using a specific path and reset=true.
|
||||
let db = match OurDB::new("/project_rhai_wasm_db", true) {
|
||||
Ok(db_instance) => Arc::new(db_instance),
|
||||
Err(e) => {
|
||||
let err_msg = format!("Failed to initialize OurDB for WASM: {}", e);
|
||||
console::error_1(&err_msg.into());
|
||||
return Err(JsValue::from_str(&err_msg));
|
||||
}
|
||||
};
|
||||
|
||||
register_projects_rhai_module(&mut engine, db);
|
||||
|
||||
let mut scope = Scope::new();
|
||||
|
||||
match engine.run_with_scope(&mut scope, script) {
|
||||
Ok(_) => console::log_1(&"Rhai script executed successfully in WASM!".into()),
|
||||
Err(e) => {
|
||||
let err_msg = format!("Rhai script execution failed in WASM: {}\nDetails: {:?}", e, e.to_string());
|
||||
console::error_1(&err_msg.into());
|
||||
return Err(JsValue::from_str(&e.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::models::Circle;
|
||||
use crate::db::{hero::OurDB, Collection, Db};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
@ -90,6 +90,17 @@ pub fn can_access_resource(
|
||||
object_id: u32,
|
||||
_object_type: &str,
|
||||
) -> bool {
|
||||
let circle = db
|
||||
.collection::<Circle>()
|
||||
.expect("Failed to get Circle collection")
|
||||
.get_all()
|
||||
.unwrap()[0].clone();
|
||||
|
||||
// Circle members can access everything
|
||||
if circle.members.contains(&public_key.to_string()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
println!("Checking access for public key: {}", public_key);
|
||||
|
||||
// get all access records for object
|
||||
@ -112,3 +123,21 @@ pub fn can_access_resource(
|
||||
// if circle_pk is in access records true
|
||||
return access_records.iter().any(|record| record.circle_pk == public_key)
|
||||
}
|
||||
|
||||
pub fn is_circle_member(
|
||||
db: Arc<OurDB>,
|
||||
public_key: &str,
|
||||
) -> bool {
|
||||
let circle = db
|
||||
.collection::<Circle>()
|
||||
.expect("Failed to get Circle collection")
|
||||
.get_all()
|
||||
.unwrap()[0].clone();
|
||||
|
||||
// Circle members can access everything
|
||||
if circle.members.contains(&public_key.to_string()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
// Export contact module
|
||||
pub mod access;
|
||||
pub mod rhai;
|
||||
|
||||
// Re-export contact, Group from the inner contact module (contact.rs) within src/models/contact/mod.rs
|
||||
pub use self::access::Access;
|
||||
pub use rhai::register_access_rhai_module;
|
||||
|
@ -1,238 +0,0 @@
|
||||
use crate::db::Db;
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::access::Access;
|
||||
type RhaiAccess = Access;
|
||||
use crate::db::Collection;
|
||||
use crate::db::hero::OurDB;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod rhai_access_module {
|
||||
// --- Access Functions ---
|
||||
#[rhai_fn(name = "new_access", return_raw)]
|
||||
pub fn new_access() -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
let access = Access::new();
|
||||
Ok(access)
|
||||
}
|
||||
|
||||
/// Sets the access name
|
||||
#[rhai_fn(name = "object_id", return_raw, global, pure)]
|
||||
pub fn access_object_id(
|
||||
access: &mut RhaiAccess,
|
||||
object_id: u32,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
// Take ownership of the access, apply the builder method, then put it back
|
||||
let owned_access = std::mem::replace(access, default_access);
|
||||
*access = owned_access.object_id(object_id);
|
||||
Ok(access.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "circle_pk", return_raw, global, pure)]
|
||||
pub fn access_circle_pk(
|
||||
access: &mut RhaiAccess,
|
||||
circle_pk: String,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
// Take ownership of the access, apply the builder method, then put it back
|
||||
let owned_access = std::mem::replace(access, default_access);
|
||||
*access = owned_access.circle_pk(circle_pk);
|
||||
Ok(access.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "group_id", return_raw, global, pure)]
|
||||
pub fn access_group_id(
|
||||
access: &mut RhaiAccess,
|
||||
group_id: u32,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
// Take ownership of the access, apply the builder method, then put it back
|
||||
let owned_access = std::mem::replace(access, default_access);
|
||||
*access = owned_access.group_id(group_id);
|
||||
Ok(access.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "contact_id", return_raw, global, pure)]
|
||||
pub fn access_contact_id(
|
||||
access: &mut RhaiAccess,
|
||||
contact_id: u32,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
// Take ownership of the access, apply the builder method, then put it back
|
||||
let owned_access = std::mem::replace(access, default_access);
|
||||
*access = owned_access.contact_id(contact_id);
|
||||
Ok(access.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "expires_at", return_raw, global, pure)]
|
||||
pub fn access_expires_at(
|
||||
access: &mut RhaiAccess,
|
||||
expires_at: Option<u64>,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
// Take ownership of the access, apply the builder method, then put it back
|
||||
let owned_access = std::mem::replace(access, default_access);
|
||||
*access = owned_access.expires_at(expires_at);
|
||||
Ok(access.clone())
|
||||
}
|
||||
|
||||
// Access Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_access_id(access: &mut RhaiAccess) -> i64 {
|
||||
access.base_data.id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "object_id", pure)]
|
||||
pub fn get_access_object_id(access: &mut RhaiAccess) -> i64 {
|
||||
access.object_id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "circle_pk", pure)]
|
||||
pub fn get_access_circle_pk(access: &mut RhaiAccess) -> String {
|
||||
access.circle_pk.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "group_id", pure)]
|
||||
pub fn get_access_group_id(access: &mut RhaiAccess) -> i64 {
|
||||
access.group_id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "contact_id", pure)]
|
||||
pub fn get_access_contact_id(access: &mut RhaiAccess) -> i64 {
|
||||
access.contact_id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "expires_at", pure)]
|
||||
pub fn get_access_expires_at(access: &mut RhaiAccess) -> i64 {
|
||||
access.expires_at.unwrap_or(0) as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_access_created_at(access: &mut RhaiAccess) -> i64 {
|
||||
access.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_access_modified_at(access: &mut RhaiAccess) -> i64 {
|
||||
access.base_data.modified_at
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_access_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Register the exported module globally
|
||||
let module = exported_module!(rhai_access_module);
|
||||
engine.register_global_module(module.into());
|
||||
|
||||
// Create a module for database functions
|
||||
let mut db_module = Module::new();
|
||||
|
||||
let db_clone_set_access = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"save_access",
|
||||
move |access: Access| -> Result<Access, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_access.set(&access).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_access: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated access with the correct ID
|
||||
Ok(result.1)
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_access = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"delete_access",
|
||||
move |access: Access| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_access
|
||||
.collection::<Access>()
|
||||
.expect("can open access collection")
|
||||
.delete_by_id(access.base_data.id)
|
||||
.expect("can delete event");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_access = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"get_access_by_id",
|
||||
move |id_i64: INT| -> Result<Access, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_access
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_access_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Access with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_accesss function to get all accesss
|
||||
let db_clone_list_accesss = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"list_accesss",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_accesss.collection::<Access>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get access collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let accesss = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all accesss: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for access in accesss {
|
||||
array.push(Dynamic::from(access));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
},
|
||||
);
|
||||
|
||||
// Register the database module globally
|
||||
engine.register_global_module(db_module.into());
|
||||
|
||||
println!("Successfully registered access Rhai module using export_module approach.");
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use heromodels_core::BaseModelDataOps;
|
||||
use heromodels_core::{BaseModelData, Index};
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
|
||||
use rhai::{CustomType, EvalAltResult, Position, TypeBuilder}; // For #[derive(CustomType)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// --- Enums ---
|
||||
@ -34,6 +34,26 @@ impl Default for BusinessType {
|
||||
}
|
||||
}
|
||||
|
||||
impl BusinessType {
|
||||
pub fn to_string(&self) -> String {
|
||||
format!("{:?}", self)
|
||||
}
|
||||
|
||||
pub fn from_string(s: &str) -> Result<Self, Box<EvalAltResult>> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"coop" => Ok(BusinessType::Coop),
|
||||
"single" => Ok(BusinessType::Single),
|
||||
"twin" => Ok(BusinessType::Twin),
|
||||
"starter" => Ok(BusinessType::Starter),
|
||||
"global" => Ok(BusinessType::Global),
|
||||
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Invalid business type: {}", s).into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Company Struct ---
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, CustomType, Default)] // Added CustomType
|
||||
|
@ -14,11 +14,4 @@ pub use product::{Product, ProductComponent, ProductStatus, ProductType};
|
||||
pub use shareholder::{Shareholder, ShareholderType};
|
||||
|
||||
pub mod sale;
|
||||
pub use sale::{Sale, SaleItem, SaleStatus};
|
||||
|
||||
// pub use user::{User}; // Assuming a simple User model for now
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
pub mod rhai;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use rhai::register_biz_rhai_module;
|
||||
pub use sale::{Sale, SaleItem, SaleStatus};
|
@ -1,4 +1,5 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -19,7 +20,7 @@ pub enum ProductStatus {
|
||||
}
|
||||
|
||||
// ProductComponent represents a component or sub-part of a product.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, CustomType)]
|
||||
pub struct ProductComponent {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
|
@ -1,794 +0,0 @@
|
||||
use crate::db::Collection; // For db.set and db.get_by_id
|
||||
use crate::db::Db;
|
||||
use crate::db::hero::OurDB;
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, INT, Module, Position};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::company::{BusinessType, Company, CompanyStatus};
|
||||
use crate::models::biz::product::{Product, ProductComponent, ProductStatus, ProductType};
|
||||
use crate::models::biz::sale::{Sale, SaleItem, SaleStatus};
|
||||
use crate::models::biz::shareholder::{Shareholder, ShareholderType};
|
||||
use heromodels_core::Model;
|
||||
|
||||
type RhaiCompany = Company;
|
||||
type RhaiShareholder = Shareholder;
|
||||
type RhaiProduct = Product;
|
||||
type RhaiProductComponent = ProductComponent;
|
||||
type RhaiSale = Sale;
|
||||
type RhaiSaleItem = SaleItem;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod rhai_biz_module {
|
||||
// --- Company Functions ---
|
||||
#[rhai_fn(name = "new_company")]
|
||||
pub fn new_company() -> RhaiCompany {
|
||||
Company::new()
|
||||
}
|
||||
|
||||
// Company builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn company_name(
|
||||
company: &mut RhaiCompany,
|
||||
name: String,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.name(name);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "fiscal_year_end", return_raw, global, pure)]
|
||||
pub fn company_fiscal_year_end(
|
||||
company: &mut RhaiCompany,
|
||||
fiscal_year_end: String,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.fiscal_year_end(fiscal_year_end);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "registration_number", return_raw, global, pure)]
|
||||
pub fn company_registration_number(
|
||||
company: &mut RhaiCompany,
|
||||
reg_num: String,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.registration_number(reg_num);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "incorporation_date", return_raw, global, pure)]
|
||||
pub fn company_incorporation_date(
|
||||
company: &mut RhaiCompany,
|
||||
date: i64,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.incorporation_date(date);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn company_status(
|
||||
company: &mut RhaiCompany,
|
||||
status: CompanyStatus,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.status(status);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "business_type", return_raw, global, pure)]
|
||||
pub fn company_business_type(
|
||||
company: &mut RhaiCompany,
|
||||
business_type: BusinessType,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.business_type(business_type);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
// Company getters
|
||||
#[rhai_fn(name = "get_company_id")]
|
||||
pub fn get_company_id(company: &mut RhaiCompany) -> i64 {
|
||||
company.get_id() as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_company_name")]
|
||||
pub fn get_company_name(company: &mut RhaiCompany) -> String {
|
||||
company.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_company_created_at")]
|
||||
pub fn get_company_created_at(company: &mut RhaiCompany) -> i64 {
|
||||
company.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_company_modified_at")]
|
||||
pub fn get_company_modified_at(company: &mut RhaiCompany) -> i64 {
|
||||
company.base_data.modified_at
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_company_registration_number")]
|
||||
pub fn get_company_registration_number(company: &mut RhaiCompany) -> String {
|
||||
company.registration_number.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_company_fiscal_year_end")]
|
||||
pub fn get_company_fiscal_year_end(company: &mut RhaiCompany) -> String {
|
||||
company.fiscal_year_end.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_company_incorporation_date")]
|
||||
pub fn get_company_incorporation_date(company: &mut RhaiCompany) -> i64 {
|
||||
company.incorporation_date
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_company_status")]
|
||||
pub fn get_company_status(company: &mut RhaiCompany) -> CompanyStatus {
|
||||
company.status.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_company_business_type")]
|
||||
pub fn get_company_business_type(company: &mut RhaiCompany) -> BusinessType {
|
||||
company.business_type.clone()
|
||||
}
|
||||
|
||||
// --- Shareholder Functions ---
|
||||
#[rhai_fn(name = "new_shareholder")]
|
||||
pub fn new_shareholder() -> RhaiShareholder {
|
||||
Shareholder::new()
|
||||
}
|
||||
|
||||
// Shareholder builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn shareholder_name(
|
||||
shareholder: &mut RhaiShareholder,
|
||||
name: String,
|
||||
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
let owned_shareholder = mem::take(shareholder);
|
||||
*shareholder = owned_shareholder.name(name);
|
||||
Ok(shareholder.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "company_id", return_raw, global, pure)]
|
||||
pub fn shareholder_company_id(
|
||||
shareholder: &mut RhaiShareholder,
|
||||
company_id: i64,
|
||||
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
let company_id_u32 = id_from_i64_to_u32(company_id)?;
|
||||
let owned_shareholder = mem::take(shareholder);
|
||||
*shareholder = owned_shareholder.company_id(company_id_u32);
|
||||
Ok(shareholder.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "share_count", return_raw, global, pure)]
|
||||
pub fn shareholder_share_count(
|
||||
shareholder: &mut RhaiShareholder,
|
||||
share_count: f64,
|
||||
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
shareholder.shares = share_count;
|
||||
Ok(shareholder.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "type_", return_raw, global, pure)]
|
||||
pub fn shareholder_type(
|
||||
shareholder: &mut RhaiShareholder,
|
||||
type_: ShareholderType,
|
||||
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
let owned_shareholder = mem::take(shareholder);
|
||||
*shareholder = owned_shareholder.type_(type_);
|
||||
Ok(shareholder.clone())
|
||||
}
|
||||
|
||||
// Shareholder getters
|
||||
#[rhai_fn(name = "get_shareholder_id")]
|
||||
pub fn get_shareholder_id(shareholder: &mut RhaiShareholder) -> i64 {
|
||||
shareholder.get_id() as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_shareholder_name")]
|
||||
pub fn get_shareholder_name(shareholder: &mut RhaiShareholder) -> String {
|
||||
shareholder.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_shareholder_company_id")]
|
||||
pub fn get_shareholder_company_id(shareholder: &mut RhaiShareholder) -> i64 {
|
||||
shareholder.company_id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_shareholder_share_count")]
|
||||
pub fn get_shareholder_share_count(shareholder: &mut RhaiShareholder) -> i64 {
|
||||
shareholder.shares as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_shareholder_type")]
|
||||
pub fn get_shareholder_type(shareholder: &mut RhaiShareholder) -> ShareholderType {
|
||||
shareholder.type_.clone()
|
||||
}
|
||||
|
||||
// --- ProductComponent Functions ---
|
||||
#[rhai_fn(name = "new_product_component")]
|
||||
pub fn new_product_component() -> RhaiProductComponent {
|
||||
ProductComponent::new()
|
||||
}
|
||||
|
||||
// ProductComponent builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn product_component_name(
|
||||
component: &mut RhaiProductComponent,
|
||||
name: String,
|
||||
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
let owned_component = mem::take(component);
|
||||
*component = owned_component.name(name);
|
||||
Ok(component.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn product_component_description(
|
||||
component: &mut RhaiProductComponent,
|
||||
description: String,
|
||||
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
let owned_component = mem::take(component);
|
||||
*component = owned_component.description(description);
|
||||
Ok(component.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "quantity", return_raw, global, pure)]
|
||||
pub fn product_component_quantity(
|
||||
component: &mut RhaiProductComponent,
|
||||
quantity: i64,
|
||||
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
let owned_component = mem::take(component);
|
||||
*component = owned_component.quantity(quantity as u32);
|
||||
Ok(component.clone())
|
||||
}
|
||||
|
||||
// ProductComponent getters
|
||||
#[rhai_fn(name = "get_product_component_name")]
|
||||
pub fn get_product_component_name(component: &mut RhaiProductComponent) -> String {
|
||||
component.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_component_description")]
|
||||
pub fn get_product_component_description(component: &mut RhaiProductComponent) -> String {
|
||||
component.description.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_component_quantity")]
|
||||
pub fn get_product_component_quantity(component: &mut RhaiProductComponent) -> i64 {
|
||||
component.quantity as i64
|
||||
}
|
||||
|
||||
// --- Product Functions ---
|
||||
#[rhai_fn(name = "new_product")]
|
||||
pub fn new_product() -> RhaiProduct {
|
||||
Product::new()
|
||||
}
|
||||
|
||||
// Product builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn product_name(
|
||||
product: &mut RhaiProduct,
|
||||
name: String,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.name(name);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn product_description(
|
||||
product: &mut RhaiProduct,
|
||||
description: String,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.description(description);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "price", return_raw, global, pure)]
|
||||
pub fn product_price(
|
||||
product: &mut RhaiProduct,
|
||||
price: f64,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.price(price);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "type_", return_raw, global, pure)]
|
||||
pub fn product_type(
|
||||
product: &mut RhaiProduct,
|
||||
type_: ProductType,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.type_(type_);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "category", return_raw, global, pure)]
|
||||
pub fn product_category(
|
||||
product: &mut RhaiProduct,
|
||||
category: String,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.category(category);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn product_status(
|
||||
product: &mut RhaiProduct,
|
||||
status: ProductStatus,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.status(status);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "max_amount", return_raw, global, pure)]
|
||||
pub fn product_max_amount(
|
||||
product: &mut RhaiProduct,
|
||||
max_amount: i64,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.max_amount(max_amount as u16);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "purchase_till", return_raw, global, pure)]
|
||||
pub fn product_purchase_till(
|
||||
product: &mut RhaiProduct,
|
||||
purchase_till: i64,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.purchase_till(purchase_till);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "active_till", return_raw, global, pure)]
|
||||
pub fn product_active_till(
|
||||
product: &mut RhaiProduct,
|
||||
active_till: i64,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.active_till(active_till);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "add_component", return_raw, global, pure)]
|
||||
pub fn product_add_component(
|
||||
product: &mut RhaiProduct,
|
||||
component: RhaiProductComponent,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.add_component(component);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "components", return_raw, global, pure)]
|
||||
pub fn product_components(
|
||||
product: &mut RhaiProduct,
|
||||
components: Vec<RhaiProductComponent>,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.components(components);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
// Product getters
|
||||
#[rhai_fn(name = "get_product_id")]
|
||||
pub fn get_product_id(product: &mut RhaiProduct) -> i64 {
|
||||
product.get_id() as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_name")]
|
||||
pub fn get_product_name(product: &mut RhaiProduct) -> String {
|
||||
product.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_description")]
|
||||
pub fn get_product_description(product: &mut RhaiProduct) -> String {
|
||||
product.description.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_price")]
|
||||
pub fn get_product_price(product: &mut RhaiProduct) -> f64 {
|
||||
product.price
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_type")]
|
||||
pub fn get_product_type(product: &mut RhaiProduct) -> ProductType {
|
||||
product.type_.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_category")]
|
||||
pub fn get_product_category(product: &mut RhaiProduct) -> String {
|
||||
product.category.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_status")]
|
||||
pub fn get_product_status(product: &mut RhaiProduct) -> ProductStatus {
|
||||
product.status.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_max_amount")]
|
||||
pub fn get_product_max_amount(product: &mut RhaiProduct) -> i64 {
|
||||
product.max_amount as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_purchase_till")]
|
||||
pub fn get_product_purchase_till(product: &mut RhaiProduct) -> i64 {
|
||||
product.purchase_till
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_active_till")]
|
||||
pub fn get_product_active_till(product: &mut RhaiProduct) -> i64 {
|
||||
product.active_till
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_components")]
|
||||
pub fn get_product_components(product: &mut RhaiProduct) -> Vec<RhaiProductComponent> {
|
||||
product.components.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_created_at")]
|
||||
pub fn get_product_created_at(product: &mut RhaiProduct) -> i64 {
|
||||
product.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_modified_at")]
|
||||
pub fn get_product_modified_at(product: &mut RhaiProduct) -> i64 {
|
||||
product.base_data.modified_at
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_product_comments")]
|
||||
pub fn get_product_comments(product: &mut RhaiProduct) -> Vec<i64> {
|
||||
product
|
||||
.base_data
|
||||
.comments
|
||||
.iter()
|
||||
.map(|&id| id as i64)
|
||||
.collect()
|
||||
}
|
||||
|
||||
// --- SaleItem Functions ---
|
||||
#[rhai_fn(name = "new_sale_item")]
|
||||
pub fn new_sale_item() -> RhaiSaleItem {
|
||||
SaleItem::new()
|
||||
}
|
||||
|
||||
// SaleItem builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn sale_item_name(
|
||||
item: &mut RhaiSaleItem,
|
||||
name: String,
|
||||
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
let owned_item = mem::take(item);
|
||||
*item = owned_item.name(name);
|
||||
Ok(item.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "price", return_raw, global, pure)]
|
||||
pub fn sale_item_price(
|
||||
item: &mut RhaiSaleItem,
|
||||
price: f64,
|
||||
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
item.unit_price = price;
|
||||
Ok(item.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "quantity", return_raw, global, pure)]
|
||||
pub fn sale_item_quantity(
|
||||
item: &mut RhaiSaleItem,
|
||||
quantity: i64,
|
||||
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
let owned_item = mem::take(item);
|
||||
*item = owned_item.quantity(quantity.try_into().unwrap());
|
||||
Ok(item.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "product_id", return_raw, global, pure)]
|
||||
pub fn sale_item_product_id(
|
||||
item: &mut RhaiSaleItem,
|
||||
product_id: i64,
|
||||
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
let product_id_u32 = id_from_i64_to_u32(product_id)?;
|
||||
let owned_item = mem::take(item);
|
||||
*item = owned_item.product_id(product_id_u32);
|
||||
Ok(item.clone())
|
||||
}
|
||||
|
||||
// SaleItem getters
|
||||
#[rhai_fn(name = "get_sale_item_name")]
|
||||
pub fn get_sale_item_name(item: &mut RhaiSaleItem) -> String {
|
||||
item.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_item_price")]
|
||||
pub fn get_sale_item_price(item: &mut RhaiSaleItem) -> f64 {
|
||||
item.unit_price
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_item_quantity")]
|
||||
pub fn get_sale_item_quantity(item: &mut RhaiSaleItem) -> i64 {
|
||||
item.quantity as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_item_product_id")]
|
||||
pub fn get_sale_item_product_id(item: &mut RhaiSaleItem) -> i64 {
|
||||
item.product_id as i64
|
||||
}
|
||||
|
||||
// --- Sale Functions ---
|
||||
#[rhai_fn(name = "new_sale")]
|
||||
pub fn new_sale() -> RhaiSale {
|
||||
Sale::new()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "transaction_id", return_raw, global, pure)]
|
||||
pub fn sale_transaction_id(
|
||||
sale: &mut RhaiSale,
|
||||
transaction_id: u32,
|
||||
) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
sale.transaction_id = transaction_id;
|
||||
Ok(sale.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn sale_status(
|
||||
sale: &mut RhaiSale,
|
||||
status: SaleStatus,
|
||||
) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
let owned_sale = mem::take(sale);
|
||||
*sale = owned_sale.status(status);
|
||||
Ok(sale.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "add_item", return_raw, global, pure)]
|
||||
pub fn sale_add_item(
|
||||
sale: &mut RhaiSale,
|
||||
item: RhaiSaleItem,
|
||||
) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
let owned_sale = mem::take(sale);
|
||||
*sale = owned_sale.add_item(item);
|
||||
Ok(sale.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "items", return_raw, global, pure)]
|
||||
pub fn sale_items(
|
||||
sale: &mut RhaiSale,
|
||||
items: Vec<RhaiSaleItem>,
|
||||
) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
let owned_sale = mem::take(sale);
|
||||
*sale = owned_sale.items(items);
|
||||
Ok(sale.clone())
|
||||
}
|
||||
|
||||
// Sale getters
|
||||
#[rhai_fn(name = "get_sale_id")]
|
||||
pub fn get_sale_id(sale: &mut RhaiSale) -> i64 {
|
||||
sale.get_id() as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_transaction_id")]
|
||||
pub fn get_sale_transaction_id(sale: &mut RhaiSale) -> u32 {
|
||||
sale.transaction_id
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_status")]
|
||||
pub fn get_sale_status(sale: &mut RhaiSale) -> SaleStatus {
|
||||
sale.status.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_items")]
|
||||
pub fn get_sale_items(sale: &mut RhaiSale) -> Vec<RhaiSaleItem> {
|
||||
sale.items.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_created_at")]
|
||||
pub fn get_sale_created_at(sale: &mut RhaiSale) -> i64 {
|
||||
sale.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_modified_at")]
|
||||
pub fn get_sale_modified_at(sale: &mut RhaiSale) -> i64 {
|
||||
sale.base_data.modified_at
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_sale_comments")]
|
||||
pub fn get_sale_comments(sale: &mut RhaiSale) -> Vec<i64> {
|
||||
sale.base_data
|
||||
.comments
|
||||
.iter()
|
||||
.map(|&id| id as i64)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_biz_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Register the exported module globally
|
||||
let module = exported_module!(rhai_biz_module);
|
||||
engine.register_global_module(module.into());
|
||||
|
||||
// Create a new module for database operations
|
||||
let mut db_module = Module::new();
|
||||
|
||||
// Database operations will obtain fresh collection handles directly.
|
||||
|
||||
// Add database functions for Company
|
||||
let db_for_set_company = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"set_company",
|
||||
move |company: Company| -> Result<INT, Box<EvalAltResult>> {
|
||||
let company_collection_set = db_for_set_company
|
||||
.collection::<Company>()
|
||||
.expect("Failed to get company collection for set in closure");
|
||||
company_collection_set
|
||||
.set(&company)
|
||||
.map(|(id_val, _)| id_val as INT)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save company: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_for_get_company = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_company_by_id",
|
||||
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let company_collection_get = db_for_get_company
|
||||
.collection::<Company>()
|
||||
.expect("Failed to get company collection for get in closure");
|
||||
let id_u32 = id_from_i64_to_u32(id)?;
|
||||
company_collection_get
|
||||
.get_by_id(id_u32)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get company with id {}: {:?}", id, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add database functions for Shareholder
|
||||
let db_for_set_shareholder = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"set_shareholder",
|
||||
move |shareholder: Shareholder| -> Result<INT, Box<EvalAltResult>> {
|
||||
let shareholder_collection_set = db_for_set_shareholder
|
||||
.collection::<Shareholder>()
|
||||
.expect("Failed to get shareholder collection for set in closure");
|
||||
shareholder_collection_set
|
||||
.set(&shareholder)
|
||||
.map(|(id_val, _)| id_val as INT)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save shareholder: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_for_get_shareholder = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_shareholder_by_id",
|
||||
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let shareholder_collection_get = db_for_get_shareholder
|
||||
.collection::<Shareholder>()
|
||||
.expect("Failed to get shareholder collection for get in closure");
|
||||
let id_u32 = id_from_i64_to_u32(id)?;
|
||||
shareholder_collection_get
|
||||
.get_by_id(id_u32)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get shareholder with id {}: {:?}", id, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add database functions for Product
|
||||
let db_for_set_product = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"set_product",
|
||||
move |product: Product| -> Result<INT, Box<EvalAltResult>> {
|
||||
let product_collection_set = db_for_set_product
|
||||
.collection::<Product>()
|
||||
.expect("Failed to get product collection for set in closure");
|
||||
product_collection_set
|
||||
.set(&product)
|
||||
.map(|(id_val, _)| id_val as INT)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save product: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_for_get_product = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_product_by_id",
|
||||
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let product_collection_get = db_for_get_product
|
||||
.collection::<Product>()
|
||||
.expect("Failed to get product collection for get in closure");
|
||||
let id_u32 = id_from_i64_to_u32(id)?;
|
||||
product_collection_get
|
||||
.get_by_id(id_u32)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get product with id {}: {:?}", id, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add database functions for Sale
|
||||
let db_for_set_sale = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"set_sale",
|
||||
move |sale: Sale| -> Result<INT, Box<EvalAltResult>> {
|
||||
let sale_collection_set = db_for_set_sale
|
||||
.collection::<Sale>()
|
||||
.expect("Failed to get sale collection for set in closure");
|
||||
sale_collection_set
|
||||
.set(&sale)
|
||||
.map(|(id_val, _)| id_val as INT)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save sale: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_for_get_sale = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_sale_by_id",
|
||||
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let sale_collection_get = db_for_get_sale
|
||||
.collection::<Sale>()
|
||||
.expect("Failed to get sale collection for get in closure");
|
||||
let id_u32 = id_from_i64_to_u32(id)?;
|
||||
sale_collection_get
|
||||
.get_by_id(id_u32)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get sale with id {}: {:?}", id, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Register the database module globally
|
||||
engine.register_global_module(db_module.into());
|
||||
|
||||
println!("Successfully registered biz Rhai module using export_module approach.");
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
use heromodels_core::{BaseModelData, BaseModelDataOps, Model};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents the status of a sale.
|
||||
@ -16,7 +17,7 @@ impl Default for SaleStatus {
|
||||
}
|
||||
|
||||
/// Represents an individual item within a Sale.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, CustomType)]
|
||||
pub struct SaleItem {
|
||||
pub product_id: u32,
|
||||
pub name: String, // Denormalized product name at time of sale
|
||||
@ -72,7 +73,7 @@ impl SaleItem {
|
||||
}
|
||||
|
||||
/// Represents a sale of products or services.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, CustomType)]
|
||||
pub struct Sale {
|
||||
pub base_data: BaseModelData,
|
||||
pub company_id: u32,
|
||||
|
@ -1,7 +1,5 @@
|
||||
// Export calendar module
|
||||
pub mod calendar;
|
||||
pub mod rhai;
|
||||
|
||||
// Re-export Calendar, Event, Attendee, and AttendanceStatus from the inner calendar module (calendar.rs) within src/models/calendar/mod.rs
|
||||
pub use self::calendar::{AttendanceStatus, Attendee, Calendar, Event};
|
||||
pub use rhai::register_calendar_rhai_module;
|
||||
|
@ -1,484 +0,0 @@
|
||||
use crate::db::Db;
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::calendar::{AttendanceStatus, Attendee, Calendar, Event};
|
||||
type RhaiEvent = Event;
|
||||
type RhaiAttendee = Attendee;
|
||||
type RhaiCalendar = Calendar;
|
||||
use crate::db::Collection;
|
||||
use crate::db::hero::OurDB;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod rhai_calendar_module {
|
||||
// --- Event Functions ---
|
||||
#[rhai_fn(name = "new_event")]
|
||||
pub fn new_event() -> RhaiEvent {
|
||||
Event::new()
|
||||
}
|
||||
|
||||
/// Sets the event title
|
||||
#[rhai_fn(name = "title", return_raw, global, pure)]
|
||||
pub fn event_title(
|
||||
event: &mut RhaiEvent,
|
||||
title: String,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.title(title);
|
||||
Ok(event.clone())
|
||||
}
|
||||
|
||||
/// Sets the event description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn event_description(
|
||||
event: &mut RhaiEvent,
|
||||
description: String,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.description(description);
|
||||
Ok(event.clone())
|
||||
}
|
||||
|
||||
/// Sets the event location
|
||||
#[rhai_fn(name = "location", return_raw, global, pure)]
|
||||
pub fn event_location(
|
||||
event: &mut RhaiEvent,
|
||||
location: String,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.location(location);
|
||||
Ok(event.clone())
|
||||
}
|
||||
|
||||
/// Adds an attendee to the event
|
||||
#[rhai_fn(name = "add_attendee", return_raw, global, pure)]
|
||||
pub fn event_add_attendee(
|
||||
event: &mut RhaiEvent,
|
||||
attendee: RhaiAttendee,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
// Use take to get ownership of the event
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.add_attendee(attendee);
|
||||
Ok(event.clone())
|
||||
}
|
||||
|
||||
/// Reschedules the event with new start and end times
|
||||
#[rhai_fn(name = "reschedule", return_raw, global, pure)]
|
||||
pub fn event_reschedule(
|
||||
event: &mut RhaiEvent,
|
||||
new_start_time: i64,
|
||||
new_end_time: i64,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
// Validate timestamps
|
||||
if new_end_time <= new_start_time {
|
||||
return Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
"End time must be after start time".into(),
|
||||
Position::NONE,
|
||||
)));
|
||||
}
|
||||
|
||||
// Use take to get ownership of the event
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.reschedule(new_start_time, new_end_time);
|
||||
Ok(event.clone())
|
||||
}
|
||||
|
||||
/// Updates an attendee's status in the event
|
||||
#[rhai_fn(name = "update_attendee_status", return_raw, global, pure)]
|
||||
pub fn event_update_attendee_status(
|
||||
event: &mut RhaiEvent,
|
||||
contact_id: i64,
|
||||
status_str: String,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
let status_enum = AttendanceStatus::from_string(&status_str)
|
||||
.map_err(|_| Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Invalid attendance status: '{}'. Expected one of: Pending, Accepted, Declined, Tentative", status_str).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
|
||||
// Use take to get ownership of the event
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.update_attendee_status(id_from_i64_to_u32(contact_id)?, status_enum);
|
||||
Ok(event.clone())
|
||||
}
|
||||
|
||||
// Event Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_event_id(event: &mut RhaiEvent) -> i64 {
|
||||
event.base_data.id as i64
|
||||
}
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_event_created_at(event: &mut RhaiEvent) -> i64 {
|
||||
event.base_data.created_at
|
||||
}
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_event_modified_at(event: &mut RhaiEvent) -> i64 {
|
||||
event.base_data.modified_at
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "title", pure)]
|
||||
pub fn get_event_title(event: &mut RhaiEvent) -> String {
|
||||
event.title.clone()
|
||||
}
|
||||
#[rhai_fn(get = "description", pure)]
|
||||
pub fn get_event_description(event: &mut RhaiEvent) -> Option<String> {
|
||||
event.description.clone()
|
||||
}
|
||||
#[rhai_fn(get = "start_time", pure)]
|
||||
pub fn get_event_start_time(event: &mut RhaiEvent) -> i64 {
|
||||
event.start_time
|
||||
}
|
||||
#[rhai_fn(get = "end_time", pure)]
|
||||
pub fn get_event_end_time(event: &mut RhaiEvent) -> i64 {
|
||||
event.end_time
|
||||
}
|
||||
#[rhai_fn(get = "location", pure)]
|
||||
pub fn get_event_location(event: &mut RhaiEvent) -> Option<String> {
|
||||
event.location.clone()
|
||||
}
|
||||
#[rhai_fn(get = "attendees", pure)]
|
||||
pub fn get_event_attendees(event: &mut RhaiEvent) -> Vec<RhaiAttendee> {
|
||||
event.attendees.clone()
|
||||
}
|
||||
|
||||
// --- Attendee Functions ---
|
||||
#[rhai_fn(name = "new_attendee")]
|
||||
pub fn new_attendee() -> RhaiAttendee {
|
||||
Attendee::new(0) // Default contact_id, will be set via builder
|
||||
}
|
||||
|
||||
/// Sets the contact ID for an attendee
|
||||
#[rhai_fn(name = "with_contact_id", return_raw, global, pure)]
|
||||
pub fn attendee_with_contact_id(
|
||||
attendee: &mut RhaiAttendee,
|
||||
contact_id: i64,
|
||||
) -> Result<RhaiAttendee, Box<EvalAltResult>> {
|
||||
let new_contact_id = id_from_i64_to_u32(contact_id).unwrap_or(0);
|
||||
let owned_attendee = mem::replace(attendee, Attendee::new(0));
|
||||
*attendee = Attendee::new(new_contact_id);
|
||||
attendee.status = owned_attendee.status;
|
||||
Ok(attendee.clone())
|
||||
}
|
||||
|
||||
/// Sets the status for an attendee
|
||||
#[rhai_fn(name = "with_status", return_raw, global, pure)]
|
||||
pub fn attendee_with_status(
|
||||
attendee: &mut RhaiAttendee,
|
||||
status_str: String,
|
||||
) -> Result<RhaiAttendee, Box<EvalAltResult>> {
|
||||
let status_enum = AttendanceStatus::from_string(&status_str)
|
||||
.map_err(|_| Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Invalid attendance status: '{}'. Expected one of: Accepted, Declined, Tentative, NoResponse", status_str).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
|
||||
let owned_attendee = mem::replace(attendee, Attendee::new(0));
|
||||
*attendee = owned_attendee.status(status_enum);
|
||||
Ok(attendee.clone())
|
||||
}
|
||||
|
||||
// We now use with_status instead of update_attendee_status for consistency
|
||||
|
||||
// Attendee Getters
|
||||
#[rhai_fn(get = "contact_id", pure)]
|
||||
pub fn get_attendee_contact_id(attendee: &mut RhaiAttendee) -> i64 {
|
||||
attendee.contact_id as i64
|
||||
}
|
||||
#[rhai_fn(get = "status", pure)]
|
||||
pub fn get_attendee_status(attendee: &mut RhaiAttendee) -> String {
|
||||
attendee.status.to_string()
|
||||
}
|
||||
|
||||
// --- Calendar Functions ---
|
||||
#[rhai_fn(name = "new_calendar", return_raw)]
|
||||
pub fn new_calendar() -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
let calendar = Calendar::new(None, "");
|
||||
Ok(calendar)
|
||||
}
|
||||
|
||||
/// Sets the calendar name
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn calendar_name(
|
||||
calendar: &mut RhaiCalendar,
|
||||
name: String,
|
||||
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
// Create a default Calendar to replace the taken one
|
||||
let default_calendar = Calendar::new(None, "");
|
||||
|
||||
// Take ownership of the calendar, apply the builder method, then put it back
|
||||
let owned_calendar = std::mem::replace(calendar, default_calendar);
|
||||
*calendar = Calendar::new(Some(owned_calendar.base_data.id), name);
|
||||
Ok(calendar.clone())
|
||||
}
|
||||
|
||||
/// Sets the calendar description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn calendar_description(
|
||||
calendar: &mut RhaiCalendar,
|
||||
description: String,
|
||||
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
// Create a default Calendar to replace the taken one
|
||||
let default_calendar = Calendar::new(None, "");
|
||||
|
||||
// Take ownership of the calendar, apply the builder method, then put it back
|
||||
let owned_calendar = std::mem::replace(calendar, default_calendar);
|
||||
let updated_calendar = owned_calendar.description(description);
|
||||
*calendar = updated_calendar.clone();
|
||||
Ok(updated_calendar)
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "add_event_to_calendar", return_raw, global, pure)]
|
||||
pub fn calendar_add_event(
|
||||
calendar: &mut RhaiCalendar,
|
||||
event: RhaiEvent,
|
||||
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
// Create a default Calendar to replace the taken one
|
||||
let default_calendar = Calendar::new(None, "");
|
||||
|
||||
// Take ownership of the calendar, apply the builder method, then put it back
|
||||
let owned_calendar = std::mem::replace(calendar, default_calendar);
|
||||
*calendar = owned_calendar.add_event(event.base_data.id as i64);
|
||||
Ok(calendar.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "remove_event_from_calendar", return_raw)]
|
||||
pub fn calendar_remove_event(
|
||||
calendar: &mut RhaiCalendar,
|
||||
event_id: i64,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
// Create a default Calendar to replace the taken one
|
||||
let default_calendar = Calendar::new(None, "");
|
||||
|
||||
// Take ownership of the calendar, apply the builder method, then put it back
|
||||
let owned_calendar = std::mem::replace(calendar, default_calendar);
|
||||
*calendar = owned_calendar.remove_event(id_from_i64_to_u32(event_id)? as i64);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Calendar Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_calendar_id(calendar: &mut RhaiCalendar) -> i64 {
|
||||
calendar.base_data.id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "name", pure)]
|
||||
pub fn get_calendar_name(calendar: &mut RhaiCalendar) -> String {
|
||||
calendar.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_calendar_created_at(calendar: &mut RhaiCalendar) -> i64 {
|
||||
calendar.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_calendar_modified_at(calendar: &mut RhaiCalendar) -> i64 {
|
||||
calendar.base_data.modified_at
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "events", pure)]
|
||||
pub fn get_calendar_events(calendar: &mut RhaiCalendar) -> Vec<i64> {
|
||||
calendar.events.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "description", pure)]
|
||||
pub fn get_calendar_description(calendar: &mut RhaiCalendar) -> Option<String> {
|
||||
calendar.description.clone()
|
||||
}
|
||||
|
||||
// Calendar doesn't have an owner_id field in the current implementation
|
||||
// pub fn get_calendar_owner_id(calendar: &mut RhaiCalendar) -> i64 { calendar.owner_id as i64 }
|
||||
}
|
||||
|
||||
pub fn register_calendar_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Register the exported module globally
|
||||
let module = exported_module!(rhai_calendar_module);
|
||||
engine.register_global_module(module.into());
|
||||
|
||||
// Create a module for database functions
|
||||
let mut db_module = Module::new();
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_set_event = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"save_event",
|
||||
move |event: Event| -> Result<Event, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_event.set(&event).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_event: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result.1)
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_event = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"delete_event",
|
||||
move |event: Event| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_event
|
||||
.collection::<Event>()
|
||||
.expect("can open event collection")
|
||||
.delete_by_id(event.base_data.id)
|
||||
.expect("can delete event");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_event = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"get_event_by_id",
|
||||
move |id_i64: INT| -> Result<Event, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_event
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_event_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Event with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_set_calendar = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"save_calendar",
|
||||
move |calendar: Calendar| -> Result<Calendar, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_calendar.set(&calendar).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_calendar: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated calendar with the correct ID
|
||||
Ok(result.1)
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_calendar = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"delete_calendar",
|
||||
move |calendar: Calendar| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_calendar
|
||||
.collection::<Calendar>()
|
||||
.expect("can open calendar collection")
|
||||
.delete_by_id(calendar.base_data.id)
|
||||
.expect("can delete event");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_calendar = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"get_calendar_by_id",
|
||||
move |id_i64: INT| -> Result<Calendar, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_calendar
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_calendar_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Calendar with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_calendars function to get all calendars
|
||||
let db_clone_list_calendars = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"list_calendars",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_calendars
|
||||
.collection::<Calendar>()
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get calendar collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let calendars = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all calendars: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for calendar in calendars {
|
||||
array.push(Dynamic::from(calendar));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_events function to get all events
|
||||
let db_clone_list_events = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"list_events",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_events.collection::<Event>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get event collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let events = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all events: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for event in events {
|
||||
array.push(Dynamic::from(event));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
},
|
||||
);
|
||||
|
||||
// Register the database module globally
|
||||
engine.register_global_module(db_module.into());
|
||||
|
||||
println!("Successfully registered calendar Rhai module using export_module approach.");
|
||||
}
|
@ -4,7 +4,7 @@ use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents the visual theme for a circle.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default)]
|
||||
pub struct ThemeData {
|
||||
pub primary_color: String,
|
||||
pub background_color: String,
|
||||
@ -15,20 +15,6 @@ pub struct ThemeData {
|
||||
pub nav_timeline_visible: bool,
|
||||
}
|
||||
|
||||
impl Default for ThemeData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
primary_color: "#3b82f6".to_string(),
|
||||
background_color: "#0a0a0a".to_string(),
|
||||
background_pattern: "none".to_string(),
|
||||
logo_symbol: "◯".to_string(),
|
||||
logo_url: "".to_string(),
|
||||
nav_dashboard_visible: true,
|
||||
nav_timeline_visible: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an event in a calendar
|
||||
#[model]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default)]
|
||||
|
@ -21,6 +21,21 @@ pub struct Contact {
|
||||
pub circle: String,
|
||||
}
|
||||
|
||||
impl Default for Contact {
|
||||
fn default() -> Self {
|
||||
Contact {
|
||||
base_data: BaseModelData::new(),
|
||||
name: String::new(),
|
||||
description: None,
|
||||
address: String::new(),
|
||||
phone: String::new(),
|
||||
email: String::new(),
|
||||
notes: None,
|
||||
circle: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Contact {
|
||||
pub fn new() -> Self {
|
||||
Contact {
|
||||
|
@ -1,7 +1,5 @@
|
||||
// Export contact module
|
||||
pub mod contact;
|
||||
pub mod rhai;
|
||||
|
||||
// Re-export contact, Group from the inner contact module (contact.rs) within src/models/contact/mod.rs
|
||||
pub use self::contact::{Contact, Group};
|
||||
pub use rhai::register_contact_rhai_module;
|
||||
|
@ -1,336 +0,0 @@
|
||||
use crate::db::Db;
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::contact::{Contact, Group};
|
||||
type RhaiGroup = Group;
|
||||
type RhaiContact = Contact;
|
||||
use crate::db::Collection;
|
||||
use crate::db::hero::OurDB;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod rhai_contact_module {
|
||||
// --- Event Functions ---
|
||||
#[rhai_fn(name = "new_group")]
|
||||
pub fn new_group() -> RhaiGroup {
|
||||
Group::new()
|
||||
}
|
||||
|
||||
/// Sets the event title
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn group_name(
|
||||
group: &mut RhaiGroup,
|
||||
name: String,
|
||||
) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
let owned_group = mem::take(group);
|
||||
*group = owned_group.name(name);
|
||||
Ok(group.clone())
|
||||
}
|
||||
|
||||
/// Sets the event description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn group_description(
|
||||
group: &mut RhaiGroup,
|
||||
description: String,
|
||||
) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
let owned_group = mem::take(group);
|
||||
*group = owned_group.description(description);
|
||||
Ok(group.clone())
|
||||
}
|
||||
|
||||
/// Adds an attendee to the event
|
||||
#[rhai_fn(name = "add_contact", return_raw, global, pure)]
|
||||
pub fn group_add_contact(
|
||||
group: &mut RhaiGroup,
|
||||
contact_id: i64,
|
||||
) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
// Use take to get ownership of the event
|
||||
let owned_group = mem::take(group);
|
||||
*group = owned_group.add_contact(contact_id as u32);
|
||||
Ok(group.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "contacts", pure)]
|
||||
pub fn get_group_contacts(group: &mut RhaiGroup) -> Vec<i64> {
|
||||
group
|
||||
.contacts
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|id| id as i64)
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Group Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_group_id(group: &mut RhaiGroup) -> i64 {
|
||||
group.base_data.id as i64
|
||||
}
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_group_created_at(group: &mut RhaiGroup) -> i64 {
|
||||
group.base_data.created_at
|
||||
}
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_group_modified_at(group: &mut RhaiGroup) -> i64 {
|
||||
group.base_data.modified_at
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "name", pure)]
|
||||
pub fn get_group_name(group: &mut RhaiGroup) -> String {
|
||||
group.name.clone()
|
||||
}
|
||||
#[rhai_fn(get = "description", pure)]
|
||||
pub fn get_group_description(group: &mut RhaiGroup) -> Option<String> {
|
||||
group.description.clone()
|
||||
}
|
||||
|
||||
// --- Contact Functions ---
|
||||
#[rhai_fn(name = "new_contact", return_raw)]
|
||||
pub fn new_contact() -> Result<RhaiContact, Box<EvalAltResult>> {
|
||||
let contact = Contact::new();
|
||||
Ok(contact)
|
||||
}
|
||||
|
||||
/// Sets the contact name
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn contact_name(
|
||||
contact: &mut RhaiContact,
|
||||
name: String,
|
||||
) -> Result<RhaiContact, Box<EvalAltResult>> {
|
||||
// Create a default Contact to replace the taken one
|
||||
let default_contact = Contact::new();
|
||||
|
||||
// Take ownership of the contact, apply the builder method, then put it back
|
||||
let owned_contact = std::mem::replace(contact, default_contact);
|
||||
*contact = owned_contact.name(name);
|
||||
Ok(contact.clone())
|
||||
}
|
||||
|
||||
/// Sets the contact description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn contact_description(
|
||||
contact: &mut RhaiContact,
|
||||
description: String,
|
||||
) -> Result<RhaiContact, Box<EvalAltResult>> {
|
||||
// Create a default Contact to replace the taken one
|
||||
let default_contact = Contact::new();
|
||||
|
||||
// Take ownership of the contact, apply the builder method, then put it back
|
||||
let owned_contact = std::mem::replace(contact, default_contact);
|
||||
*contact = owned_contact.description(description);
|
||||
Ok(contact.clone())
|
||||
}
|
||||
|
||||
// Contact Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_contact_id(contact: &mut RhaiContact) -> i64 {
|
||||
contact.base_data.id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "name", pure)]
|
||||
pub fn get_contact_name(contact: &mut RhaiContact) -> String {
|
||||
contact.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_contact_created_at(contact: &mut RhaiContact) -> i64 {
|
||||
contact.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_contact_modified_at(contact: &mut RhaiContact) -> i64 {
|
||||
contact.base_data.modified_at
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_contact_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Register the exported module globally
|
||||
let module = exported_module!(rhai_contact_module);
|
||||
engine.register_global_module(module.into());
|
||||
|
||||
// Create a module for database functions
|
||||
let mut db_module = Module::new();
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_set_group = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"save_group",
|
||||
move |group: Group| -> Result<Group, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_group.set(&group).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_group: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result.1)
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_group = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"delete_group",
|
||||
move |group: Group| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_group
|
||||
.collection::<Group>()
|
||||
.expect("can open group collection")
|
||||
.delete_by_id(group.base_data.id)
|
||||
.expect("can delete group");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_group = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"get_group_by_id",
|
||||
move |id_i64: INT| -> Result<Group, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_group
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_event_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Event with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_set_contact = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"save_contact",
|
||||
move |contact: Contact| -> Result<Contact, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_contact.set(&contact).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_contact: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated contact with the correct ID
|
||||
Ok(result.1)
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_contact = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"delete_contact",
|
||||
move |contact: Contact| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_contact
|
||||
.collection::<Contact>()
|
||||
.expect("can open contact collection")
|
||||
.delete_by_id(contact.base_data.id)
|
||||
.expect("can delete event");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_contact = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"get_contact_by_id",
|
||||
move |id_i64: INT| -> Result<Contact, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_contact
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_contact_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Contact with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_contacts function to get all contacts
|
||||
let db_clone_list_contacts = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"list_contacts",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_contacts
|
||||
.collection::<Contact>()
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get contact collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let contacts = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all contacts: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for contact in contacts {
|
||||
array.push(Dynamic::from(contact));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_events function to get all events
|
||||
let db_clone_list_groups = db.clone();
|
||||
db_module.set_native_fn(
|
||||
"list_groups",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_groups.collection::<Group>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get group collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let groups = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all groups: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for group in groups {
|
||||
array.push(Dynamic::from(group));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
},
|
||||
);
|
||||
|
||||
// Register the database module globally
|
||||
engine.register_global_module(db_module.into());
|
||||
|
||||
println!("Successfully registered contact Rhai module using export_module approach.");
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
pub mod account;
|
||||
pub mod asset;
|
||||
pub mod marketplace;
|
||||
pub mod rhai;
|
||||
|
||||
// Re-export main models for easier access
|
||||
pub use self::account::Account;
|
||||
|
@ -1,973 +0,0 @@
|
||||
use chrono::Utc;
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::account::Account;
|
||||
use super::asset::{Asset, AssetType};
|
||||
use super::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
|
||||
|
||||
use crate::db::hero::OurDB;
|
||||
use crate::db::{Collection, Db};
|
||||
|
||||
type RhaiAccount = Account;
|
||||
type RhaiAsset = Asset;
|
||||
type RhaiListing = Listing;
|
||||
type RhaiBid = Bid;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions for enum conversions
|
||||
|
||||
// AssetType conversions
|
||||
fn asset_type_to_string(asset_type: &AssetType) -> String {
|
||||
match asset_type {
|
||||
AssetType::Native => "Native".to_string(),
|
||||
AssetType::Erc20 => "Erc20".to_string(),
|
||||
AssetType::Erc721 => "Erc721".to_string(),
|
||||
AssetType::Erc1155 => "Erc1155".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_asset_type(s: &str) -> Result<AssetType, Box<EvalAltResult>> {
|
||||
match s {
|
||||
"Native" => Ok(AssetType::Native),
|
||||
"Erc20" => Ok(AssetType::Erc20),
|
||||
"Erc721" => Ok(AssetType::Erc721),
|
||||
"Erc1155" => Ok(AssetType::Erc1155),
|
||||
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"Invalid asset type: '{}'. Expected one of: Native, Erc20, Erc721, Erc1155",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
// ListingStatus conversions
|
||||
fn listing_status_to_string(status: &ListingStatus) -> String {
|
||||
match status {
|
||||
ListingStatus::Active => "Active".to_string(),
|
||||
ListingStatus::Sold => "Sold".to_string(),
|
||||
ListingStatus::Cancelled => "Cancelled".to_string(),
|
||||
ListingStatus::Expired => "Expired".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_listing_status(s: &str) -> Result<ListingStatus, Box<EvalAltResult>> {
|
||||
match s {
|
||||
"Active" => Ok(ListingStatus::Active),
|
||||
"Sold" => Ok(ListingStatus::Sold),
|
||||
"Cancelled" => Ok(ListingStatus::Cancelled),
|
||||
"Expired" => Ok(ListingStatus::Expired),
|
||||
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"Invalid listing status: '{}'. Expected one of: Active, Sold, Cancelled, Expired",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
// ListingType conversions
|
||||
fn listing_type_to_string(lt: &ListingType) -> String {
|
||||
match lt {
|
||||
ListingType::FixedPrice => "FixedPrice".to_string(),
|
||||
ListingType::Auction => "Auction".to_string(),
|
||||
ListingType::Exchange => "Exchange".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_listing_type(s: &str) -> Result<ListingType, Box<EvalAltResult>> {
|
||||
match s {
|
||||
"FixedPrice" => Ok(ListingType::FixedPrice),
|
||||
"Auction" => Ok(ListingType::Auction),
|
||||
"Exchange" => Ok(ListingType::Exchange),
|
||||
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"Invalid listing type: '{}'. Expected one of: FixedPrice, Auction, Exchange",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
// BidStatus conversions
|
||||
fn bid_status_to_string(status: &BidStatus) -> String {
|
||||
match status {
|
||||
BidStatus::Active => "Active".to_string(),
|
||||
BidStatus::Accepted => "Accepted".to_string(),
|
||||
BidStatus::Rejected => "Rejected".to_string(),
|
||||
BidStatus::Cancelled => "Cancelled".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_bid_status(s: &str) -> Result<BidStatus, Box<EvalAltResult>> {
|
||||
match s {
|
||||
"Active" => Ok(BidStatus::Active),
|
||||
"Accepted" => Ok(BidStatus::Accepted),
|
||||
"Rejected" => Ok(BidStatus::Rejected),
|
||||
"Cancelled" => Ok(BidStatus::Cancelled),
|
||||
_ => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"Invalid bid status: '{}'. Expected one of: Active, Accepted, Rejected, Cancelled",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
// Account module
|
||||
#[export_module]
|
||||
mod account_module {
|
||||
use super::*;
|
||||
|
||||
// Constructor
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn new_account() -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
Ok(Account::new())
|
||||
}
|
||||
|
||||
// Getters
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_id(account: &mut RhaiAccount) -> INT {
|
||||
account.base_data.id as INT
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_created_at(account: &mut RhaiAccount) -> INT {
|
||||
account.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_name(account: &mut RhaiAccount) -> String {
|
||||
account.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_user_id(account: &mut RhaiAccount) -> INT {
|
||||
account.user_id as INT
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_description(account: &mut RhaiAccount) -> String {
|
||||
account.description.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_ledger(account: &mut RhaiAccount) -> String {
|
||||
account.ledger.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_address(account: &mut RhaiAccount) -> String {
|
||||
account.address.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_pubkey(account: &mut RhaiAccount) -> String {
|
||||
account.pubkey.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_assets(account: &mut RhaiAccount) -> Array {
|
||||
let mut assets_array = Array::new();
|
||||
for asset_id in &account.assets {
|
||||
assets_array.push(Dynamic::from(*asset_id as INT));
|
||||
}
|
||||
assets_array
|
||||
}
|
||||
|
||||
// Setters using builder pattern with proper mutability handling
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn name(
|
||||
account: &mut RhaiAccount,
|
||||
name: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.name(name);
|
||||
*account = acc;
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn user_id(
|
||||
account: &mut RhaiAccount,
|
||||
user_id: INT,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let user_id = id_from_i64_to_u32(user_id)?;
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.user_id(user_id);
|
||||
*account = acc;
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn description(
|
||||
account: &mut RhaiAccount,
|
||||
description: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.description(description);
|
||||
*account = acc;
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn ledger(
|
||||
account: &mut RhaiAccount,
|
||||
ledger: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.ledger(ledger);
|
||||
*account = acc;
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn address(
|
||||
account: &mut RhaiAccount,
|
||||
address: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.address(address);
|
||||
*account = acc;
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn pubkey(
|
||||
account: &mut RhaiAccount,
|
||||
pubkey: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.pubkey(pubkey);
|
||||
*account = acc;
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn add_asset(
|
||||
account: &mut RhaiAccount,
|
||||
asset_id: INT,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let asset_id = id_from_i64_to_u32(asset_id)?;
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.add_asset(asset_id);
|
||||
*account = acc;
|
||||
Ok(account.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// Asset module
|
||||
#[export_module]
|
||||
mod asset_module {
|
||||
use super::*;
|
||||
|
||||
// Constructor
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn new_asset() -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
Ok(Asset::new())
|
||||
}
|
||||
|
||||
// Getters
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_id(asset: &mut RhaiAsset) -> INT {
|
||||
asset.base_data.id as INT
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_created_at(asset: &mut RhaiAsset) -> INT {
|
||||
asset.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_name(asset: &mut RhaiAsset) -> String {
|
||||
asset.name.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_description(asset: &mut RhaiAsset) -> String {
|
||||
asset.description.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_amount(asset: &mut RhaiAsset) -> f64 {
|
||||
asset.amount
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_address(asset: &mut RhaiAsset) -> String {
|
||||
asset.address.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_asset_type(asset: &mut RhaiAsset) -> String {
|
||||
asset_type_to_string(&asset.asset_type)
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_decimals(asset: &mut RhaiAsset) -> INT {
|
||||
asset.decimals as INT
|
||||
}
|
||||
|
||||
// Setters using builder pattern with proper mutability handling
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn name(asset: &mut RhaiAsset, name: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.name(name);
|
||||
*asset = ast;
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn description(
|
||||
asset: &mut RhaiAsset,
|
||||
description: String,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.description(description);
|
||||
*asset = ast;
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn amount(asset: &mut RhaiAsset, amount: f64) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.amount(amount);
|
||||
*asset = ast;
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn address(
|
||||
asset: &mut RhaiAsset,
|
||||
address: String,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.address(address);
|
||||
*asset = ast;
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn asset_type(
|
||||
asset: &mut RhaiAsset,
|
||||
asset_type_str: String,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let asset_type_enum = string_to_asset_type(&asset_type_str)?;
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.asset_type(asset_type_enum);
|
||||
*asset = ast;
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn decimals(asset: &mut RhaiAsset, decimals: INT) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
if decimals < 0 || decimals > 255 {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Decimals value must be between 0 and 255, got {}", decimals).into(),
|
||||
Position::NONE,
|
||||
)));
|
||||
}
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.decimals(decimals as u8);
|
||||
*asset = ast;
|
||||
Ok(asset.clone())
|
||||
}
|
||||
}
|
||||
// Listing module
|
||||
#[export_module]
|
||||
mod listing_module {
|
||||
use super::*;
|
||||
|
||||
// Constructor
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn new_listing() -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
Ok(Listing::new())
|
||||
}
|
||||
|
||||
// Getters
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_id(listing: &mut RhaiListing) -> INT {
|
||||
listing.base_data.id as INT
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_created_at(listing: &mut RhaiListing) -> INT {
|
||||
listing.base_data.created_at
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_seller_id(listing: &mut RhaiListing) -> INT {
|
||||
// Parse the seller_id string to u32, then to INT
|
||||
listing.seller_id.parse::<u32>().unwrap_or(0) as INT
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_asset_id(listing: &mut RhaiListing) -> INT {
|
||||
// Parse the asset_id string to u32, then to INT
|
||||
listing.asset_id.parse::<u32>().unwrap_or(0) as INT
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_price(listing: &mut RhaiListing) -> f64 {
|
||||
listing.price
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_currency(listing: &mut RhaiListing) -> String {
|
||||
listing.currency.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_listing_type(listing: &mut RhaiListing) -> String {
|
||||
listing_type_to_string(&listing.listing_type)
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_status(listing: &mut RhaiListing) -> String {
|
||||
listing_status_to_string(&listing.status)
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_title(listing: &mut RhaiListing) -> String {
|
||||
listing.title.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_description(listing: &mut RhaiListing) -> String {
|
||||
listing.description.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_image_url(listing: &mut RhaiListing) -> String {
|
||||
listing.image_url.clone().unwrap_or_else(|| "".to_string())
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_end_date(listing: &mut RhaiListing) -> INT {
|
||||
listing.expires_at.map_or(0, |d| d.timestamp())
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_tags(listing: &mut RhaiListing) -> Array {
|
||||
let mut tags_array = Array::new();
|
||||
for tag in &listing.tags {
|
||||
tags_array.push(Dynamic::from(tag.clone()));
|
||||
}
|
||||
tags_array
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_bids(listing: &mut RhaiListing) -> Array {
|
||||
let mut bids_array = Array::new();
|
||||
for bid in &listing.bids {
|
||||
bids_array.push(Dynamic::from(bid.clone()));
|
||||
}
|
||||
bids_array
|
||||
}
|
||||
|
||||
// Setters using builder pattern with proper mutability handling
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn seller_id(
|
||||
listing: &mut RhaiListing,
|
||||
seller_id: INT,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let seller_id = id_from_i64_to_u32(seller_id)?;
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.seller_id(seller_id);
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn asset_id(
|
||||
listing: &mut RhaiListing,
|
||||
asset_id: INT,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let asset_id = id_from_i64_to_u32(asset_id)?;
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.asset_id(asset_id);
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn price(listing: &mut RhaiListing, price: f64) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.price(price);
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn currency(
|
||||
listing: &mut RhaiListing,
|
||||
currency: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.currency(currency);
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn listing_type(
|
||||
listing: &mut RhaiListing,
|
||||
listing_type_str: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let listing_type_enum = string_to_listing_type(&listing_type_str)?;
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.listing_type(listing_type_enum);
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn title(
|
||||
listing: &mut RhaiListing,
|
||||
title: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.title(title);
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn description(
|
||||
listing: &mut RhaiListing,
|
||||
description: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.description(description);
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn image_url(
|
||||
listing: &mut RhaiListing,
|
||||
image_url: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.image_url(Some(image_url));
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn expires_at(
|
||||
listing: &mut RhaiListing,
|
||||
end_date_ts: INT,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
use chrono::TimeZone;
|
||||
let end_date = chrono::Utc
|
||||
.timestamp_opt(end_date_ts, 0)
|
||||
.single()
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Invalid timestamp: {}", end_date_ts).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.expires_at(Some(end_date));
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn add_tag(
|
||||
listing: &mut RhaiListing,
|
||||
tag: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.add_tag(tag);
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn add_bid(
|
||||
listing: &mut RhaiListing,
|
||||
bid: RhaiBid,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let lst = mem::take(listing);
|
||||
match lst.clone().add_bid(bid) {
|
||||
Ok(updated_lst) => {
|
||||
*listing = updated_lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
Err(err) => {
|
||||
// Put back the original listing since the add_bid failed
|
||||
*listing = lst;
|
||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to add bid: {}", err).into(),
|
||||
Position::NONE,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn complete_sale(
|
||||
listing: &mut RhaiListing,
|
||||
buyer_id: INT,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let buyer_id = id_from_i64_to_u32(buyer_id)?;
|
||||
let lst = mem::take(listing);
|
||||
|
||||
// First set the buyer_id and sale_price
|
||||
let lst_with_buyer = lst.clone().buyer_id(buyer_id);
|
||||
|
||||
// Now complete the sale
|
||||
match lst_with_buyer.complete_sale() {
|
||||
Ok(updated_lst) => {
|
||||
*listing = updated_lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
Err(err) => {
|
||||
// Put back the original listing since the complete_sale failed
|
||||
*listing = lst;
|
||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to complete sale: {}", err).into(),
|
||||
Position::NONE,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn cancel(listing: &mut RhaiListing) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let lst = mem::take(listing);
|
||||
match lst.clone().cancel() {
|
||||
Ok(updated_lst) => {
|
||||
*listing = updated_lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
Err(err) => {
|
||||
// Put back the original listing since the cancel failed
|
||||
*listing = lst;
|
||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to cancel listing: {}", err).into(),
|
||||
Position::NONE,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn check_expiration(listing: &mut RhaiListing) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.check_expiration();
|
||||
*listing = lst;
|
||||
Ok(listing.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// Bid module
|
||||
#[export_module]
|
||||
mod bid_module {
|
||||
use super::*;
|
||||
|
||||
// Constructor
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn new_bid() -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
Ok(Bid::new())
|
||||
}
|
||||
|
||||
// Getters
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_listing_id(bid: &mut RhaiBid) -> String {
|
||||
bid.listing_id.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_bidder_id(bid: &mut RhaiBid) -> INT {
|
||||
bid.bidder_id as INT
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_amount(bid: &mut RhaiBid) -> f64 {
|
||||
bid.amount
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_currency(bid: &mut RhaiBid) -> String {
|
||||
bid.currency.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_status(bid: &mut RhaiBid) -> String {
|
||||
bid_status_to_string(&bid.status)
|
||||
}
|
||||
|
||||
#[rhai_fn(global, pure)]
|
||||
pub fn get_created_at(bid: &mut RhaiBid) -> INT {
|
||||
bid.created_at.timestamp()
|
||||
}
|
||||
|
||||
// Setters using builder pattern with proper mutability handling
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn listing_id(
|
||||
bid: &mut RhaiBid,
|
||||
listing_id: String,
|
||||
) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
let mut b = mem::take(bid);
|
||||
b = b.listing_id(listing_id);
|
||||
*bid = b;
|
||||
Ok(bid.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn bidder_id(bid: &mut RhaiBid, bidder_id: INT) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
let bidder_id = id_from_i64_to_u32(bidder_id)?;
|
||||
let mut b = mem::take(bid);
|
||||
b = b.bidder_id(bidder_id);
|
||||
*bid = b;
|
||||
Ok(bid.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn amount(bid: &mut RhaiBid, amount: f64) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
let mut b = mem::take(bid);
|
||||
b = b.amount(amount);
|
||||
*bid = b;
|
||||
Ok(bid.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn currency(bid: &mut RhaiBid, currency: String) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
let mut b = mem::take(bid);
|
||||
b = b.currency(currency);
|
||||
*bid = b;
|
||||
Ok(bid.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn update_status(
|
||||
bid: &mut RhaiBid,
|
||||
status_str: String,
|
||||
) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
let status = string_to_bid_status(&status_str)?;
|
||||
let mut b = mem::take(bid);
|
||||
b = b.status(status);
|
||||
*bid = b;
|
||||
Ok(bid.clone())
|
||||
}
|
||||
}
|
||||
use rhai::ImmutableString;
|
||||
|
||||
// Register all Rhai functions for the finance module
|
||||
pub fn register_finance_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// --- Register model-specific modules with the engine ---
|
||||
let account_module = exported_module!(account_module);
|
||||
engine.register_global_module(account_module.into());
|
||||
|
||||
let asset_module = exported_module!(asset_module);
|
||||
engine.register_global_module(asset_module.into());
|
||||
|
||||
let listing_module = exported_module!(listing_module);
|
||||
engine.register_global_module(listing_module.into());
|
||||
|
||||
let bid_module = exported_module!(bid_module);
|
||||
engine.register_global_module(bid_module.into());
|
||||
|
||||
// --- Global Helper Functions (Enum conversions) ---
|
||||
engine.register_fn("str_to_asset_type", |s: ImmutableString| {
|
||||
self::string_to_asset_type(s.as_str())
|
||||
});
|
||||
engine.register_fn("asset_type_to_str", self::asset_type_to_string);
|
||||
engine.register_fn("str_to_listing_status", |s: ImmutableString| {
|
||||
self::string_to_listing_status(s.as_str())
|
||||
});
|
||||
engine.register_fn("listing_status_to_str", self::listing_status_to_string);
|
||||
engine.register_fn("str_to_listing_type", |s: ImmutableString| {
|
||||
self::string_to_listing_type(s.as_str())
|
||||
});
|
||||
engine.register_fn("listing_type_to_str", self::listing_type_to_string);
|
||||
engine.register_fn("str_to_bid_status", |s: ImmutableString| {
|
||||
self::string_to_bid_status(s.as_str())
|
||||
});
|
||||
engine.register_fn("bid_status_to_str", self::bid_status_to_string);
|
||||
|
||||
// --- Database interaction functions (registered in a separate db_module) ---
|
||||
let mut db_module = Module::new();
|
||||
|
||||
// Account DB functions
|
||||
let db_set_account = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"set_account",
|
||||
move |account: Account| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_account.collection::<Account>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Account collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.set(&account)
|
||||
.map(|(id, _)| id as INT)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save Account: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_get_account = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_account_by_id",
|
||||
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_rhai)?;
|
||||
let collection = db_get_account.collection::<Account>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Account collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.get_by_id(id_u32)
|
||||
.map(|opt_account| {
|
||||
opt_account
|
||||
.map(Dynamic::from)
|
||||
.unwrap_or_else(|| Dynamic::UNIT)
|
||||
})
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Account with ID {}: {:?}", id_rhai, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Asset DB functions
|
||||
let db_set_asset = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"set_asset",
|
||||
move |asset: Asset| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_asset.collection::<Asset>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Asset collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.set(&asset)
|
||||
.map(|(id, _)| id as INT)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save Asset: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_get_asset = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_asset_by_id",
|
||||
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_rhai)?;
|
||||
let collection = db_get_asset.collection::<Asset>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Asset collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.get_by_id(id_u32)
|
||||
.map(|opt_asset| {
|
||||
opt_asset
|
||||
.map(Dynamic::from)
|
||||
.unwrap_or_else(|| Dynamic::UNIT)
|
||||
})
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Asset with ID {}: {:?}", id_rhai, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Listing DB functions
|
||||
let db_set_listing = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"set_listing",
|
||||
move |listing: Listing| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_listing.collection::<Listing>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Listing collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.set(&listing)
|
||||
.map(|(id, _)| id as INT)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save Listing: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_get_listing = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_listing_by_id",
|
||||
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_rhai)?;
|
||||
let collection = db_get_listing.collection::<Listing>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Listing collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.get_by_id(id_u32)
|
||||
.map(|opt_listing| {
|
||||
opt_listing
|
||||
.map(Dynamic::from)
|
||||
.unwrap_or_else(|| Dynamic::UNIT)
|
||||
})
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Listing with ID {}: {:?}", id_rhai, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_global_module(db_module.into());
|
||||
|
||||
// Global timestamp function for scripts to get current time
|
||||
engine.register_fn("timestamp", || Utc::now().timestamp());
|
||||
|
||||
println!("Successfully registered finance Rhai module.");
|
||||
}
|
@ -1,13 +1,15 @@
|
||||
use super::flow_step::FlowStep;
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents a signing flow.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default, CustomType)]
|
||||
#[model]
|
||||
pub struct Flow {
|
||||
/// Base model data (id, created_at, updated_at).
|
||||
#[rhai_type(skip)]
|
||||
pub base_data: BaseModelData,
|
||||
|
||||
/// A unique UUID for the flow, for external reference.
|
||||
|
@ -1,13 +1,15 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::default::Default;
|
||||
|
||||
/// Represents a step within a signing flow.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, CustomType)]
|
||||
#[model]
|
||||
pub struct FlowStep {
|
||||
/// Base model data.
|
||||
#[rhai_type(skip)]
|
||||
pub base_data: BaseModelData,
|
||||
|
||||
/// Optional description for the step.
|
||||
|
@ -1,11 +1,9 @@
|
||||
// Export flow model submodules
|
||||
pub mod flow;
|
||||
pub mod flow_step;
|
||||
pub mod rhai;
|
||||
pub mod signature_requirement;
|
||||
|
||||
// Re-export key types for convenience
|
||||
pub use flow::Flow;
|
||||
pub use flow_step::FlowStep;
|
||||
pub use rhai::register_flow_rhai_module;
|
||||
pub use signature_requirement::SignatureRequirement;
|
||||
pub use signature_requirement::SignatureRequirement;
|
@ -1,535 +0,0 @@
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, INT, Module, Position};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::flow::Flow;
|
||||
use super::flow_step::FlowStep;
|
||||
use super::signature_requirement::SignatureRequirement;
|
||||
type RhaiFlow = Flow;
|
||||
type RhaiFlowStep = FlowStep;
|
||||
type RhaiSignatureRequirement = SignatureRequirement;
|
||||
use crate::db::Collection;
|
||||
use crate::db::Db;
|
||||
use crate::db::hero::OurDB;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod rhai_flow_module {
|
||||
// --- Flow Functions ---
|
||||
#[rhai_fn(name = "new_flow")]
|
||||
pub fn new_flow(flow_uuid: String) -> RhaiFlow {
|
||||
Flow::new(flow_uuid)
|
||||
}
|
||||
|
||||
/// Sets the flow name
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn flow_name(flow: &mut RhaiFlow, name: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
|
||||
let owned_flow = mem::replace(flow, Flow::new("")); // Dummy for replacement
|
||||
*flow = owned_flow.name(name);
|
||||
Ok(flow.clone())
|
||||
}
|
||||
|
||||
/// Sets the flow status
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn flow_status(
|
||||
flow: &mut RhaiFlow,
|
||||
status: String,
|
||||
) -> Result<RhaiFlow, Box<EvalAltResult>> {
|
||||
let owned_flow = mem::replace(flow, Flow::new("")); // Dummy for replacement
|
||||
*flow = owned_flow.status(status);
|
||||
Ok(flow.clone())
|
||||
}
|
||||
|
||||
/// Adds a step to the flow
|
||||
#[rhai_fn(name = "add_step", return_raw, global, pure)]
|
||||
pub fn flow_add_step(
|
||||
flow: &mut RhaiFlow,
|
||||
step: RhaiFlowStep,
|
||||
) -> Result<RhaiFlow, Box<EvalAltResult>> {
|
||||
let owned_flow = mem::replace(flow, Flow::new("")); // Dummy for replacement
|
||||
*flow = owned_flow.add_step(step);
|
||||
Ok(flow.clone())
|
||||
}
|
||||
|
||||
// Flow Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_id(flow: &mut RhaiFlow) -> i64 {
|
||||
flow.base_data.id as i64
|
||||
}
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_created_at(flow: &mut RhaiFlow) -> i64 {
|
||||
flow.base_data.created_at
|
||||
}
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_modified_at(flow: &mut RhaiFlow) -> i64 {
|
||||
flow.base_data.modified_at
|
||||
}
|
||||
#[rhai_fn(get = "flow_uuid", pure)]
|
||||
pub fn get_flow_uuid(flow: &mut RhaiFlow) -> String {
|
||||
flow.flow_uuid.clone()
|
||||
}
|
||||
#[rhai_fn(get = "name", pure)]
|
||||
pub fn get_name(flow: &mut RhaiFlow) -> String {
|
||||
flow.name.clone()
|
||||
}
|
||||
#[rhai_fn(get = "status", pure)]
|
||||
pub fn get_status(flow: &mut RhaiFlow) -> String {
|
||||
flow.status.clone()
|
||||
}
|
||||
#[rhai_fn(get = "steps", pure)]
|
||||
pub fn get_steps(flow: &mut RhaiFlow) -> Array {
|
||||
flow.steps
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Dynamic::from)
|
||||
.collect::<Array>()
|
||||
}
|
||||
|
||||
// --- FlowStep Functions ---
|
||||
#[rhai_fn(global)]
|
||||
pub fn new_flow_step(step_order_i64: i64) -> Dynamic {
|
||||
match id_from_i64_to_u32(step_order_i64) {
|
||||
Ok(step_order) => {
|
||||
let mut flow_step = FlowStep::default();
|
||||
flow_step.step_order = step_order;
|
||||
Dynamic::from(flow_step)
|
||||
}
|
||||
Err(err) => Dynamic::from(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the flow step description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn flow_step_description(
|
||||
step: &mut RhaiFlowStep,
|
||||
description: String,
|
||||
) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
|
||||
let owned_step = mem::replace(step, FlowStep::default()); // Use Default trait
|
||||
*step = owned_step.description(description);
|
||||
Ok(step.clone())
|
||||
}
|
||||
|
||||
/// Sets the flow step status
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn flow_step_status(
|
||||
step: &mut RhaiFlowStep,
|
||||
status: String,
|
||||
) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
|
||||
let owned_step = mem::replace(step, FlowStep::default()); // Use Default trait
|
||||
*step = owned_step.status(status);
|
||||
Ok(step.clone())
|
||||
}
|
||||
|
||||
// FlowStep Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_step_id(step: &mut RhaiFlowStep) -> i64 {
|
||||
step.base_data.id as i64
|
||||
}
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_step_created_at(step: &mut RhaiFlowStep) -> i64 {
|
||||
step.base_data.created_at
|
||||
}
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_step_modified_at(step: &mut RhaiFlowStep) -> i64 {
|
||||
step.base_data.modified_at
|
||||
}
|
||||
#[rhai_fn(get = "description", pure)]
|
||||
pub fn get_step_description(step: &mut RhaiFlowStep) -> Dynamic {
|
||||
match &step.description {
|
||||
Some(desc) => Dynamic::from(desc.clone()),
|
||||
None => Dynamic::UNIT,
|
||||
}
|
||||
}
|
||||
#[rhai_fn(get = "step_order", pure)]
|
||||
pub fn get_step_order(step: &mut RhaiFlowStep) -> i64 {
|
||||
step.step_order as i64
|
||||
}
|
||||
#[rhai_fn(get = "status", pure)]
|
||||
pub fn get_step_status(step: &mut RhaiFlowStep) -> String {
|
||||
step.status.clone()
|
||||
}
|
||||
|
||||
// --- SignatureRequirement Functions ---
|
||||
/// Create a new signature requirement
|
||||
#[rhai_fn(global)]
|
||||
pub fn new_signature_requirement(
|
||||
flow_step_id_i64: i64,
|
||||
public_key: String,
|
||||
message: String,
|
||||
) -> Dynamic {
|
||||
match id_from_i64_to_u32(flow_step_id_i64) {
|
||||
Ok(flow_step_id) => {
|
||||
let mut signature_requirement = SignatureRequirement::default();
|
||||
signature_requirement.flow_step_id = flow_step_id;
|
||||
signature_requirement.public_key = public_key;
|
||||
signature_requirement.message = message;
|
||||
Dynamic::from(signature_requirement)
|
||||
}
|
||||
Err(err) => Dynamic::from(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the signed_by field
|
||||
#[rhai_fn(name = "signed_by", return_raw, global, pure)]
|
||||
pub fn signature_requirement_signed_by(
|
||||
sr: &mut RhaiSignatureRequirement,
|
||||
signed_by: String,
|
||||
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
|
||||
*sr = owned_sr.signed_by(signed_by);
|
||||
Ok(sr.clone())
|
||||
}
|
||||
|
||||
/// Sets the signature field
|
||||
#[rhai_fn(name = "signature", return_raw, global, pure)]
|
||||
pub fn signature_requirement_signature(
|
||||
sr: &mut RhaiSignatureRequirement,
|
||||
signature: String,
|
||||
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
|
||||
*sr = owned_sr.signature(signature);
|
||||
Ok(sr.clone())
|
||||
}
|
||||
|
||||
/// Sets the status field
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn signature_requirement_status(
|
||||
sr: &mut RhaiSignatureRequirement,
|
||||
status: String,
|
||||
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
|
||||
*sr = owned_sr.status(status);
|
||||
Ok(sr.clone())
|
||||
}
|
||||
|
||||
// SignatureRequirement Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_sr_id(sr: &mut RhaiSignatureRequirement) -> i64 {
|
||||
sr.base_data.id as i64
|
||||
}
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_sr_created_at(sr: &mut RhaiSignatureRequirement) -> i64 {
|
||||
sr.base_data.created_at
|
||||
}
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_sr_modified_at(sr: &mut RhaiSignatureRequirement) -> i64 {
|
||||
sr.base_data.modified_at
|
||||
}
|
||||
#[rhai_fn(get = "flow_step_id", pure)]
|
||||
pub fn get_sr_flow_step_id(sr: &mut RhaiSignatureRequirement) -> i64 {
|
||||
sr.flow_step_id as i64
|
||||
}
|
||||
#[rhai_fn(get = "public_key", pure)]
|
||||
pub fn get_sr_public_key(sr: &mut RhaiSignatureRequirement) -> String {
|
||||
sr.public_key.clone()
|
||||
}
|
||||
#[rhai_fn(get = "message", pure)]
|
||||
pub fn get_sr_message(sr: &mut RhaiSignatureRequirement) -> String {
|
||||
sr.message.clone()
|
||||
}
|
||||
#[rhai_fn(get = "signed_by", pure)]
|
||||
pub fn get_sr_signed_by(sr: &mut RhaiSignatureRequirement) -> Dynamic {
|
||||
match &sr.signed_by {
|
||||
Some(signed_by) => Dynamic::from(signed_by.clone()),
|
||||
None => Dynamic::UNIT,
|
||||
}
|
||||
}
|
||||
#[rhai_fn(get = "signature", pure)]
|
||||
pub fn get_sr_signature(sr: &mut RhaiSignatureRequirement) -> Dynamic {
|
||||
match &sr.signature {
|
||||
Some(signature) => Dynamic::from(signature.clone()),
|
||||
None => Dynamic::UNIT,
|
||||
}
|
||||
}
|
||||
#[rhai_fn(get = "status", pure)]
|
||||
pub fn get_sr_status(sr: &mut RhaiSignatureRequirement) -> String {
|
||||
sr.status.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the flow module with the Rhai engine
|
||||
pub fn register_flow_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Create a module for database functions
|
||||
let mut db_module = Module::new();
|
||||
|
||||
// Flow database functions
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"save_flow",
|
||||
move |flow: Flow| -> Result<Flow, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone.set(&flow).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error save_flow: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated flow with the correct ID
|
||||
Ok(result.1)
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_flow_by_id",
|
||||
move |id_i64: INT| -> Result<Flow, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_flow_by_id: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Flow with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"delete_flow",
|
||||
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
let collection = db_clone.collection::<Flow>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get flow collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection.delete_by_id(id_u32).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to delete Flow (ID: {}): {:?}", id_u32, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"list_flows",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<Flow>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get flow collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let flows = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all flows: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for flow in flows {
|
||||
array.push(Dynamic::from(flow));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
},
|
||||
);
|
||||
|
||||
// FlowStep database functions
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"save_flow_step",
|
||||
move |step: FlowStep| -> Result<FlowStep, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone.set(&step).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error save_flow_step: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated flow step with the correct ID
|
||||
Ok(result.1)
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_flow_step_by_id",
|
||||
move |id_i64: INT| -> Result<FlowStep, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_flow_step_by_id: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("FlowStep with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"delete_flow_step",
|
||||
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
let collection = db_clone.collection::<FlowStep>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get flow step collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection.delete_by_id(id_u32).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to delete FlowStep (ID: {}): {:?}", id_u32, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"list_flow_steps",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<FlowStep>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get flow step collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let steps = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all flow steps: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for step in steps {
|
||||
array.push(Dynamic::from(step));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
},
|
||||
);
|
||||
|
||||
// SignatureRequirement database functions
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"save_signature_requirement",
|
||||
move |sr: SignatureRequirement| -> Result<SignatureRequirement, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone.set(&sr).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error save_signature_requirement: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated signature requirement with the correct ID
|
||||
Ok(result.1)
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"get_signature_requirement_by_id",
|
||||
move |id_i64: INT| -> Result<SignatureRequirement, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_signature_requirement_by_id: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("SignatureRequirement with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"delete_signature_requirement",
|
||||
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
let collection = db_clone.collection::<SignatureRequirement>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get signature requirement collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection.delete_by_id(id_u32).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"Failed to delete SignatureRequirement (ID: {}): {:?}",
|
||||
id_u32, e
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn(
|
||||
"list_signature_requirements",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<SignatureRequirement>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get signature requirement collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let srs = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all signature requirements: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for sr in srs {
|
||||
array.push(Dynamic::from(sr));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
},
|
||||
);
|
||||
|
||||
// Register the database module globally
|
||||
engine.register_static_module("db", db_module.into());
|
||||
|
||||
// Register the flow module using exported_module! macro
|
||||
let module = exported_module!(rhai_flow_module);
|
||||
engine.register_global_module(module.into());
|
||||
|
||||
println!("Flow Rhai module registered.");
|
||||
}
|
@ -1,13 +1,15 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::default::Default;
|
||||
|
||||
/// Represents a signature requirement for a flow step.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, CustomType)]
|
||||
#[model]
|
||||
pub struct SignatureRequirement {
|
||||
/// Base model data.
|
||||
#[rhai_type(skip)]
|
||||
pub base_data: BaseModelData,
|
||||
|
||||
/// Foreign key to the FlowStep this requirement belongs to.
|
||||
|
@ -1,5 +1,3 @@
|
||||
pub mod contract;
|
||||
pub mod rhai;
|
||||
|
||||
pub use contract::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
|
||||
pub use rhai::register_legal_rhai_module;
|
||||
|
@ -1,613 +0,0 @@
|
||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, Module, NativeCallContext, Position};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::db::hero::OurDB; // Updated path based on compiler suggestion
|
||||
// use heromodels_core::BaseModelData; // Removed as fields are accessed via contract.base_data directly
|
||||
use crate::models::legal::{
|
||||
Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus,
|
||||
};
|
||||
|
||||
use crate::db::Collection; // Import the Collection trait
|
||||
|
||||
// --- Helper Functions for ID and Timestamp Conversion ---
|
||||
fn i64_to_u32(
|
||||
val: i64,
|
||||
context_pos: Position,
|
||||
field_name: &str,
|
||||
object_name: &str,
|
||||
) -> Result<u32, Box<EvalAltResult>> {
|
||||
val.try_into().map_err(|_e| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to u32",
|
||||
field_name, object_name, val
|
||||
),
|
||||
context_pos,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn i64_to_u64(
|
||||
val: i64,
|
||||
context_pos: Position,
|
||||
field_name: &str,
|
||||
object_name: &str,
|
||||
) -> Result<u64, Box<EvalAltResult>> {
|
||||
val.try_into().map_err(|_e| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to u64",
|
||||
field_name, object_name, val
|
||||
),
|
||||
context_pos,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn i64_to_i32(
|
||||
val: i64,
|
||||
context_pos: Position,
|
||||
field_name: &str,
|
||||
object_name: &str,
|
||||
) -> Result<i32, Box<EvalAltResult>> {
|
||||
val.try_into().map_err(|_e| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to i32",
|
||||
field_name, object_name, val
|
||||
),
|
||||
context_pos,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn register_legal_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// --- ContractStatus Enum ---
|
||||
// Register ContractStatus enum as constants
|
||||
let mut contract_status_module = Module::new();
|
||||
contract_status_module.set_var("Draft", ContractStatus::Draft);
|
||||
contract_status_module.set_var("PendingSignatures", ContractStatus::PendingSignatures);
|
||||
contract_status_module.set_var("Signed", ContractStatus::Signed);
|
||||
contract_status_module.set_var("Active", ContractStatus::Active);
|
||||
contract_status_module.set_var("Expired", ContractStatus::Expired);
|
||||
contract_status_module.set_var("Cancelled", ContractStatus::Cancelled);
|
||||
engine.register_static_module("ContractStatusConstants", contract_status_module.into());
|
||||
engine.register_type_with_name::<ContractStatus>("ContractStatus"); // Expose the type itself
|
||||
|
||||
// Register SignerStatus enum as constants
|
||||
let mut signer_status_module = Module::new();
|
||||
signer_status_module.set_var("Pending", SignerStatus::Pending);
|
||||
signer_status_module.set_var("Signed", SignerStatus::Signed);
|
||||
signer_status_module.set_var("Rejected", SignerStatus::Rejected);
|
||||
engine.register_static_module("SignerStatusConstants", signer_status_module.into());
|
||||
engine.register_type_with_name::<SignerStatus>("SignerStatus"); // Expose the type itself
|
||||
|
||||
// --- ContractRevision ---
|
||||
engine.register_type_with_name::<ContractRevision>("ContractRevision");
|
||||
engine.register_fn(
|
||||
"new_contract_revision",
|
||||
move |context: NativeCallContext,
|
||||
version_i64: i64,
|
||||
content: String,
|
||||
created_at_i64: i64,
|
||||
created_by: String|
|
||||
-> Result<ContractRevision, Box<EvalAltResult>> {
|
||||
let version = i64_to_u32(
|
||||
version_i64,
|
||||
context.position(),
|
||||
"version",
|
||||
"new_contract_revision",
|
||||
)?;
|
||||
let created_at = i64_to_u64(
|
||||
created_at_i64,
|
||||
context.position(),
|
||||
"created_at",
|
||||
"new_contract_revision",
|
||||
)?;
|
||||
Ok(ContractRevision::new(
|
||||
version, content, created_at, created_by,
|
||||
))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"comments",
|
||||
|mut revision: ContractRevision, comments: String| -> ContractRevision {
|
||||
revision.comments = Some(comments);
|
||||
revision
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"version",
|
||||
|revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(revision.version as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"content",
|
||||
|revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(revision.content.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_at",
|
||||
|revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(revision.created_at as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_by",
|
||||
|revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(revision.created_by.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"comments",
|
||||
|revision: &mut ContractRevision| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(revision
|
||||
.comments
|
||||
.clone()
|
||||
.map_or(Dynamic::UNIT, Dynamic::from))
|
||||
},
|
||||
);
|
||||
|
||||
// --- ContractSigner ---
|
||||
engine.register_type_with_name::<ContractSigner>("ContractSigner");
|
||||
engine.register_fn(
|
||||
"new_contract_signer",
|
||||
|id: String, name: String, email: String| -> ContractSigner {
|
||||
ContractSigner::new(id, name, email)
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"status",
|
||||
|signer: ContractSigner, status: SignerStatus| -> ContractSigner { signer.status(status) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"signed_at",
|
||||
|context: NativeCallContext,
|
||||
signer: ContractSigner,
|
||||
signed_at_i64: i64|
|
||||
-> Result<ContractSigner, Box<EvalAltResult>> {
|
||||
let signed_at_u64 = i64_to_u64(
|
||||
signed_at_i64,
|
||||
context.position(),
|
||||
"signed_at",
|
||||
"ContractSigner.signed_at",
|
||||
)?;
|
||||
Ok(signer.signed_at(signed_at_u64))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"clear_signed_at",
|
||||
|signer: ContractSigner| -> ContractSigner { signer.clear_signed_at() },
|
||||
);
|
||||
engine.register_fn(
|
||||
"comments",
|
||||
|signer: ContractSigner, comments: String| -> ContractSigner { signer.comments(comments) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"clear_comments",
|
||||
|signer: ContractSigner| -> ContractSigner { signer.clear_comments() },
|
||||
);
|
||||
|
||||
engine.register_get(
|
||||
"id",
|
||||
|signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(signer.id.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"name",
|
||||
|signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(signer.name.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"email",
|
||||
|signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(signer.email.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"status",
|
||||
|signer: &mut ContractSigner| -> Result<SignerStatus, Box<EvalAltResult>> {
|
||||
Ok(signer.status.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"signed_at_ts",
|
||||
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(signer
|
||||
.signed_at
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"comments",
|
||||
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(signer.comments.clone().map_or(Dynamic::UNIT, Dynamic::from))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"signed_at",
|
||||
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(signer
|
||||
.signed_at
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts)))
|
||||
},
|
||||
);
|
||||
|
||||
// --- Contract ---
|
||||
engine.register_type_with_name::<Contract>("Contract");
|
||||
engine.register_fn(
|
||||
"new_contract",
|
||||
move |context: NativeCallContext,
|
||||
base_id_i64: i64,
|
||||
contract_id: String|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let base_id = i64_to_u32(
|
||||
base_id_i64,
|
||||
context.position(),
|
||||
"base_id",
|
||||
"new_contract",
|
||||
)?;
|
||||
Ok(Contract::new(base_id, contract_id))
|
||||
},
|
||||
);
|
||||
|
||||
// Builder methods
|
||||
engine.register_fn("title", |contract: Contract, title: String| -> Contract {
|
||||
contract.title(title)
|
||||
});
|
||||
engine.register_fn(
|
||||
"description",
|
||||
|contract: Contract, description: String| -> Contract { contract.description(description) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"contract_type",
|
||||
|contract: Contract, contract_type: String| -> Contract {
|
||||
contract.contract_type(contract_type)
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"status",
|
||||
|contract: Contract, status: ContractStatus| -> Contract { contract.status(status) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"created_by",
|
||||
|contract: Contract, created_by: String| -> Contract { contract.created_by(created_by) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"terms_and_conditions",
|
||||
|contract: Contract, terms: String| -> Contract { contract.terms_and_conditions(terms) },
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"start_date",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
start_date_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let start_date_u64 = i64_to_u64(
|
||||
start_date_i64,
|
||||
context.position(),
|
||||
"start_date",
|
||||
"Contract.start_date",
|
||||
)?;
|
||||
Ok(contract.start_date(start_date_u64))
|
||||
},
|
||||
);
|
||||
engine.register_fn("clear_start_date", |contract: Contract| -> Contract {
|
||||
contract.clear_start_date()
|
||||
});
|
||||
|
||||
engine.register_fn(
|
||||
"end_date",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
end_date_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let end_date_u64 = i64_to_u64(
|
||||
end_date_i64,
|
||||
context.position(),
|
||||
"end_date",
|
||||
"Contract.end_date",
|
||||
)?;
|
||||
Ok(contract.end_date(end_date_u64))
|
||||
},
|
||||
);
|
||||
engine.register_fn("clear_end_date", |contract: Contract| -> Contract {
|
||||
contract.clear_end_date()
|
||||
});
|
||||
|
||||
engine.register_fn(
|
||||
"renewal_period_days",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
days_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let days_i32 = i64_to_i32(
|
||||
days_i64,
|
||||
context.position(),
|
||||
"renewal_period_days",
|
||||
"Contract.renewal_period_days",
|
||||
)?;
|
||||
Ok(contract.renewal_period_days(days_i32))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"clear_renewal_period_days",
|
||||
|contract: Contract| -> Contract { contract.clear_renewal_period_days() },
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"next_renewal_date",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
date_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let date_u64 = i64_to_u64(
|
||||
date_i64,
|
||||
context.position(),
|
||||
"next_renewal_date",
|
||||
"Contract.next_renewal_date",
|
||||
)?;
|
||||
Ok(contract.next_renewal_date(date_u64))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"clear_next_renewal_date",
|
||||
|contract: Contract| -> Contract { contract.clear_next_renewal_date() },
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"add_signer",
|
||||
|contract: Contract, signer: ContractSigner| -> Contract { contract.add_signer(signer) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"signers",
|
||||
|contract: Contract, signers_array: Array| -> Contract {
|
||||
let signers_vec = signers_array
|
||||
.into_iter()
|
||||
.filter_map(|s| s.try_cast::<ContractSigner>())
|
||||
.collect();
|
||||
contract.signers(signers_vec)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"add_revision",
|
||||
|contract: Contract, revision: ContractRevision| -> Contract {
|
||||
contract.add_revision(revision)
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"revisions",
|
||||
|contract: Contract, revisions_array: Array| -> Contract {
|
||||
let revisions_vec = revisions_array
|
||||
.into_iter()
|
||||
.filter_map(|r| r.try_cast::<ContractRevision>())
|
||||
.collect();
|
||||
contract.revisions(revisions_vec)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"current_version",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
version_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let version_u32 = i64_to_u32(
|
||||
version_i64,
|
||||
context.position(),
|
||||
"current_version",
|
||||
"Contract.current_version",
|
||||
)?;
|
||||
Ok(contract.current_version(version_u32))
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"last_signed_date",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
date_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let date_u64 = i64_to_u64(
|
||||
date_i64,
|
||||
context.position(),
|
||||
"last_signed_date",
|
||||
"Contract.last_signed_date",
|
||||
)?;
|
||||
Ok(contract.last_signed_date(date_u64))
|
||||
},
|
||||
);
|
||||
engine.register_fn("clear_last_signed_date", |contract: Contract| -> Contract {
|
||||
contract.clear_last_signed_date()
|
||||
});
|
||||
|
||||
// Getters for Contract
|
||||
engine.register_get(
|
||||
"id",
|
||||
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(contract.base_data.id as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_at_ts",
|
||||
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(contract.base_data.created_at as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"updated_at_ts",
|
||||
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(contract.base_data.modified_at as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"contract_id",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.contract_id.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"title",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.title.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"description",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.description.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"contract_type",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.contract_type.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"status",
|
||||
|contract: &mut Contract| -> Result<ContractStatus, Box<EvalAltResult>> {
|
||||
Ok(contract.status.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_by",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.created_by.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"terms_and_conditions",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.terms_and_conditions.clone())
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_get(
|
||||
"start_date",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.start_date
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"end_date",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.end_date
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"renewal_period_days",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.renewal_period_days
|
||||
.map_or(Dynamic::UNIT, |days| Dynamic::from(days as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"next_renewal_date",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.next_renewal_date
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"last_signed_date",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.last_signed_date
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_get(
|
||||
"current_version",
|
||||
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(contract.current_version as i64)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_get(
|
||||
"signers",
|
||||
|contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
|
||||
let rhai_array = contract
|
||||
.signers
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Dynamic::from)
|
||||
.collect::<Array>();
|
||||
Ok(rhai_array)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"revisions",
|
||||
|contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
|
||||
let rhai_array = contract
|
||||
.revisions
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Dynamic::from)
|
||||
.collect::<Array>();
|
||||
Ok(rhai_array)
|
||||
},
|
||||
);
|
||||
|
||||
// Method set_status
|
||||
engine.register_fn(
|
||||
"set_contract_status",
|
||||
|contract: &mut Contract, status: ContractStatus| {
|
||||
contract.set_status(status);
|
||||
},
|
||||
);
|
||||
|
||||
// --- Database Interaction ---
|
||||
let captured_db_for_set = Arc::clone(&db);
|
||||
engine.register_fn(
|
||||
"set_contract",
|
||||
move |contract: Contract| -> Result<(), Box<EvalAltResult>> {
|
||||
captured_db_for_set.set(&contract).map(|_| ()).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"Failed to set Contract (ID: {}): {:?}",
|
||||
contract.base_data.id, e
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let captured_db_for_get = Arc::clone(&db);
|
||||
engine.register_fn(
|
||||
"get_contract_by_id",
|
||||
move |context: NativeCallContext, id_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let id_u32 = i64_to_u32(id_i64, context.position(), "id", "get_contract_by_id")?;
|
||||
|
||||
captured_db_for_get
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Error getting Contract (ID: {}): {}", id_u32, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Contract with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
@ -28,15 +28,4 @@ pub use flow::{Flow, FlowStep, SignatureRequirement};
|
||||
pub use governance::{AttachedFile, Ballot, Proposal, ProposalStatus, VoteEventStatus, VoteOption};
|
||||
pub use legal::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
|
||||
pub use library::collection::Collection;
|
||||
pub use library::items::{Image, Markdown, Pdf};
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use biz::register_biz_rhai_module;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use calendar::register_calendar_rhai_module;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use circle::register_circle_rhai_module;
|
||||
pub use flow::register_flow_rhai_module;
|
||||
pub use legal::register_legal_rhai_module;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use projects::register_projects_rhai_module;
|
||||
pub use library::items::{Image, Markdown, Pdf};
|
@ -1,16 +1,13 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::db::{hero::OurDB, Collection, Db};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
// Temporarily removed to fix compilation issues
|
||||
// use rhai_autobind_macros::rhai_model_export;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use rhai::CustomType;
|
||||
use rhailib_derive::RhaiApi;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::TypeBuilder;
|
||||
|
||||
/// Represents an event in a contact
|
||||
#[model]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default, RhaiApi)]
|
||||
pub struct Object {
|
||||
/// Base model data
|
||||
pub base_data: BaseModelData,
|
||||
|
@ -23,9 +23,3 @@ pub use task_enums::*;
|
||||
// pub use kanban::*;
|
||||
// pub use sprint::*;
|
||||
// pub use story::*;
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
pub mod rhai;
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use rhai::register_projects_rhai_module;
|
||||
|
@ -1,408 +0,0 @@
|
||||
// heromodels/src/models/projects/rhai.rs
|
||||
|
||||
use crate::db::hero::OurDB;
|
||||
use crate::db::{Collection, Db};
|
||||
use heromodels_core::{BaseModelDataOps, Model};
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, Position};
|
||||
use std::sync::Arc;
|
||||
|
||||
// Import models from the projects::base module
|
||||
use super::base::{ItemType, /* Label, */ Priority, Project, Status}; // Label commented out as it's unused for now
|
||||
|
||||
// Helper function for ID conversion (if needed, similar to other rhai.rs files)
|
||||
fn id_from_i64(val: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
if val < 0 {
|
||||
Err(EvalAltResult::ErrorArithmetic(
|
||||
format!("ID value cannot be negative: {}", val),
|
||||
rhai::Position::NONE,
|
||||
)
|
||||
.into())
|
||||
} else {
|
||||
Ok(val as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
// Register enums as constants (example for Priority)
|
||||
engine.register_static_module("Priority", {
|
||||
let mut module = rhai::Module::new();
|
||||
module.set_var("Critical", Priority::Critical);
|
||||
module.set_var("High", Priority::High);
|
||||
module.set_var("Medium", Priority::Medium);
|
||||
module.set_var("Low", Priority::Low);
|
||||
module.set_var("None", Priority::None);
|
||||
module.into()
|
||||
});
|
||||
|
||||
engine.register_static_module("Status", {
|
||||
let mut module = rhai::Module::new();
|
||||
module.set_var("Todo", Status::Todo);
|
||||
module.set_var("InProgress", Status::InProgress);
|
||||
module.set_var("Review", Status::Review);
|
||||
module.set_var("Done", Status::Done);
|
||||
module.set_var("Archived", Status::Archived);
|
||||
module.into()
|
||||
});
|
||||
|
||||
engine.register_static_module("ItemType", {
|
||||
let mut module = rhai::Module::new();
|
||||
module.set_var("Epic", ItemType::Epic);
|
||||
module.set_var("Story", ItemType::Story);
|
||||
module.set_var("Task", ItemType::Task);
|
||||
module.set_var("Bug", ItemType::Bug);
|
||||
module.set_var("Improvement", ItemType::Improvement);
|
||||
module.set_var("Feature", ItemType::Feature);
|
||||
module.into()
|
||||
});
|
||||
|
||||
// --- Enum Type Registration ---
|
||||
engine.register_type_with_name::<Priority>("Priority");
|
||||
engine.register_fn("to_string", |p: &mut Priority| ToString::to_string(p));
|
||||
|
||||
engine.register_type_with_name::<Status>("Status");
|
||||
engine.register_fn("to_string", |s: &mut Status| ToString::to_string(s));
|
||||
|
||||
engine.register_type_with_name::<ItemType>("ItemType");
|
||||
engine.register_fn("to_string", |it: &mut ItemType| ToString::to_string(it));
|
||||
|
||||
// --- Project Registration ---
|
||||
engine.register_type_with_name::<Project>("Project");
|
||||
|
||||
// Constructor for Project
|
||||
// Zero-argument constructor
|
||||
engine.register_fn("new_project", || -> Result<Project, Box<EvalAltResult>> {
|
||||
// Assuming Project::new() or Project::default() can be used.
|
||||
// If Project::new() requires args, this needs adjustment or Project needs Default impl.
|
||||
Ok(Project::new(0, "".to_string(), "".to_string(), 0))
|
||||
});
|
||||
|
||||
// Multi-argument constructor (renamed)
|
||||
engine.register_fn(
|
||||
"new_project_with_details",
|
||||
|id_i64: i64,
|
||||
name: String,
|
||||
description: String,
|
||||
owner_id_i64: i64|
|
||||
-> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(Project::new(
|
||||
id_from_i64(id_i64)?,
|
||||
name,
|
||||
description,
|
||||
id_from_i64(owner_id_i64)?,
|
||||
))
|
||||
},
|
||||
);
|
||||
|
||||
// Getters for Project
|
||||
engine.register_get("id", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(p.get_id() as i64)
|
||||
});
|
||||
engine.register_get(
|
||||
"name",
|
||||
|p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.name.clone()) },
|
||||
);
|
||||
engine.register_get(
|
||||
"description",
|
||||
|p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.description.clone()) },
|
||||
);
|
||||
engine.register_get(
|
||||
"owner_id",
|
||||
|p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.owner_id as i64) },
|
||||
);
|
||||
engine.register_get(
|
||||
"member_ids",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.member_ids
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"board_ids",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.board_ids
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"sprint_ids",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.sprint_ids
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"epic_ids",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.epic_ids
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"tags",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.tags
|
||||
.iter()
|
||||
.map(|tag| rhai::Dynamic::from(tag.clone()))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_at",
|
||||
|p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.created_at) },
|
||||
);
|
||||
engine.register_get(
|
||||
"modified_at",
|
||||
|p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.modified_at) },
|
||||
);
|
||||
engine.register_get(
|
||||
"comments",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.base_data
|
||||
.comments
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"status",
|
||||
|p: &mut Project| -> Result<Status, Box<EvalAltResult>> { Ok(p.status.clone()) },
|
||||
);
|
||||
engine.register_get(
|
||||
"priority",
|
||||
|p: &mut Project| -> Result<Priority, Box<EvalAltResult>> { Ok(p.priority.clone()) },
|
||||
);
|
||||
engine.register_get(
|
||||
"item_type",
|
||||
|p: &mut Project| -> Result<ItemType, Box<EvalAltResult>> { Ok(p.item_type.clone()) },
|
||||
);
|
||||
|
||||
// Builder methods for Project
|
||||
// let db_clone = db.clone(); // This was unused
|
||||
engine.register_fn(
|
||||
"name",
|
||||
|p: Project, name: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.name(name)) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"description",
|
||||
|p: Project, description: String| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.description(description))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"owner_id",
|
||||
|p: Project, owner_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.owner_id(id_from_i64(owner_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_member_id",
|
||||
|p: Project, member_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_member_id(id_from_i64(member_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"member_ids",
|
||||
|p: Project, member_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = member_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.member_ids(ids))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_board_id",
|
||||
|p: Project, board_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_board_id(id_from_i64(board_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"board_ids",
|
||||
|p: Project, board_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = board_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.board_ids(ids))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_sprint_id",
|
||||
|p: Project, sprint_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_sprint_id(id_from_i64(sprint_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"sprint_ids",
|
||||
|p: Project, sprint_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = sprint_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.sprint_ids(ids))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_epic_id",
|
||||
|p: Project, epic_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_epic_id(id_from_i64(epic_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"epic_ids",
|
||||
|p: Project, epic_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = epic_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
let val_i64 = id_dyn.clone().try_cast::<i64>().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected integer for ID".to_string(),
|
||||
id_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
id_from_i64(val_i64)
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.epic_ids(ids))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_tag",
|
||||
|p: Project, tag: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_tag(tag)) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"tags",
|
||||
|p: Project, tags_dyn: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let tags_vec = tags_dyn
|
||||
.into_iter()
|
||||
.map(|tag_dyn: Dynamic| {
|
||||
tag_dyn.clone().into_string().map_err(|_err| {
|
||||
// _err is Rhai's internal error, we create a new one
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"Expected string for tag".to_string(),
|
||||
tag_dyn.type_name().to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<String>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.tags(tags_vec))
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn(
|
||||
"status",
|
||||
|p: Project, status: Status| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.status(status))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"priority",
|
||||
|p: Project, priority: Priority| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.priority(priority))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"item_type",
|
||||
|p: Project, item_type: ItemType| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.item_type(item_type))
|
||||
},
|
||||
);
|
||||
// Base ModelData builders
|
||||
engine.register_fn(
|
||||
"add_base_comment",
|
||||
|p: Project, comment_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_base_comment(id_from_i64(comment_id_i64)?))
|
||||
},
|
||||
);
|
||||
|
||||
// --- Database Interaction Functions ---
|
||||
let db_clone_set = db.clone();
|
||||
engine.register_fn(
|
||||
"set_project",
|
||||
move |project: Project| -> Result<(), Box<EvalAltResult>> {
|
||||
let collection = db_clone_set.collection::<Project>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to access project collection".to_string(),
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))
|
||||
})?;
|
||||
|
||||
collection.set(&project).map(|_| ()).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to save project".to_string(),
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get = db.clone();
|
||||
engine.register_fn(
|
||||
"get_project_by_id",
|
||||
move |id_i64: i64| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id = id_from_i64(id_i64)?;
|
||||
let collection = db_clone_get.collection::<Project>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to access project collection".to_string(),
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))
|
||||
})?;
|
||||
|
||||
match collection.get_by_id(id) {
|
||||
Ok(Some(project)) => Ok(Dynamic::from(project)),
|
||||
Ok(None) => Ok(Dynamic::UNIT), // Represents '()' in Rhai
|
||||
Err(e) => Err(Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to retrieve project by ID".to_string(),
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))),
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: Register Rhai bindings for the `Label` model if needed, or remove unused import.
|
||||
// Register Label type and its methods/getters
|
||||
// engine.register_type_with_name::<Label>("Label")
|
||||
// .register_fn("new_label", Label::new) // Simplified
|
||||
// // ... other Label methods and getters ...
|
||||
// ;
|
||||
|
||||
// TODO: Add DB interaction functions like set_project, get_project_by_id etc.
|
||||
}
|
Loading…
Reference in New Issue
Block a user