diff --git a/heromodels/Cargo.lock b/heromodels/Cargo.lock index 4d7c0f9..2bf5b50 100644 --- a/heromodels/Cargo.lock +++ b/heromodels/Cargo.lock @@ -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", ] diff --git a/heromodels/Cargo.toml b/heromodels/Cargo.toml index 4cd485a..c113a97 100644 --- a/heromodels/Cargo.toml +++ b/heromodels/Cargo.toml @@ -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> rhai_client_macros = { path = "../rhai_client_macros" } strum = "0.26" diff --git a/heromodels/examples/biz_rhai/biz.rhai b/heromodels/examples/biz_rhai/biz.rhai deleted file mode 100644 index a044707..0000000 --- a/heromodels/examples/biz_rhai/biz.rhai +++ /dev/null @@ -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."); diff --git a/heromodels/examples/biz_rhai/example.rs b/heromodels/examples/biz_rhai/example.rs deleted file mode 100644 index f316fae..0000000 --- a/heromodels/examples/biz_rhai/example.rs +++ /dev/null @@ -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> { - println!("Executing Rhai script: examples/biz_rhai/biz.rhai"); - - // Create a new Rhai engine - let mut engine = Engine::new(); - - // Create an Arc> 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) - } - } -} diff --git a/heromodels/examples/calendar_rhai/calendar.rhai b/heromodels/examples/calendar_rhai/calendar.rhai deleted file mode 100644 index 08f79bc..0000000 --- a/heromodels/examples/calendar_rhai/calendar.rhai +++ /dev/null @@ -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. 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()); \ No newline at end of file diff --git a/heromodels/examples/calendar_rhai/example.rs b/heromodels/examples/calendar_rhai/example.rs deleted file mode 100644 index dc7c05f..0000000 --- a/heromodels/examples/calendar_rhai/example.rs +++ /dev/null @@ -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> { - // 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, _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, 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, 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) -> Vec { - // 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 => Calendar), - ); - - engine.register_fn("delete_calendar_by_id", |_db: Arc, _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(()) -} diff --git a/heromodels/examples/finance_rhai/example.rs b/heromodels/examples/finance_rhai/example.rs deleted file mode 100644 index 10f8fde..0000000 --- a/heromodels/examples/finance_rhai/example.rs +++ /dev/null @@ -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>>, - pub assets: Arc>>, - pub listings: Arc>>, - // 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> { - 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(()) -} diff --git a/heromodels/examples/finance_rhai/finance.rhai b/heromodels/examples/finance_rhai/finance.rhai deleted file mode 100644 index 076d9e7..0000000 --- a/heromodels/examples/finance_rhai/finance.rhai +++ /dev/null @@ -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 ---"); diff --git a/heromodels/examples/flow_rhai/example.rs b/heromodels/examples/flow_rhai/example.rs deleted file mode 100644 index 6c106d4..0000000 --- a/heromodels/examples/flow_rhai/example.rs +++ /dev/null @@ -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> { - // 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(()) -} diff --git a/heromodels/examples/flow_rhai/flow.rs b/heromodels/examples/flow_rhai/flow.rs deleted file mode 100644 index 8a66acf..0000000 --- a/heromodels/examples/flow_rhai/flow.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Hero Models - Flow Rhai Example -print("Hero Models - Flow Rhai Example"); -print("============================="); - -// Helper to format Option (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."); diff --git a/heromodels/examples/gov_example.rhai b/heromodels/examples/gov_example.rhai deleted file mode 100644 index 5187ea5..0000000 --- a/heromodels/examples/gov_example.rhai +++ /dev/null @@ -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!"); -() diff --git a/heromodels/examples/gov_rhai_example.rs b/heromodels/examples/gov_rhai_example.rs deleted file mode 100644 index 0f09b78..0000000 --- a/heromodels/examples/gov_rhai_example.rs +++ /dev/null @@ -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> { - // 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, duration: Duration| dt + duration); - - // Add the DB instance to the engine's scope - engine.register_fn("get_db", move || -> Arc { 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(()) -} diff --git a/heromodels/examples/governance_rhai/example.rs b/heromodels/examples/governance_rhai/example.rs deleted file mode 100644 index f6ee8fe..0000000 --- a/heromodels/examples/governance_rhai/example.rs +++ /dev/null @@ -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> { - // 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, proposal: Proposal| { - println!("Proposal saved: {}", proposal.title); - }); - - engine.register_fn( - "get_proposal_by_id", - |_db: Arc, 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, 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) -> Vec { - // 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 => Proposal), - ); - - engine.register_fn("delete_proposal_by_id", |_db: Arc, _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(()) -} diff --git a/heromodels/examples/governance_rhai/governance.rhai b/heromodels/examples/governance_rhai/governance.rhai deleted file mode 100644 index 4b7d1bb..0000000 --- a/heromodels/examples/governance_rhai/governance.rhai +++ /dev/null @@ -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."); diff --git a/heromodels/examples/governance_rhai_client/example.rs b/heromodels/examples/governance_rhai_client/example.rs deleted file mode 100644 index d68e3ab..0000000 --- a/heromodels/examples/governance_rhai_client/example.rs +++ /dev/null @@ -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, proposal: Proposal) { - println!("Proposal saved: {}", proposal.title); -} - -fn get_all_proposals(_db: Arc) -> Vec { - // 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, id: i64) { - // In a real implementation, this would delete the proposal from the database - println!("Proposal deleted with ID: {}", id); -} - -fn main() -> Result<(), Box> { - // 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, 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, 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 => 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(()) -} diff --git a/heromodels/examples/legal_rhai/example.rs b/heromodels/examples/legal_rhai/example.rs deleted file mode 100644 index 0b42f0d..0000000 --- a/heromodels/examples/legal_rhai/example.rs +++ /dev/null @@ -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> { - // 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(()) -} diff --git a/heromodels/examples/legal_rhai/legal.rhai b/heromodels/examples/legal_rhai/legal.rhai deleted file mode 100644 index 11e494a..0000000 --- a/heromodels/examples/legal_rhai/legal.rhai +++ /dev/null @@ -1,119 +0,0 @@ -// heromodels - Legal Module Rhai Example -print("Hero Models - Legal Rhai Example"); -print("==============================="); - -// Helper to format Option (Dynamic in Rhai: String or ()) for printing -fn format_optional_string(val, placeholder) { - if val == () { - placeholder - } else { - val - } -} - -// Helper to format Option (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."); diff --git a/heromodels/examples/library_rhai/example.rs b/heromodels/examples/library_rhai/example.rs deleted file mode 100644 index 13955f6..0000000 --- a/heromodels/examples/library_rhai/example.rs +++ /dev/null @@ -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> { - // 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(()) -} diff --git a/heromodels/examples/library_rhai/library.rhai b/heromodels/examples/library_rhai/library.rhai deleted file mode 100644 index eb0a579..0000000 --- a/heromodels/examples/library_rhai/library.rhai +++ /dev/null @@ -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); \ No newline at end of file diff --git a/heromodels/examples/project_rhai/example.rs b/heromodels/examples/project_rhai/example.rs deleted file mode 100644 index d70bf0e..0000000 --- a/heromodels/examples/project_rhai/example.rs +++ /dev/null @@ -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> { - println!("Executing Rhai script: examples/project_rhai/project_test.rhai"); - - // Create a new Rhai engine - let mut engine = Engine::new(); - - // Create an Arc 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) - } - } -} diff --git a/heromodels/examples/project_rhai/project_test.rhai b/heromodels/examples/project_rhai/project_test.rhai deleted file mode 100644 index 757c778..0000000 --- a/heromodels/examples/project_rhai/project_test.rhai +++ /dev/null @@ -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 ---"); diff --git a/heromodels/examples/project_rhai_wasm/.cargo/config.toml b/heromodels/examples/project_rhai_wasm/.cargo/config.toml deleted file mode 100644 index f603c8f..0000000 --- a/heromodels/examples/project_rhai_wasm/.cargo/config.toml +++ /dev/null @@ -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) diff --git a/heromodels/examples/project_rhai_wasm/Cargo.toml b/heromodels/examples/project_rhai_wasm/Cargo.toml deleted file mode 100644 index cf9b426..0000000 --- a/heromodels/examples/project_rhai_wasm/Cargo.toml +++ /dev/null @@ -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' diff --git a/heromodels/examples/project_rhai_wasm/index.html b/heromodels/examples/project_rhai_wasm/index.html deleted file mode 100644 index ec6ccdb..0000000 --- a/heromodels/examples/project_rhai_wasm/index.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - HeroModels Project Rhai WASM Test - - - -

HeroModels Project Rhai WASM Test

-

Open your browser's developer console to see detailed logs from the Rhai script.

- - - - - diff --git a/heromodels/examples/project_rhai_wasm/src/lib.rs b/heromodels/examples/project_rhai_wasm/src/lib.rs deleted file mode 100644 index 21aff17..0000000 --- a/heromodels/examples/project_rhai_wasm/src/lib.rs +++ /dev/null @@ -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(()) -} diff --git a/heromodels/src/models/access/access.rs b/heromodels/src/models/access/access.rs index 5aede48..f374269 100644 --- a/heromodels/src/models/access/access.rs +++ b/heromodels/src/models/access/access.rs @@ -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::() + .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, + public_key: &str, +) -> bool { + let circle = db + .collection::() + .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; +} diff --git a/heromodels/src/models/access/mod.rs b/heromodels/src/models/access/mod.rs index 4ecbdc2..7e39043 100644 --- a/heromodels/src/models/access/mod.rs +++ b/heromodels/src/models/access/mod.rs @@ -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; diff --git a/heromodels/src/models/access/rhai.rs b/heromodels/src/models/access/rhai.rs deleted file mode 100644 index fff6411..0000000 --- a/heromodels/src/models/access/rhai.rs +++ /dev/null @@ -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::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> { - 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> { - // 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> { - // 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> { - // 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> { - // 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, - ) -> Result> { - // 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) { - // 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> { - // 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> { - // Use the Collection trait method directly - let result = db_clone_delete_access - .collection::() - .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> { - 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> { - let collection = db_clone_list_accesss.collection::().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."); -} diff --git a/heromodels/src/models/biz/company.rs b/heromodels/src/models/biz/company.rs index d5fe452..a43171d 100644 --- a/heromodels/src/models/biz/company.rs +++ b/heromodels/src/models/biz/company.rs @@ -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> { + 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 diff --git a/heromodels/src/models/biz/mod.rs b/heromodels/src/models/biz/mod.rs index abadf00..f81389c 100644 --- a/heromodels/src/models/biz/mod.rs +++ b/heromodels/src/models/biz/mod.rs @@ -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}; \ No newline at end of file diff --git a/heromodels/src/models/biz/product.rs b/heromodels/src/models/biz/product.rs index c282918..5a498d9 100644 --- a/heromodels/src/models/biz/product.rs +++ b/heromodels/src/models/biz/product.rs @@ -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, diff --git a/heromodels/src/models/biz/rhai.rs b/heromodels/src/models/biz/rhai.rs deleted file mode 100644 index 22ef6d8..0000000 --- a/heromodels/src/models/biz/rhai.rs +++ /dev/null @@ -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::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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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, - ) -> Result> { - 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 { - 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 { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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, - ) -> Result> { - 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 { - 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 { - sale.base_data - .comments - .iter() - .map(|&id| id as i64) - .collect() - } -} - -pub fn register_biz_rhai_module(engine: &mut Engine, db: Arc) { - // 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> { - let company_collection_set = db_for_set_company - .collection::() - .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> { - let company_collection_get = db_for_get_company - .collection::() - .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> { - let shareholder_collection_set = db_for_set_shareholder - .collection::() - .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> { - let shareholder_collection_get = db_for_get_shareholder - .collection::() - .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> { - let product_collection_set = db_for_set_product - .collection::() - .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> { - let product_collection_get = db_for_get_product - .collection::() - .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> { - let sale_collection_set = db_for_set_sale - .collection::() - .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> { - let sale_collection_get = db_for_get_sale - .collection::() - .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."); -} diff --git a/heromodels/src/models/biz/sale.rs b/heromodels/src/models/biz/sale.rs index 938f58d..d7168cb 100644 --- a/heromodels/src/models/biz/sale.rs +++ b/heromodels/src/models/biz/sale.rs @@ -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, diff --git a/heromodels/src/models/calendar/mod.rs b/heromodels/src/models/calendar/mod.rs index fc7beb4..5374dc2 100644 --- a/heromodels/src/models/calendar/mod.rs +++ b/heromodels/src/models/calendar/mod.rs @@ -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; diff --git a/heromodels/src/models/calendar/rhai.rs b/heromodels/src/models/calendar/rhai.rs deleted file mode 100644 index a8e6af7..0000000 --- a/heromodels/src/models/calendar/rhai.rs +++ /dev/null @@ -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::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> { - 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> { - 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> { - 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> { - // 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> { - // 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> { - 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 { - 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 { - event.location.clone() - } - #[rhai_fn(get = "attendees", pure)] - pub fn get_event_attendees(event: &mut RhaiEvent) -> Vec { - 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> { - 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> { - 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> { - 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> { - // 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> { - // 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> { - // 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> { - // 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 { - calendar.events.clone() - } - - #[rhai_fn(get = "description", pure)] - pub fn get_calendar_description(calendar: &mut RhaiCalendar) -> Option { - 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) { - // 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> { - // 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> { - // Use the Collection trait method directly - let result = db_clone_delete_event - .collection::() - .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> { - 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> { - // 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> { - // Use the Collection trait method directly - let result = db_clone_delete_calendar - .collection::() - .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> { - 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> { - let collection = db_clone_list_calendars - .collection::() - .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> { - let collection = db_clone_list_events.collection::().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."); -} diff --git a/heromodels/src/models/circle/circle.rs b/heromodels/src/models/circle/circle.rs index b9a0696..d5e996d 100644 --- a/heromodels/src/models/circle/circle.rs +++ b/heromodels/src/models/circle/circle.rs @@ -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)] diff --git a/heromodels/src/models/contact/contact.rs b/heromodels/src/models/contact/contact.rs index 1b9da79..823276f 100644 --- a/heromodels/src/models/contact/contact.rs +++ b/heromodels/src/models/contact/contact.rs @@ -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 { diff --git a/heromodels/src/models/contact/mod.rs b/heromodels/src/models/contact/mod.rs index 4a3aa34..533e45a 100644 --- a/heromodels/src/models/contact/mod.rs +++ b/heromodels/src/models/contact/mod.rs @@ -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; diff --git a/heromodels/src/models/contact/rhai.rs b/heromodels/src/models/contact/rhai.rs deleted file mode 100644 index fb0b01d..0000000 --- a/heromodels/src/models/contact/rhai.rs +++ /dev/null @@ -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::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> { - 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> { - 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> { - // 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 { - 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 { - group.description.clone() - } - - // --- Contact Functions --- - #[rhai_fn(name = "new_contact", return_raw)] - pub fn new_contact() -> Result> { - 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> { - // 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> { - // 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) { - // 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> { - // 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> { - // Use the Collection trait method directly - let result = db_clone_delete_group - .collection::() - .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> { - 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> { - // 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> { - // Use the Collection trait method directly - let result = db_clone_delete_contact - .collection::() - .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> { - 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> { - let collection = db_clone_list_contacts - .collection::() - .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> { - let collection = db_clone_list_groups.collection::().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."); -} diff --git a/heromodels/src/models/finance/mod.rs b/heromodels/src/models/finance/mod.rs index 871c4f9..a9706a7 100644 --- a/heromodels/src/models/finance/mod.rs +++ b/heromodels/src/models/finance/mod.rs @@ -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; diff --git a/heromodels/src/models/finance/rhai.rs b/heromodels/src/models/finance/rhai.rs deleted file mode 100644 index e45057e..0000000 --- a/heromodels/src/models/finance/rhai.rs +++ /dev/null @@ -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::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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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::().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::().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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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> { - 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) { - // --- 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> { - let collection = db_set_account.collection::().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> { - let id_u32 = id_from_i64_to_u32(id_rhai)?; - let collection = db_get_account.collection::().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> { - let collection = db_set_asset.collection::().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> { - let id_u32 = id_from_i64_to_u32(id_rhai)?; - let collection = db_get_asset.collection::().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> { - let collection = db_set_listing.collection::().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> { - let id_u32 = id_from_i64_to_u32(id_rhai)?; - let collection = db_get_listing.collection::().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."); -} diff --git a/heromodels/src/models/flow/flow.rs b/heromodels/src/models/flow/flow.rs index 28d16f9..0eec251 100644 --- a/heromodels/src/models/flow/flow.rs +++ b/heromodels/src/models/flow/flow.rs @@ -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. diff --git a/heromodels/src/models/flow/flow_step.rs b/heromodels/src/models/flow/flow_step.rs index ce554f6..e5bc2b6 100644 --- a/heromodels/src/models/flow/flow_step.rs +++ b/heromodels/src/models/flow/flow_step.rs @@ -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. diff --git a/heromodels/src/models/flow/mod.rs b/heromodels/src/models/flow/mod.rs index 8dd2206..fd3cb68 100644 --- a/heromodels/src/models/flow/mod.rs +++ b/heromodels/src/models/flow/mod.rs @@ -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; \ No newline at end of file diff --git a/heromodels/src/models/flow/rhai.rs b/heromodels/src/models/flow/rhai.rs deleted file mode 100644 index 03f96b9..0000000 --- a/heromodels/src/models/flow/rhai.rs +++ /dev/null @@ -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::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> { - 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> { - 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> { - 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::() - } - - // --- 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> { - 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> { - 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> { - 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> { - 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> { - 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) { - // 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> { - // 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> { - 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> { - let id_u32 = id_from_i64_to_u32(id_i64)?; - // Use the Collection trait method directly - let collection = db_clone.collection::().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> { - let collection = db_clone.collection::().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> { - // 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> { - 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> { - let id_u32 = id_from_i64_to_u32(id_i64)?; - // Use the Collection trait method directly - let collection = db_clone.collection::().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> { - let collection = db_clone.collection::().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> { - // 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> { - 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> { - let id_u32 = id_from_i64_to_u32(id_i64)?; - // Use the Collection trait method directly - let collection = db_clone.collection::().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> { - let collection = db_clone.collection::().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."); -} diff --git a/heromodels/src/models/flow/signature_requirement.rs b/heromodels/src/models/flow/signature_requirement.rs index 453b99d..318b8a5 100644 --- a/heromodels/src/models/flow/signature_requirement.rs +++ b/heromodels/src/models/flow/signature_requirement.rs @@ -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. diff --git a/heromodels/src/models/legal/mod.rs b/heromodels/src/models/legal/mod.rs index 4b16576..17ada52 100644 --- a/heromodels/src/models/legal/mod.rs +++ b/heromodels/src/models/legal/mod.rs @@ -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; diff --git a/heromodels/src/models/legal/rhai.rs b/heromodels/src/models/legal/rhai.rs deleted file mode 100644 index 279fa0a..0000000 --- a/heromodels/src/models/legal/rhai.rs +++ /dev/null @@ -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> { - 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> { - 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> { - 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) { - // --- 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"); // 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"); // Expose the type itself - - // --- ContractRevision --- - engine.register_type_with_name::("ContractRevision"); - engine.register_fn( - "new_contract_revision", - move |context: NativeCallContext, - version_i64: i64, - content: String, - created_at_i64: i64, - created_by: String| - -> Result> { - 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> { - Ok(revision.version as i64) - }, - ); - engine.register_get( - "content", - |revision: &mut ContractRevision| -> Result> { - Ok(revision.content.clone()) - }, - ); - engine.register_get( - "created_at", - |revision: &mut ContractRevision| -> Result> { - Ok(revision.created_at as i64) - }, - ); - engine.register_get( - "created_by", - |revision: &mut ContractRevision| -> Result> { - Ok(revision.created_by.clone()) - }, - ); - engine.register_get( - "comments", - |revision: &mut ContractRevision| -> Result> { - Ok(revision - .comments - .clone() - .map_or(Dynamic::UNIT, Dynamic::from)) - }, - ); - - // --- ContractSigner --- - engine.register_type_with_name::("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> { - 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> { - Ok(signer.id.clone()) - }, - ); - engine.register_get( - "name", - |signer: &mut ContractSigner| -> Result> { - Ok(signer.name.clone()) - }, - ); - engine.register_get( - "email", - |signer: &mut ContractSigner| -> Result> { - Ok(signer.email.clone()) - }, - ); - engine.register_get( - "status", - |signer: &mut ContractSigner| -> Result> { - Ok(signer.status.clone()) - }, - ); - engine.register_get( - "signed_at_ts", - |signer: &mut ContractSigner| -> Result> { - Ok(signer - .signed_at - .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) - }, - ); - engine.register_get( - "comments", - |signer: &mut ContractSigner| -> Result> { - Ok(signer.comments.clone().map_or(Dynamic::UNIT, Dynamic::from)) - }, - ); - engine.register_get( - "signed_at", - |signer: &mut ContractSigner| -> Result> { - Ok(signer - .signed_at - .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts))) - }, - ); - - // --- Contract --- - engine.register_type_with_name::("Contract"); - engine.register_fn( - "new_contract", - move |context: NativeCallContext, - base_id_i64: i64, - contract_id: String| - -> Result> { - 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> { - 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> { - 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> { - 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> { - 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::()) - .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::()) - .collect(); - contract.revisions(revisions_vec) - }, - ); - - engine.register_fn( - "current_version", - |context: NativeCallContext, - contract: Contract, - version_i64: i64| - -> Result> { - 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> { - 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> { - Ok(contract.base_data.id as i64) - }, - ); - engine.register_get( - "created_at_ts", - |contract: &mut Contract| -> Result> { - Ok(contract.base_data.created_at as i64) - }, - ); - engine.register_get( - "updated_at_ts", - |contract: &mut Contract| -> Result> { - Ok(contract.base_data.modified_at as i64) - }, - ); - engine.register_get( - "contract_id", - |contract: &mut Contract| -> Result> { - Ok(contract.contract_id.clone()) - }, - ); - engine.register_get( - "title", - |contract: &mut Contract| -> Result> { - Ok(contract.title.clone()) - }, - ); - engine.register_get( - "description", - |contract: &mut Contract| -> Result> { - Ok(contract.description.clone()) - }, - ); - engine.register_get( - "contract_type", - |contract: &mut Contract| -> Result> { - Ok(contract.contract_type.clone()) - }, - ); - engine.register_get( - "status", - |contract: &mut Contract| -> Result> { - Ok(contract.status.clone()) - }, - ); - engine.register_get( - "created_by", - |contract: &mut Contract| -> Result> { - Ok(contract.created_by.clone()) - }, - ); - engine.register_get( - "terms_and_conditions", - |contract: &mut Contract| -> Result> { - Ok(contract.terms_and_conditions.clone()) - }, - ); - - engine.register_get( - "start_date", - |contract: &mut Contract| -> Result> { - Ok(contract - .start_date - .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) - }, - ); - engine.register_get( - "end_date", - |contract: &mut Contract| -> Result> { - Ok(contract - .end_date - .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) - }, - ); - engine.register_get( - "renewal_period_days", - |contract: &mut Contract| -> Result> { - 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> { - 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> { - Ok(contract - .last_signed_date - .map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64))) - }, - ); - - engine.register_get( - "current_version", - |contract: &mut Contract| -> Result> { - Ok(contract.current_version as i64) - }, - ); - - engine.register_get( - "signers", - |contract: &mut Contract| -> Result> { - let rhai_array = contract - .signers - .iter() - .cloned() - .map(Dynamic::from) - .collect::(); - Ok(rhai_array) - }, - ); - engine.register_get( - "revisions", - |contract: &mut Contract| -> Result> { - let rhai_array = contract - .revisions - .iter() - .cloned() - .map(Dynamic::from) - .collect::(); - 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> { - 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> { - 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, - )) - }) - }, - ); -} diff --git a/heromodels/src/models/mod.rs b/heromodels/src/models/mod.rs index 8587cf3..6b9363c 100644 --- a/heromodels/src/models/mod.rs +++ b/heromodels/src/models/mod.rs @@ -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}; \ No newline at end of file diff --git a/heromodels/src/models/object/object.rs b/heromodels/src/models/object/object.rs index 8c5bcd5..8ebca6d 100644 --- a/heromodels/src/models/object/object.rs +++ b/heromodels/src/models/object/object.rs @@ -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, diff --git a/heromodels/src/models/projects/mod.rs b/heromodels/src/models/projects/mod.rs index afdab0e..6e58770 100644 --- a/heromodels/src/models/projects/mod.rs +++ b/heromodels/src/models/projects/mod.rs @@ -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; diff --git a/heromodels/src/models/projects/rhai.rs b/heromodels/src/models/projects/rhai.rs deleted file mode 100644 index 6f38196..0000000 --- a/heromodels/src/models/projects/rhai.rs +++ /dev/null @@ -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> { - 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) { - // 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"); - engine.register_fn("to_string", |p: &mut Priority| ToString::to_string(p)); - - engine.register_type_with_name::("Status"); - engine.register_fn("to_string", |s: &mut Status| ToString::to_string(s)); - - engine.register_type_with_name::("ItemType"); - engine.register_fn("to_string", |it: &mut ItemType| ToString::to_string(it)); - - // --- Project Registration --- - engine.register_type_with_name::("Project"); - - // Constructor for Project - // Zero-argument constructor - engine.register_fn("new_project", || -> Result> { - // 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> { - 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> { - Ok(p.get_id() as i64) - }); - engine.register_get( - "name", - |p: &mut Project| -> Result> { Ok(p.name.clone()) }, - ); - engine.register_get( - "description", - |p: &mut Project| -> Result> { Ok(p.description.clone()) }, - ); - engine.register_get( - "owner_id", - |p: &mut Project| -> Result> { Ok(p.owner_id as i64) }, - ); - engine.register_get( - "member_ids", - |p: &mut Project| -> Result> { - Ok(p.member_ids - .iter() - .map(|&id| rhai::Dynamic::from(id as i64)) - .collect()) - }, - ); - engine.register_get( - "board_ids", - |p: &mut Project| -> Result> { - Ok(p.board_ids - .iter() - .map(|&id| rhai::Dynamic::from(id as i64)) - .collect()) - }, - ); - engine.register_get( - "sprint_ids", - |p: &mut Project| -> Result> { - Ok(p.sprint_ids - .iter() - .map(|&id| rhai::Dynamic::from(id as i64)) - .collect()) - }, - ); - engine.register_get( - "epic_ids", - |p: &mut Project| -> Result> { - Ok(p.epic_ids - .iter() - .map(|&id| rhai::Dynamic::from(id as i64)) - .collect()) - }, - ); - engine.register_get( - "tags", - |p: &mut Project| -> Result> { - Ok(p.tags - .iter() - .map(|tag| rhai::Dynamic::from(tag.clone())) - .collect()) - }, - ); - engine.register_get( - "created_at", - |p: &mut Project| -> Result> { Ok(p.base_data.created_at) }, - ); - engine.register_get( - "modified_at", - |p: &mut Project| -> Result> { Ok(p.base_data.modified_at) }, - ); - engine.register_get( - "comments", - |p: &mut Project| -> Result> { - Ok(p.base_data - .comments - .iter() - .map(|&id| rhai::Dynamic::from(id as i64)) - .collect()) - }, - ); - engine.register_get( - "status", - |p: &mut Project| -> Result> { Ok(p.status.clone()) }, - ); - engine.register_get( - "priority", - |p: &mut Project| -> Result> { Ok(p.priority.clone()) }, - ); - engine.register_get( - "item_type", - |p: &mut Project| -> Result> { 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> { Ok(p.name(name)) }, - ); - engine.register_fn( - "description", - |p: Project, description: String| -> Result> { - Ok(p.description(description)) - }, - ); - engine.register_fn( - "owner_id", - |p: Project, owner_id_i64: i64| -> Result> { - Ok(p.owner_id(id_from_i64(owner_id_i64)?)) - }, - ); - engine.register_fn( - "add_member_id", - |p: Project, member_id_i64: i64| -> Result> { - Ok(p.add_member_id(id_from_i64(member_id_i64)?)) - }, - ); - engine.register_fn( - "member_ids", - |p: Project, member_ids_i64: rhai::Array| -> Result> { - let ids = member_ids_i64 - .into_iter() - .map(|id_dyn: Dynamic| { - let val_i64 = id_dyn.clone().try_cast::().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::, Box>>()?; - Ok(p.member_ids(ids)) - }, - ); - engine.register_fn( - "add_board_id", - |p: Project, board_id_i64: i64| -> Result> { - Ok(p.add_board_id(id_from_i64(board_id_i64)?)) - }, - ); - engine.register_fn( - "board_ids", - |p: Project, board_ids_i64: rhai::Array| -> Result> { - let ids = board_ids_i64 - .into_iter() - .map(|id_dyn: Dynamic| { - let val_i64 = id_dyn.clone().try_cast::().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::, Box>>()?; - Ok(p.board_ids(ids)) - }, - ); - engine.register_fn( - "add_sprint_id", - |p: Project, sprint_id_i64: i64| -> Result> { - Ok(p.add_sprint_id(id_from_i64(sprint_id_i64)?)) - }, - ); - engine.register_fn( - "sprint_ids", - |p: Project, sprint_ids_i64: rhai::Array| -> Result> { - let ids = sprint_ids_i64 - .into_iter() - .map(|id_dyn: Dynamic| { - let val_i64 = id_dyn.clone().try_cast::().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::, Box>>()?; - Ok(p.sprint_ids(ids)) - }, - ); - engine.register_fn( - "add_epic_id", - |p: Project, epic_id_i64: i64| -> Result> { - Ok(p.add_epic_id(id_from_i64(epic_id_i64)?)) - }, - ); - engine.register_fn( - "epic_ids", - |p: Project, epic_ids_i64: rhai::Array| -> Result> { - let ids = epic_ids_i64 - .into_iter() - .map(|id_dyn: Dynamic| { - let val_i64 = id_dyn.clone().try_cast::().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::, Box>>()?; - Ok(p.epic_ids(ids)) - }, - ); - engine.register_fn( - "add_tag", - |p: Project, tag: String| -> Result> { Ok(p.add_tag(tag)) }, - ); - engine.register_fn( - "tags", - |p: Project, tags_dyn: rhai::Array| -> Result> { - 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::, Box>>()?; - Ok(p.tags(tags_vec)) - }, - ); - - engine.register_fn( - "status", - |p: Project, status: Status| -> Result> { - Ok(p.status(status)) - }, - ); - engine.register_fn( - "priority", - |p: Project, priority: Priority| -> Result> { - Ok(p.priority(priority)) - }, - ); - engine.register_fn( - "item_type", - |p: Project, item_type: ItemType| -> Result> { - Ok(p.item_type(item_type)) - }, - ); - // Base ModelData builders - engine.register_fn( - "add_base_comment", - |p: Project, comment_id_i64: i64| -> Result> { - 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> { - let collection = db_clone_set.collection::().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> { - let id = id_from_i64(id_i64)?; - let collection = db_clone_get.collection::().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::