fmt, fixes and additions
This commit is contained in:
parent
6b3cbfc4b2
commit
e91a44ce37
@ -1,5 +1,5 @@
|
||||
use heromodels_derive::model;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// Define the necessary structs and traits for testing
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@ -42,10 +42,6 @@ path = "examples/finance_example/main.rs"
|
||||
name = "calendar_rhai"
|
||||
path = "examples/calendar_rhai/example.rs"
|
||||
|
||||
[[example]]
|
||||
name = "calendar_rhai_client"
|
||||
path = "examples/calendar_rhai_client/example.rs"
|
||||
|
||||
[[example]]
|
||||
name = "flow_rhai"
|
||||
path = "examples/flow_rhai/example.rs"
|
||||
|
@ -68,10 +68,26 @@ fn main() {
|
||||
.build();
|
||||
|
||||
// Save all users to database and get their assigned IDs and updated models
|
||||
let (user1_id, db_user1) = db.collection().expect("can open user collection").set(&user1).expect("can set user");
|
||||
let (user2_id, db_user2) = db.collection().expect("can open user collection").set(&user2).expect("can set user");
|
||||
let (user3_id, db_user3) = db.collection().expect("can open user collection").set(&user3).expect("can set user");
|
||||
let (user4_id, db_user4) = db.collection().expect("can open user collection").set(&user4).expect("can set user");
|
||||
let (user1_id, db_user1) = db
|
||||
.collection()
|
||||
.expect("can open user collection")
|
||||
.set(&user1)
|
||||
.expect("can set user");
|
||||
let (user2_id, db_user2) = db
|
||||
.collection()
|
||||
.expect("can open user collection")
|
||||
.set(&user2)
|
||||
.expect("can set user");
|
||||
let (user3_id, db_user3) = db
|
||||
.collection()
|
||||
.expect("can open user collection")
|
||||
.set(&user3)
|
||||
.expect("can set user");
|
||||
let (user4_id, db_user4) = db
|
||||
.collection()
|
||||
.expect("can open user collection")
|
||||
.set(&user4)
|
||||
.expect("can set user");
|
||||
|
||||
println!("User 1 assigned ID: {}", user1_id);
|
||||
println!("User 2 assigned ID: {}", user2_id);
|
||||
@ -170,7 +186,8 @@ fn main() {
|
||||
.build();
|
||||
|
||||
// Save the comment and get its assigned ID and updated model
|
||||
let (comment_id, db_comment) = db.collection()
|
||||
let (comment_id, db_comment) = db
|
||||
.collection()
|
||||
.expect("can open comment collection")
|
||||
.set(&comment)
|
||||
.expect("can set comment");
|
||||
@ -186,7 +203,8 @@ fn main() {
|
||||
updated_user.base_data.add_comment(db_comment.get_id());
|
||||
|
||||
// Save the updated user and get the new version
|
||||
let (_, user_with_comment) = db.collection::<User>()
|
||||
let (_, user_with_comment) = db
|
||||
.collection::<User>()
|
||||
.expect("can open user collection")
|
||||
.set(&updated_user)
|
||||
.expect("can set updated user");
|
||||
|
@ -1,8 +1,8 @@
|
||||
use rhai::{Engine, EvalAltResult, Scope};
|
||||
use std::sync::Arc;
|
||||
use heromodels::db::hero::OurDB; // Corrected path for OurDB
|
||||
use heromodels::models::biz::register_biz_rhai_module; // Corrected path
|
||||
use rhai::{Engine, EvalAltResult, Scope};
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
println!("Executing Rhai script: examples/biz_rhai/biz.rhai");
|
||||
@ -20,8 +20,12 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
// 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())))?;
|
||||
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();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use chrono::{Duration, Utc};
|
||||
use heromodels::db::{Collection, Db};
|
||||
use heromodels::models::calendar::{Attendee, AttendanceStatus, Calendar, Event};
|
||||
use heromodels::models::calendar::{AttendanceStatus, Attendee, Calendar, Event};
|
||||
use heromodels_core::Model;
|
||||
|
||||
fn main() {
|
||||
@ -12,10 +12,8 @@ fn main() {
|
||||
println!("====================================");
|
||||
|
||||
// --- Create Attendees ---
|
||||
let attendee1 = Attendee::new("user_123".to_string())
|
||||
.status(AttendanceStatus::Accepted);
|
||||
let attendee2 = Attendee::new("user_456".to_string())
|
||||
.status(AttendanceStatus::Tentative);
|
||||
let attendee1 = Attendee::new("user_123".to_string()).status(AttendanceStatus::Accepted);
|
||||
let attendee2 = Attendee::new("user_456".to_string()).status(AttendanceStatus::Tentative);
|
||||
let attendee3 = Attendee::new("user_789".to_string()); // Default NoResponse
|
||||
|
||||
// --- Create Events ---
|
||||
@ -45,7 +43,7 @@ fn main() {
|
||||
"event_gamma".to_string(),
|
||||
"Client Call",
|
||||
now + Duration::days(2),
|
||||
now + Duration::days(2) + Duration::seconds(3600)
|
||||
now + Duration::days(2) + Duration::seconds(3600),
|
||||
);
|
||||
|
||||
// --- Create Calendars ---
|
||||
@ -58,25 +56,43 @@ fn main() {
|
||||
.add_event(event2.clone());
|
||||
|
||||
// Create a calendar with auto-generated ID (explicit IDs are no longer supported)
|
||||
let calendar2 = Calendar::new(None, "Personal Calendar")
|
||||
.add_event(event3_for_calendar2.clone());
|
||||
|
||||
let calendar2 =
|
||||
Calendar::new(None, "Personal Calendar").add_event(event3_for_calendar2.clone());
|
||||
|
||||
// --- Store Calendars in DB ---
|
||||
let cal_collection = db.collection::<Calendar>().expect("can open calendar collection");
|
||||
let cal_collection = db
|
||||
.collection::<Calendar>()
|
||||
.expect("can open calendar collection");
|
||||
|
||||
let (_, calendar1) = cal_collection.set(&calendar1).expect("can set calendar1");
|
||||
let (_, calendar2) = cal_collection.set(&calendar2).expect("can set calendar2");
|
||||
|
||||
println!("Created calendar1 (ID: {}): Name - '{}'", calendar1.get_id(), calendar1.name);
|
||||
println!("Created calendar2 (ID: {}): Name - '{}'", calendar2.get_id(), calendar2.name);
|
||||
println!(
|
||||
"Created calendar1 (ID: {}): Name - '{}'",
|
||||
calendar1.get_id(),
|
||||
calendar1.name
|
||||
);
|
||||
println!(
|
||||
"Created calendar2 (ID: {}): Name - '{}'",
|
||||
calendar2.get_id(),
|
||||
calendar2.name
|
||||
);
|
||||
|
||||
// --- Retrieve a Calendar by ID ---
|
||||
let stored_calendar1_opt = cal_collection.get_by_id(calendar1.get_id()).expect("can try to load calendar1");
|
||||
assert!(stored_calendar1_opt.is_some(), "Calendar1 should be found in DB");
|
||||
let stored_calendar1_opt = cal_collection
|
||||
.get_by_id(calendar1.get_id())
|
||||
.expect("can try to load calendar1");
|
||||
assert!(
|
||||
stored_calendar1_opt.is_some(),
|
||||
"Calendar1 should be found in DB"
|
||||
);
|
||||
let mut stored_calendar1 = stored_calendar1_opt.unwrap();
|
||||
|
||||
println!("\nRetrieved calendar1 from DB: Name - '{}', Events count: {}", stored_calendar1.name, stored_calendar1.events.len());
|
||||
println!(
|
||||
"\nRetrieved calendar1 from DB: Name - '{}', Events count: {}",
|
||||
stored_calendar1.name,
|
||||
stored_calendar1.events.len()
|
||||
);
|
||||
assert_eq!(stored_calendar1.name, "Work Calendar");
|
||||
assert_eq!(stored_calendar1.events.len(), 2);
|
||||
assert_eq!(stored_calendar1.events[0].title, "Team Meeting");
|
||||
@ -91,42 +107,76 @@ fn main() {
|
||||
event_to_update.reschedule(new_start_time, new_end_time)
|
||||
});
|
||||
|
||||
let rescheduled_event = stored_calendar1.events.iter().find(|e| e.id == event_id_to_reschedule)
|
||||
let rescheduled_event = stored_calendar1
|
||||
.events
|
||||
.iter()
|
||||
.find(|e| e.id == event_id_to_reschedule)
|
||||
.expect("Rescheduled event should exist");
|
||||
assert_eq!(rescheduled_event.start_time, new_start_time);
|
||||
assert_eq!(rescheduled_event.end_time, new_end_time);
|
||||
println!("Event '{}' rescheduled in stored_calendar1.", rescheduled_event.title);
|
||||
println!(
|
||||
"Event '{}' rescheduled in stored_calendar1.",
|
||||
rescheduled_event.title
|
||||
);
|
||||
|
||||
// --- Store the modified calendar ---
|
||||
let (_, mut stored_calendar1) = cal_collection.set(&stored_calendar1).expect("can set modified calendar1");
|
||||
let re_retrieved_calendar1_opt = cal_collection.get_by_id(calendar1.get_id()).expect("can try to load modified calendar1");
|
||||
let (_, mut stored_calendar1) = cal_collection
|
||||
.set(&stored_calendar1)
|
||||
.expect("can set modified calendar1");
|
||||
let re_retrieved_calendar1_opt = cal_collection
|
||||
.get_by_id(calendar1.get_id())
|
||||
.expect("can try to load modified calendar1");
|
||||
let re_retrieved_calendar1 = re_retrieved_calendar1_opt.unwrap();
|
||||
let re_retrieved_event = re_retrieved_calendar1.events.iter().find(|e| e.id == event_id_to_reschedule)
|
||||
let re_retrieved_event = re_retrieved_calendar1
|
||||
.events
|
||||
.iter()
|
||||
.find(|e| e.id == event_id_to_reschedule)
|
||||
.expect("Rescheduled event should exist in re-retrieved calendar");
|
||||
assert_eq!(re_retrieved_event.start_time, new_start_time, "Reschedule not persisted correctly");
|
||||
assert_eq!(
|
||||
re_retrieved_event.start_time, new_start_time,
|
||||
"Reschedule not persisted correctly"
|
||||
);
|
||||
|
||||
println!("\nModified and re-saved calendar1. Rescheduled event start time: {}", re_retrieved_event.start_time);
|
||||
println!(
|
||||
"\nModified and re-saved calendar1. Rescheduled event start time: {}",
|
||||
re_retrieved_event.start_time
|
||||
);
|
||||
|
||||
// --- Add a new event to an existing calendar ---
|
||||
let event4_new = Event::new(
|
||||
"event_delta".to_string(),
|
||||
"1-on-1",
|
||||
now + Duration::days(3),
|
||||
now + Duration::days(3) + Duration::seconds(1800) // 30 minutes
|
||||
now + Duration::days(3) + Duration::seconds(1800), // 30 minutes
|
||||
);
|
||||
stored_calendar1 = stored_calendar1.add_event(event4_new);
|
||||
assert_eq!(stored_calendar1.events.len(), 3);
|
||||
let (_, stored_calendar1) = cal_collection.set(&stored_calendar1).expect("can set calendar1 after adding new event");
|
||||
println!("Added new event '1-on-1' to stored_calendar1. Total events: {}", stored_calendar1.events.len());
|
||||
let (_, stored_calendar1) = cal_collection
|
||||
.set(&stored_calendar1)
|
||||
.expect("can set calendar1 after adding new event");
|
||||
println!(
|
||||
"Added new event '1-on-1' to stored_calendar1. Total events: {}",
|
||||
stored_calendar1.events.len()
|
||||
);
|
||||
|
||||
// --- Delete a Calendar ---
|
||||
cal_collection.delete_by_id(calendar2.get_id()).expect("can delete calendar2");
|
||||
let deleted_calendar2_opt = cal_collection.get_by_id(calendar2.get_id()).expect("can try to load deleted calendar2");
|
||||
assert!(deleted_calendar2_opt.is_none(), "Calendar2 should be deleted from DB");
|
||||
cal_collection
|
||||
.delete_by_id(calendar2.get_id())
|
||||
.expect("can delete calendar2");
|
||||
let deleted_calendar2_opt = cal_collection
|
||||
.get_by_id(calendar2.get_id())
|
||||
.expect("can try to load deleted calendar2");
|
||||
assert!(
|
||||
deleted_calendar2_opt.is_none(),
|
||||
"Calendar2 should be deleted from DB"
|
||||
);
|
||||
|
||||
println!("\nDeleted calendar2 (ID: {}) from DB.", calendar2.get_id());
|
||||
println!("Calendar model DB Prefix: {}", Calendar::db_prefix());
|
||||
|
||||
println!("\nExample finished. DB stored at {}", db_path);
|
||||
println!("To clean up, you can manually delete the directory: {}", db_path);
|
||||
println!(
|
||||
"To clean up, you can manually delete the directory: {}",
|
||||
db_path
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::calendar::{Attendee, AttendanceStatus, Calendar, Event};
|
||||
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};
|
||||
use rhai_wrapper::wrap_vec_return;
|
||||
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize Rhai engine
|
||||
@ -29,9 +28,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
});
|
||||
|
||||
// Register setter methods for Calendar properties
|
||||
engine.register_fn("set_description", |calendar: &mut Calendar, desc: String| {
|
||||
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 {
|
||||
@ -49,10 +51,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Calendar saved: {}", _calendar.name);
|
||||
});
|
||||
|
||||
engine.register_fn("get_calendar_by_id", |_db: Arc<OurDB>, id: i64| -> Calendar {
|
||||
engine.register_fn(
|
||||
"get_calendar_by_id",
|
||||
|_db: Arc<OurDB>, id: i64| -> Calendar {
|
||||
// In a real implementation, this would retrieve the calendar from the database
|
||||
Calendar::new(Some(id as u32), "Retrieved Calendar")
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Register a function to check if a calendar exists
|
||||
engine.register_fn("calendar_exists", |_db: Arc<OurDB>, id: i64| -> bool {
|
||||
@ -63,11 +68,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Define the function separately to use with the wrap_vec_return macro
|
||||
fn get_all_calendars(_db: Arc<OurDB>) -> Vec<Calendar> {
|
||||
// In a real implementation, this would retrieve all calendars from the database
|
||||
vec![Calendar::new(Some(1), "Calendar 1"), Calendar::new(Some(2), "Calendar 2")]
|
||||
vec![
|
||||
Calendar::new(Some(1), "Calendar 1"),
|
||||
Calendar::new(Some(2), "Calendar 2"),
|
||||
]
|
||||
}
|
||||
|
||||
// Register the function with the wrap_vec_return macro
|
||||
engine.register_fn("get_all_calendars", wrap_vec_return!(get_all_calendars, Arc<OurDB> => Calendar));
|
||||
engine.register_fn(
|
||||
"get_all_calendars",
|
||||
wrap_vec_return!(get_all_calendars, Arc<OurDB> => Calendar),
|
||||
);
|
||||
|
||||
engine.register_fn("delete_calendar_by_id", |_db: Arc<OurDB>, _id: i64| {
|
||||
// In a real implementation, this would delete the calendar from the database
|
||||
|
@ -41,11 +41,16 @@ fn main() {
|
||||
println!("Before saving - CustomUser DB Keys: {:?}", user.db_keys());
|
||||
|
||||
// Save the model to the database
|
||||
let collection = db.collection::<CustomUser>().expect("can open user collection");
|
||||
let collection = db
|
||||
.collection::<CustomUser>()
|
||||
.expect("can open user collection");
|
||||
let (user_id, saved_user) = collection.set(&user).expect("can save user");
|
||||
|
||||
println!("\nAfter saving - CustomUser ID: {}", saved_user.get_id());
|
||||
println!("After saving - CustomUser DB Keys: {:?}", saved_user.db_keys());
|
||||
println!(
|
||||
"After saving - CustomUser DB Keys: {:?}",
|
||||
saved_user.db_keys()
|
||||
);
|
||||
println!("Returned ID: {}", user_id);
|
||||
|
||||
// Verify that the ID was auto-generated
|
||||
@ -53,5 +58,8 @@ fn main() {
|
||||
assert_ne!(saved_user.get_id(), 0);
|
||||
|
||||
println!("\nExample finished. DB stored at {}", db_path);
|
||||
println!("To clean up, you can manually delete the directory: {}", db_path);
|
||||
println!(
|
||||
"To clean up, you can manually delete the directory: {}",
|
||||
db_path
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
// heromodels/examples/finance_example/main.rs
|
||||
|
||||
use chrono::{Utc, Duration};
|
||||
use chrono::{Duration, Utc};
|
||||
use heromodels::models::finance::marketplace::{
|
||||
Bid, BidStatus, Listing, ListingStatus, ListingType,
|
||||
};
|
||||
use heromodels::models::finance::{Account, Asset, AssetType};
|
||||
use heromodels::models::finance::marketplace::{Listing, ListingType, ListingStatus, Bid, BidStatus};
|
||||
|
||||
fn main() {
|
||||
println!("Finance Models Example\n");
|
||||
@ -18,10 +20,13 @@ fn main() {
|
||||
"My primary Ethereum wallet", // description
|
||||
"ethereum", // ledger
|
||||
"0x1234567890abcdef1234567890abcdef12345678", // address
|
||||
"0xpubkey123456789" // pubkey
|
||||
"0xpubkey123456789", // pubkey
|
||||
);
|
||||
|
||||
println!("Created Account: '{}' (ID: {})", account.name, account.base_data.id);
|
||||
println!(
|
||||
"Created Account: '{}' (ID: {})",
|
||||
account.name, account.base_data.id
|
||||
);
|
||||
println!("Owner: User {}", account.user_id);
|
||||
println!("Blockchain: {}", account.ledger);
|
||||
println!("Address: {}", account.address);
|
||||
@ -67,7 +72,12 @@ fn main() {
|
||||
|
||||
println!("Added Assets to Account:");
|
||||
for asset in &account.assets {
|
||||
println!("- {} ({:?}): {} units", asset.name, asset.asset_type, asset.formatted_amount());
|
||||
println!(
|
||||
"- {} ({:?}): {} units",
|
||||
asset.name,
|
||||
asset.asset_type,
|
||||
asset.formatted_amount()
|
||||
);
|
||||
}
|
||||
|
||||
println!("\nTotal Account Value (raw sum): {}", account.total_value());
|
||||
@ -113,9 +123,18 @@ fn main() {
|
||||
Some("https://example.com/usdc.png"), // image_url
|
||||
);
|
||||
|
||||
println!("Created Fixed Price Listing: '{}' (ID: {})", fixed_price_listing.title, fixed_price_listing.base_data.id);
|
||||
println!("Price: {} {}", fixed_price_listing.price, fixed_price_listing.currency);
|
||||
println!("Type: {:?}, Status: {:?}", fixed_price_listing.listing_type, fixed_price_listing.status);
|
||||
println!(
|
||||
"Created Fixed Price Listing: '{}' (ID: {})",
|
||||
fixed_price_listing.title, fixed_price_listing.base_data.id
|
||||
);
|
||||
println!(
|
||||
"Price: {} {}",
|
||||
fixed_price_listing.price, fixed_price_listing.currency
|
||||
);
|
||||
println!(
|
||||
"Type: {:?}, Status: {:?}",
|
||||
fixed_price_listing.listing_type, fixed_price_listing.status
|
||||
);
|
||||
println!("Expires: {}", fixed_price_listing.expires_at.unwrap());
|
||||
println!("");
|
||||
|
||||
@ -126,10 +145,14 @@ fn main() {
|
||||
println!("Fixed Price Sale Completed:");
|
||||
println!("Status: {:?}", fixed_price_listing.status);
|
||||
println!("Buyer: {}", fixed_price_listing.buyer_id.unwrap());
|
||||
println!("Sale Price: {} {}", fixed_price_listing.sale_price.unwrap(), fixed_price_listing.currency);
|
||||
println!(
|
||||
"Sale Price: {} {}",
|
||||
fixed_price_listing.sale_price.unwrap(),
|
||||
fixed_price_listing.currency
|
||||
);
|
||||
println!("Sold At: {}", fixed_price_listing.sold_at.unwrap());
|
||||
println!("");
|
||||
},
|
||||
}
|
||||
Err(e) => println!("Error completing sale: {}", e),
|
||||
}
|
||||
|
||||
@ -145,13 +168,26 @@ fn main() {
|
||||
"ETH", // currency
|
||||
ListingType::Auction, // listing_type
|
||||
Some(Utc::now() + Duration::days(3)), // expires_at (3 days from now)
|
||||
vec!["nft".to_string(), "collectible".to_string(), "cryptopunk".to_string()], // tags
|
||||
vec![
|
||||
"nft".to_string(),
|
||||
"collectible".to_string(),
|
||||
"cryptopunk".to_string(),
|
||||
], // tags
|
||||
Some("https://example.com/cryptopunk1234.png"), // image_url
|
||||
);
|
||||
|
||||
println!("Created Auction Listing: '{}' (ID: {})", auction_listing.title, auction_listing.base_data.id);
|
||||
println!("Starting Price: {} {}", auction_listing.price, auction_listing.currency);
|
||||
println!("Type: {:?}, Status: {:?}", auction_listing.listing_type, auction_listing.status);
|
||||
println!(
|
||||
"Created Auction Listing: '{}' (ID: {})",
|
||||
auction_listing.title, auction_listing.base_data.id
|
||||
);
|
||||
println!(
|
||||
"Starting Price: {} {}",
|
||||
auction_listing.price, auction_listing.currency
|
||||
);
|
||||
println!(
|
||||
"Type: {:?}, Status: {:?}",
|
||||
auction_listing.listing_type, auction_listing.status
|
||||
);
|
||||
println!("");
|
||||
|
||||
// Create some bids
|
||||
@ -184,7 +220,7 @@ fn main() {
|
||||
Ok(updated_listing) => {
|
||||
auction_listing = updated_listing;
|
||||
println!("- Bid added: 11.0 ETH from User 2001");
|
||||
},
|
||||
}
|
||||
Err(e) => println!("Error adding bid: {}", e),
|
||||
}
|
||||
|
||||
@ -192,7 +228,7 @@ fn main() {
|
||||
Ok(updated_listing) => {
|
||||
auction_listing = updated_listing;
|
||||
println!("- Bid added: 12.5 ETH from User 2002");
|
||||
},
|
||||
}
|
||||
Err(e) => println!("Error adding bid: {}", e),
|
||||
}
|
||||
|
||||
@ -200,18 +236,21 @@ fn main() {
|
||||
Ok(updated_listing) => {
|
||||
auction_listing = updated_listing;
|
||||
println!("- Bid added: 15.0 ETH from User 2003");
|
||||
},
|
||||
}
|
||||
Err(e) => println!("Error adding bid: {}", e),
|
||||
}
|
||||
|
||||
println!("\nCurrent Auction Status:");
|
||||
println!("Current Price: {} {}", auction_listing.price, auction_listing.currency);
|
||||
println!(
|
||||
"Current Price: {} {}",
|
||||
auction_listing.price, auction_listing.currency
|
||||
);
|
||||
|
||||
if let Some(highest_bid) = auction_listing.highest_bid() {
|
||||
println!("Highest Bid: {} {} from User {}",
|
||||
highest_bid.amount,
|
||||
highest_bid.currency,
|
||||
highest_bid.bidder_id);
|
||||
println!(
|
||||
"Highest Bid: {} {} from User {}",
|
||||
highest_bid.amount, highest_bid.currency, highest_bid.bidder_id
|
||||
);
|
||||
}
|
||||
|
||||
println!("Total Bids: {}", auction_listing.bids.len());
|
||||
@ -223,20 +262,26 @@ fn main() {
|
||||
auction_listing = updated_listing;
|
||||
println!("Auction Completed:");
|
||||
println!("Status: {:?}", auction_listing.status);
|
||||
println!("Winner: User {}", auction_listing.buyer_id.as_ref().unwrap());
|
||||
println!("Winning Bid: {} {}", auction_listing.sale_price.as_ref().unwrap(), auction_listing.currency);
|
||||
println!(
|
||||
"Winner: User {}",
|
||||
auction_listing.buyer_id.as_ref().unwrap()
|
||||
);
|
||||
println!(
|
||||
"Winning Bid: {} {}",
|
||||
auction_listing.sale_price.as_ref().unwrap(),
|
||||
auction_listing.currency
|
||||
);
|
||||
println!("");
|
||||
|
||||
println!("Final Bid Statuses:");
|
||||
for bid in &auction_listing.bids {
|
||||
println!("- User {}: {} {} (Status: {:?})",
|
||||
bid.bidder_id,
|
||||
bid.amount,
|
||||
bid.currency,
|
||||
bid.status);
|
||||
println!(
|
||||
"- User {}: {} {} (Status: {:?})",
|
||||
bid.bidder_id, bid.amount, bid.currency, bid.status
|
||||
);
|
||||
}
|
||||
println!("");
|
||||
},
|
||||
}
|
||||
Err(e) => println!("Error completing auction: {}", e),
|
||||
}
|
||||
|
||||
@ -256,9 +301,18 @@ fn main() {
|
||||
None::<String>, // image_url
|
||||
);
|
||||
|
||||
println!("Created Exchange Listing: '{}' (ID: {})", exchange_listing.title, exchange_listing.base_data.id);
|
||||
println!("Offering: Asset {} ({:?})", exchange_listing.asset_id, exchange_listing.asset_type);
|
||||
println!("Wanted: {} {}", exchange_listing.price, exchange_listing.currency);
|
||||
println!(
|
||||
"Created Exchange Listing: '{}' (ID: {})",
|
||||
exchange_listing.title, exchange_listing.base_data.id
|
||||
);
|
||||
println!(
|
||||
"Offering: Asset {} ({:?})",
|
||||
exchange_listing.asset_id, exchange_listing.asset_type
|
||||
);
|
||||
println!(
|
||||
"Wanted: {} {}",
|
||||
exchange_listing.price, exchange_listing.currency
|
||||
);
|
||||
println!("");
|
||||
|
||||
// --- PART 3: DEMONSTRATING EDGE CASES ---
|
||||
@ -319,7 +373,10 @@ fn main() {
|
||||
None::<String>, // image_url
|
||||
);
|
||||
|
||||
println!("Created Expiring Listing: '{}' (ID: {})", expiring_listing.title, expiring_listing.base_data.id);
|
||||
println!(
|
||||
"Created Expiring Listing: '{}' (ID: {})",
|
||||
expiring_listing.title, expiring_listing.base_data.id
|
||||
);
|
||||
println!("Initial Status: {:?}", expiring_listing.status);
|
||||
|
||||
// Check expiration
|
||||
|
@ -1,12 +1,12 @@
|
||||
use rhai::{Engine, Scope, EvalAltResult};
|
||||
use std::sync::{Arc, Mutex};
|
||||
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::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
|
||||
@ -42,7 +42,7 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
&mut engine,
|
||||
Arc::clone(&mock_db.accounts),
|
||||
Arc::clone(&mock_db.assets),
|
||||
Arc::clone(&mock_db.listings)
|
||||
Arc::clone(&mock_db.listings),
|
||||
);
|
||||
println!("Rhai functions registered.");
|
||||
|
||||
@ -77,8 +77,13 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
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());
|
||||
println!(
|
||||
"Account ID: {}, Name: '{}', User ID: {}, Assets: {}",
|
||||
id,
|
||||
account.name,
|
||||
account.user_id,
|
||||
account.assets.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Print final state of Assets
|
||||
@ -88,8 +93,10 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
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);
|
||||
println!(
|
||||
"Asset ID: {}, Name: '{}', Amount: {}, Type: {:?}",
|
||||
id, asset.name, asset.amount, asset.asset_type
|
||||
);
|
||||
}
|
||||
|
||||
// Print final state of Listings
|
||||
@ -101,7 +108,12 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
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()
|
||||
id,
|
||||
listing.title,
|
||||
listing.listing_type,
|
||||
listing.status,
|
||||
listing.price,
|
||||
listing.bids.len()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,8 @@ use heromodels_core::Model;
|
||||
|
||||
fn main() {
|
||||
// Create a new DB instance in /tmp/ourdb_flowbroker, and reset before every run
|
||||
let db = heromodels::db::hero::OurDB::new("/tmp/ourdb_flowbroker", true)
|
||||
.expect("Can create DB");
|
||||
let db =
|
||||
heromodels::db::hero::OurDB::new("/tmp/ourdb_flowbroker", true).expect("Can create DB");
|
||||
|
||||
println!("Hero Models - Flow Example");
|
||||
println!("===========================");
|
||||
@ -25,7 +25,10 @@ fn main() {
|
||||
"Document Approval Flow", // name
|
||||
"Pending", // status
|
||||
);
|
||||
db.collection().expect("can open flow collection").set(&flow1).expect("can set flow1");
|
||||
db.collection()
|
||||
.expect("can open flow collection")
|
||||
.set(&flow1)
|
||||
.expect("can set flow1");
|
||||
println!("Created Flow: {:?}", flow1);
|
||||
println!("Flow ID: {}", flow1.get_id());
|
||||
println!("Flow DB Prefix: {}", Flow::db_prefix());
|
||||
@ -38,7 +41,10 @@ fn main() {
|
||||
"Pending", // status
|
||||
)
|
||||
.description("Initial review by manager");
|
||||
db.collection().expect("can open flow_step collection").set(&step1_flow1).expect("can set step1_flow1");
|
||||
db.collection()
|
||||
.expect("can open flow_step collection")
|
||||
.set(&step1_flow1)
|
||||
.expect("can set step1_flow1");
|
||||
println!("Created FlowStep: {:?}", step1_flow1);
|
||||
|
||||
let step2_flow1 = FlowStep::new(
|
||||
@ -48,7 +54,10 @@ fn main() {
|
||||
"Pending", // status
|
||||
)
|
||||
.description("Legal team sign-off");
|
||||
db.collection().expect("can open flow_step collection").set(&step2_flow1).expect("can set step2_flow1");
|
||||
db.collection()
|
||||
.expect("can open flow_step collection")
|
||||
.set(&step2_flow1)
|
||||
.expect("can set step2_flow1");
|
||||
println!("Created FlowStep: {:?}", step2_flow1);
|
||||
|
||||
// --- Create SignatureRequirements for step2_flow1 ---
|
||||
@ -59,7 +68,10 @@ fn main() {
|
||||
"I approve this document for legal compliance.", // message
|
||||
"Pending", // status
|
||||
);
|
||||
db.collection().expect("can open sig_req collection").set(&sig_req1_step2).expect("can set sig_req1_step2");
|
||||
db.collection()
|
||||
.expect("can open sig_req collection")
|
||||
.set(&sig_req1_step2)
|
||||
.expect("can set sig_req1_step2");
|
||||
println!("Created SignatureRequirement: {:?}", sig_req1_step2);
|
||||
|
||||
let sig_req2_step2 = SignatureRequirement::new(
|
||||
@ -69,7 +81,10 @@ fn main() {
|
||||
"I, as General Counsel, approve this document.", // message
|
||||
"Pending", // status
|
||||
);
|
||||
db.collection().expect("can open sig_req collection").set(&sig_req2_step2).expect("can set sig_req2_step2");
|
||||
db.collection()
|
||||
.expect("can open sig_req collection")
|
||||
.set(&sig_req2_step2)
|
||||
.expect("can set sig_req2_step2");
|
||||
println!("Created SignatureRequirement: {:?}", sig_req2_step2);
|
||||
|
||||
// --- Retrieve and Verify ---
|
||||
@ -101,9 +116,18 @@ fn main() {
|
||||
.get::<flow_step_flow_id_idx, _>(&retrieved_flow.get_id())
|
||||
.expect("can load steps for flow1");
|
||||
assert_eq!(steps_for_flow1.len(), 2);
|
||||
println!("Retrieved {} FlowSteps for Flow ID {}:", steps_for_flow1.len(), retrieved_flow.get_id());
|
||||
println!(
|
||||
"Retrieved {} FlowSteps for Flow ID {}:",
|
||||
steps_for_flow1.len(),
|
||||
retrieved_flow.get_id()
|
||||
);
|
||||
for step in &steps_for_flow1 {
|
||||
println!(" - Step ID: {}, Order: {}, Desc: {:?}", step.get_id(), step.step_order, step.description);
|
||||
println!(
|
||||
" - Step ID: {}, Order: {}, Desc: {:?}",
|
||||
step.get_id(),
|
||||
step.step_order,
|
||||
step.description
|
||||
);
|
||||
}
|
||||
|
||||
// --- Update a SignatureRequirement (simulate signing) ---
|
||||
@ -114,12 +138,18 @@ fn main() {
|
||||
.expect("can load sig_req1")
|
||||
.unwrap();
|
||||
|
||||
println!("\nUpdating SignatureRequirement ID: {}", retrieved_sig_req1.get_id());
|
||||
println!(
|
||||
"\nUpdating SignatureRequirement ID: {}",
|
||||
retrieved_sig_req1.get_id()
|
||||
);
|
||||
retrieved_sig_req1.status = "Signed".to_string();
|
||||
retrieved_sig_req1.signed_by = Some("pubkey_legal_team_lead_hex_actual_signer".to_string());
|
||||
retrieved_sig_req1.signature = Some("mock_signature_base64_encoded".to_string());
|
||||
|
||||
db.collection().expect("can open sig_req collection").set(&retrieved_sig_req1).expect("can update sig_req1");
|
||||
db.collection()
|
||||
.expect("can open sig_req collection")
|
||||
.set(&retrieved_sig_req1)
|
||||
.expect("can update sig_req1");
|
||||
|
||||
let updated_sig_req1 = db
|
||||
.collection::<SignatureRequirement>()
|
||||
@ -129,7 +159,10 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(updated_sig_req1.status, "Signed");
|
||||
assert_eq!(updated_sig_req1.signature.as_deref(), Some("mock_signature_base64_encoded"));
|
||||
assert_eq!(
|
||||
updated_sig_req1.signature.as_deref(),
|
||||
Some("mock_signature_base64_encoded")
|
||||
);
|
||||
println!("Updated SignatureRequirement: {:?}", updated_sig_req1);
|
||||
|
||||
// --- Delete a FlowStep ---
|
||||
@ -157,7 +190,11 @@ fn main() {
|
||||
.expect("can load remaining steps for flow1");
|
||||
assert_eq!(remaining_steps_for_flow1.len(), 1);
|
||||
assert_eq!(remaining_steps_for_flow1[0].get_id(), step2_flow1.get_id());
|
||||
println!("Remaining FlowSteps for Flow ID {}: count = {}", retrieved_flow.get_id(), remaining_steps_for_flow1.len());
|
||||
println!(
|
||||
"Remaining FlowSteps for Flow ID {}: count = {}",
|
||||
retrieved_flow.get_id(),
|
||||
remaining_steps_for_flow1.len()
|
||||
);
|
||||
|
||||
println!("\nFlow example finished successfully!");
|
||||
}
|
||||
|
@ -20,8 +20,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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))));
|
||||
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);
|
||||
|
@ -108,7 +108,7 @@ fn main() {
|
||||
7, // user_id
|
||||
1, // chosen_option_id (Approve Allocation)
|
||||
80, // shares
|
||||
"I strongly support this proposal because it aligns with our community values."
|
||||
"I strongly support this proposal because it aligns with our community values.",
|
||||
);
|
||||
|
||||
// User 8 votes for 'Reject Allocation' with a comment
|
||||
@ -117,7 +117,7 @@ fn main() {
|
||||
8, // user_id
|
||||
2, // chosen_option_id (Reject Allocation)
|
||||
60, // shares
|
||||
"I have concerns about the allocation priorities."
|
||||
"I have concerns about the allocation priorities.",
|
||||
);
|
||||
|
||||
println!("\nBallots with Comments:");
|
||||
@ -225,7 +225,7 @@ fn main() {
|
||||
20, // user_id (eligible)
|
||||
1, // chosen_option_id
|
||||
75, // shares
|
||||
"I support this restructuring plan with some reservations."
|
||||
"I support this restructuring plan with some reservations.",
|
||||
);
|
||||
|
||||
// User 30 (eligible) votes with a comment
|
||||
@ -234,7 +234,7 @@ fn main() {
|
||||
30, // user_id (eligible)
|
||||
2, // chosen_option_id
|
||||
90, // shares
|
||||
"I believe we should reconsider the timing of these changes."
|
||||
"I believe we should reconsider the timing of these changes.",
|
||||
);
|
||||
|
||||
// User 40 (ineligible) tries to vote with a comment
|
||||
@ -243,7 +243,7 @@ fn main() {
|
||||
40, // user_id (ineligible)
|
||||
1, // chosen_option_id
|
||||
50, // shares
|
||||
"This restructuring seems unnecessary."
|
||||
"This restructuring seems unnecessary.",
|
||||
);
|
||||
|
||||
println!("Eligible users 20 and 30 added votes with comments.");
|
||||
|
@ -1,10 +1,12 @@
|
||||
use chrono::{Duration, Utc};
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::governance::{Proposal, ProposalStatus, VoteEventStatus, VoteOption, Ballot};
|
||||
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;
|
||||
use chrono::{Utc, Duration};
|
||||
use rhai_client_macros::rhai;
|
||||
|
||||
// Define the functions we want to expose to Rhai
|
||||
// We'll only use the #[rhai] attribute on functions with simple types
|
||||
@ -13,7 +15,14 @@ use rhai_client_macros::rhai;
|
||||
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)
|
||||
Proposal::new(
|
||||
id as u32,
|
||||
creator_id,
|
||||
title,
|
||||
description,
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
}
|
||||
|
||||
// Getter functions for Proposal properties
|
||||
@ -46,7 +55,13 @@ fn add_option_to_proposal(proposal: Proposal, option_id: i64, option_text: Strin
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -119,14 +134,24 @@ fn get_ballot_shares(ballot: &Ballot) -> i64 {
|
||||
|
||||
// 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 {
|
||||
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 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)
|
||||
}
|
||||
@ -141,8 +166,22 @@ fn get_all_proposals(_db: Arc<OurDB>) -> Vec<Proposal> {
|
||||
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)
|
||||
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,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
@ -172,19 +211,37 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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| {
|
||||
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)
|
||||
});
|
||||
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)
|
||||
});
|
||||
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);
|
||||
@ -203,12 +260,22 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Register functions for database operations
|
||||
engine.register_fn("save_proposal", save_proposal);
|
||||
|
||||
engine.register_fn("get_proposal_by_id", |_db: Arc<OurDB>, id: i64| -> Proposal {
|
||||
engine.register_fn(
|
||||
"get_proposal_by_id",
|
||||
|_db: Arc<OurDB>, id: i64| -> Proposal {
|
||||
// In a real implementation, this would retrieve the proposal from the database
|
||||
let start_date = Utc::now();
|
||||
let end_date = start_date + Duration::days(14);
|
||||
Proposal::new(id as u32, "Retrieved Creator", "Retrieved Proposal", "Retrieved Description", start_date, end_date)
|
||||
});
|
||||
Proposal::new(
|
||||
id as u32,
|
||||
"Retrieved Creator",
|
||||
"Retrieved Proposal",
|
||||
"Retrieved Description",
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
// Register a function to check if a proposal exists
|
||||
engine.register_fn("proposal_exists", |_db: Arc<OurDB>, id: i64| -> bool {
|
||||
@ -217,7 +284,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
});
|
||||
|
||||
// Register the function with the wrap_vec_return macro
|
||||
engine.register_fn("get_all_proposals", wrap_vec_return!(get_all_proposals, Arc<OurDB> => Proposal));
|
||||
engine.register_fn(
|
||||
"get_all_proposals",
|
||||
wrap_vec_return!(get_all_proposals, Arc<OurDB> => Proposal),
|
||||
);
|
||||
|
||||
engine.register_fn("delete_proposal_by_id", delete_proposal_by_id);
|
||||
|
||||
@ -242,33 +312,41 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Use the database instance
|
||||
|
||||
// Create a new proposal
|
||||
let proposal = create_proposal(1,
|
||||
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());
|
||||
"Proposal to allocate funds for community projects in the third quarter.".to_string(),
|
||||
);
|
||||
|
||||
println!("Created Proposal: '{}' (ID: {})",
|
||||
println!(
|
||||
"Created Proposal: '{}' (ID: {})",
|
||||
get_title(&proposal),
|
||||
get_id(&proposal));
|
||||
println!("Status: {}, Vote Status: {}",
|
||||
get_id(&proposal)
|
||||
);
|
||||
println!(
|
||||
"Status: {}, Vote Status: {}",
|
||||
get_status(&proposal),
|
||||
get_vote_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());
|
||||
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));
|
||||
println!(
|
||||
"- Option ID: {}, Text: '{}', Votes: {}",
|
||||
i,
|
||||
get_option_text(&option),
|
||||
get_option_votes(&option)
|
||||
);
|
||||
}
|
||||
|
||||
// Save the proposal to the database
|
||||
@ -278,48 +356,52 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// 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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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!(
|
||||
"- 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),
|
||||
println!(
|
||||
"- Ballot ID: {}, User ID: {}, Option ID: {}, Shares: {}",
|
||||
i,
|
||||
get_ballot_user_id(&ballot),
|
||||
get_ballot_option_id(&ballot),
|
||||
get_ballot_shares(&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));
|
||||
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));
|
||||
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:");
|
||||
@ -330,9 +412,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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!(
|
||||
" - {}: {} (Votes: {})",
|
||||
i,
|
||||
get_option_text(&option),
|
||||
get_option_votes(&option)
|
||||
);
|
||||
}
|
||||
println!("Total Ballots: {}", get_ballot_count(&closed_proposal));
|
||||
|
||||
@ -340,9 +425,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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: '{}'",
|
||||
println!(
|
||||
"Proposal ID: {}, Title: '{}'",
|
||||
get_id(&proposal),
|
||||
get_title(&proposal));
|
||||
get_title(&proposal)
|
||||
);
|
||||
}
|
||||
|
||||
// Delete a proposal
|
||||
@ -351,13 +438,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// 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,
|
||||
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());
|
||||
"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());
|
||||
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.");
|
||||
|
@ -97,7 +97,10 @@ fn main() {
|
||||
println!("Updated At (timestamp): {}", contract.base_data.modified_at); // From BaseModelData
|
||||
|
||||
if let Some(first_signer_details) = contract.signers.first() {
|
||||
println!("\nFirst Signer: {} ({})", first_signer_details.name, first_signer_details.email);
|
||||
println!(
|
||||
"\nFirst Signer: {} ({})",
|
||||
first_signer_details.name, first_signer_details.email
|
||||
);
|
||||
println!(" Status: {:?}", first_signer_details.status);
|
||||
if let Some(signed_time) = first_signer_details.signed_at {
|
||||
println!(" Signed At: {}", signed_time);
|
||||
|
@ -22,8 +22,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
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))));
|
||||
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);
|
||||
|
@ -33,6 +33,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fs::remove_dir_all(db_path)?;
|
||||
println!("--- Cleaned up temporary database. ---");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -59,21 +59,39 @@ fn main() {
|
||||
println!("Before saving - SimpleUser DB Keys: {:?}", user.db_keys());
|
||||
|
||||
println!("\nBefore saving - CustomUser ID: {}", custom_user.get_id());
|
||||
println!("Before saving - CustomUser DB Keys: {:?}", custom_user.db_keys());
|
||||
println!(
|
||||
"Before saving - CustomUser DB Keys: {:?}",
|
||||
custom_user.db_keys()
|
||||
);
|
||||
|
||||
// Save the models to the database
|
||||
let simple_collection = db.collection::<SimpleUser>().expect("can open simple user collection");
|
||||
let custom_collection = db.collection::<CustomUser>().expect("can open custom user collection");
|
||||
let simple_collection = db
|
||||
.collection::<SimpleUser>()
|
||||
.expect("can open simple user collection");
|
||||
let custom_collection = db
|
||||
.collection::<CustomUser>()
|
||||
.expect("can open custom user collection");
|
||||
|
||||
let (user_id, saved_user) = simple_collection.set(&user).expect("can save simple user");
|
||||
let (custom_user_id, saved_custom_user) = custom_collection.set(&custom_user).expect("can save custom user");
|
||||
let (custom_user_id, saved_custom_user) = custom_collection
|
||||
.set(&custom_user)
|
||||
.expect("can save custom user");
|
||||
|
||||
println!("\nAfter saving - SimpleUser ID: {}", saved_user.get_id());
|
||||
println!("After saving - SimpleUser DB Keys: {:?}", saved_user.db_keys());
|
||||
println!(
|
||||
"After saving - SimpleUser DB Keys: {:?}",
|
||||
saved_user.db_keys()
|
||||
);
|
||||
println!("Returned SimpleUser ID: {}", user_id);
|
||||
|
||||
println!("\nAfter saving - CustomUser ID: {}", saved_custom_user.get_id());
|
||||
println!("After saving - CustomUser DB Keys: {:?}", saved_custom_user.db_keys());
|
||||
println!(
|
||||
"\nAfter saving - CustomUser ID: {}",
|
||||
saved_custom_user.get_id()
|
||||
);
|
||||
println!(
|
||||
"After saving - CustomUser DB Keys: {:?}",
|
||||
saved_custom_user.db_keys()
|
||||
);
|
||||
println!("Returned CustomUser ID: {}", custom_user_id);
|
||||
|
||||
// Verify that the IDs were auto-generated
|
||||
@ -83,5 +101,8 @@ fn main() {
|
||||
assert_ne!(saved_custom_user.get_id(), 0);
|
||||
|
||||
println!("\nExample finished. DB stored at {}", db_path);
|
||||
println!("To clean up, you can manually delete the directory: {}", db_path);
|
||||
println!(
|
||||
"To clean up, you can manually delete the directory: {}",
|
||||
db_path
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use rhai::{Engine, EvalAltResult, Scope};
|
||||
use std::sync::Arc;
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::projects::register_projects_rhai_module;
|
||||
use rhai::{Engine, EvalAltResult, Scope};
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
println!("Executing Rhai script: examples/project_rhai/project_test.rhai");
|
||||
@ -18,8 +18,12 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
// 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())))?;
|
||||
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();
|
||||
|
@ -34,11 +34,16 @@ fn main() {
|
||||
println!("Before saving - SimpleUser DB Keys: {:?}", user.db_keys());
|
||||
|
||||
// Save the user to the database
|
||||
let collection = db.collection::<SimpleUser>().expect("can open user collection");
|
||||
let collection = db
|
||||
.collection::<SimpleUser>()
|
||||
.expect("can open user collection");
|
||||
let (user_id, saved_user) = collection.set(&user).expect("can save user");
|
||||
|
||||
println!("\nAfter saving - SimpleUser ID: {}", saved_user.get_id());
|
||||
println!("After saving - SimpleUser DB Keys: {:?}", saved_user.db_keys());
|
||||
println!(
|
||||
"After saving - SimpleUser DB Keys: {:?}",
|
||||
saved_user.db_keys()
|
||||
);
|
||||
println!("Returned ID: {}", user_id);
|
||||
|
||||
// Verify that the ID was auto-generated
|
||||
@ -46,6 +51,8 @@ fn main() {
|
||||
assert_ne!(saved_user.get_id(), 0);
|
||||
|
||||
println!("\nExample finished. DB stored at {}", db_path);
|
||||
println!("To clean up, you can manually delete the directory: {}", db_path);
|
||||
println!(
|
||||
"To clean up, you can manually delete the directory: {}",
|
||||
db_path
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,9 @@ where
|
||||
fn get_all(&self) -> Result<Vec<V>, Error<Self::Error>>;
|
||||
|
||||
/// Begin a transaction for this collection
|
||||
fn begin_transaction(&self) -> Result<Box<dyn Transaction<Error = Self::Error>>, Error<Self::Error>>;
|
||||
fn begin_transaction(
|
||||
&self,
|
||||
) -> Result<Box<dyn Transaction<Error = Self::Error>>, Error<Self::Error>>;
|
||||
}
|
||||
|
||||
/// Errors returned by the DB implementation
|
||||
|
@ -436,8 +436,12 @@ where
|
||||
Ok(list_of_raw_ids_set_bytes) => {
|
||||
for raw_ids_set_bytes in list_of_raw_ids_set_bytes {
|
||||
// Each item in the list is a bincode-serialized HashSet<u32> of object IDs.
|
||||
match bincode::serde::decode_from_slice::<HashSet<u32>, _>(&raw_ids_set_bytes, BINCODE_CONFIG) {
|
||||
Ok((ids_set, _)) => { // Destructure the tuple (HashSet<u32>, usize)
|
||||
match bincode::serde::decode_from_slice::<HashSet<u32>, _>(
|
||||
&raw_ids_set_bytes,
|
||||
BINCODE_CONFIG,
|
||||
) {
|
||||
Ok((ids_set, _)) => {
|
||||
// Destructure the tuple (HashSet<u32>, usize)
|
||||
all_object_ids.extend(ids_set);
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -3,5 +3,5 @@ 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 self::access::Access;
|
||||
pub use rhai::register_access_rhai_module;
|
||||
|
@ -1,22 +1,22 @@
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
|
||||
use std::sync::Arc;
|
||||
use std::mem;
|
||||
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};
|
||||
use super::access::Access;
|
||||
type RhaiAccess = Access;
|
||||
use crate::db::hero::OurDB;
|
||||
use crate::db::Collection;
|
||||
use crate::db::hero::OurDB;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_|
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
@ -30,7 +30,10 @@ mod rhai_access_module {
|
||||
|
||||
/// Sets the access name
|
||||
#[rhai_fn(name = "object_id", return_raw, global, pure)]
|
||||
pub fn access_object_id(access: &mut RhaiAccess, object_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
pub fn access_object_id(
|
||||
access: &mut RhaiAccess,
|
||||
object_id: u32,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
@ -41,7 +44,10 @@ mod rhai_access_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "circle_id", return_raw, global, pure)]
|
||||
pub fn access_circle_id(access: &mut RhaiAccess, circle_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
pub fn access_circle_id(
|
||||
access: &mut RhaiAccess,
|
||||
circle_id: u32,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
@ -52,7 +58,10 @@ mod rhai_access_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "group_id", return_raw, global, pure)]
|
||||
pub fn access_group_id(access: &mut RhaiAccess, group_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
pub fn access_group_id(
|
||||
access: &mut RhaiAccess,
|
||||
group_id: u32,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
@ -63,7 +72,10 @@ mod rhai_access_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "contact_id", return_raw, global, pure)]
|
||||
pub fn access_contact_id(access: &mut RhaiAccess, contact_id: u32) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
pub fn access_contact_id(
|
||||
access: &mut RhaiAccess,
|
||||
contact_id: u32,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
@ -74,7 +86,10 @@ mod rhai_access_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "expires_at", return_raw, global, pure)]
|
||||
pub fn access_expires_at(access: &mut RhaiAccess, expires_at: Option<u64>) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
pub fn access_expires_at(
|
||||
access: &mut RhaiAccess,
|
||||
expires_at: Option<u64>,
|
||||
) -> Result<RhaiAccess, Box<EvalAltResult>> {
|
||||
// Create a default Access to replace the taken one
|
||||
let default_access = Access::new();
|
||||
|
||||
@ -86,28 +101,44 @@ mod rhai_access_module {
|
||||
|
||||
// Access Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_access_id(access: &mut RhaiAccess) -> i64 { access.base_data.id as i64 }
|
||||
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 }
|
||||
pub fn get_access_object_id(access: &mut RhaiAccess) -> i64 {
|
||||
access.object_id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "circle_id", pure)]
|
||||
pub fn get_access_circle_id(access: &mut RhaiAccess) -> i64 { access.circle_id as i64 }
|
||||
pub fn get_access_circle_id(access: &mut RhaiAccess) -> i64 {
|
||||
access.circle_id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "group_id", pure)]
|
||||
pub fn get_access_group_id(access: &mut RhaiAccess) -> i64 { access.group_id as i64 }
|
||||
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 }
|
||||
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 }
|
||||
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 }
|
||||
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 get_access_modified_at(access: &mut RhaiAccess) -> i64 {
|
||||
access.base_data.modified_at
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_access_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
@ -119,56 +150,86 @@ pub fn register_access_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
let mut db_module = Module::new();
|
||||
|
||||
let db_clone_set_access = db.clone();
|
||||
db_module.set_native_fn("save_access", move |access: Access| -> Result<Access, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_access",
|
||||
move |access: Access| -> Result<Access, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_access.set(&access)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_access: {}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone_set_access.set(&access).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_access: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated access with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_access = db.clone();
|
||||
db_module.set_native_fn("delete_access", move |access: Access| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_access",
|
||||
move |access: Access| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_access.collection::<Access>()
|
||||
let result = db_clone_delete_access
|
||||
.collection::<Access>()
|
||||
.expect("can open access collection")
|
||||
.delete_by_id(access.base_data.id)
|
||||
.expect("can delete event");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_access = db.clone();
|
||||
db_module.set_native_fn("get_access_by_id", move |id_i64: INT| -> Result<Access, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_access_by_id",
|
||||
move |id_i64: INT| -> Result<Access, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_access.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_access_by_id: {}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Access with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone_get_access
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_access_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Access with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_accesss function to get all accesss
|
||||
let db_clone_list_accesss = db.clone();
|
||||
db_module.set_native_fn("list_accesss", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_accesss.collection::<Access>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_accesss",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_accesss.collection::<Access>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get access collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let accesss = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let accesss = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all accesss: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
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());
|
||||
|
@ -1,8 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_core::{BaseModelData, Index};
|
||||
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
|
||||
use heromodels_core::BaseModelDataOps;
|
||||
use heromodels_core::{BaseModelData, Index};
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// --- Enums ---
|
||||
|
||||
|
@ -8,17 +8,16 @@ pub mod product;
|
||||
// pub mod user;
|
||||
|
||||
// Re-export main types from sub-modules
|
||||
pub use company::{Company, CompanyStatus, BusinessType};
|
||||
pub use company::{BusinessType, Company, CompanyStatus};
|
||||
pub mod shareholder;
|
||||
pub use product::{Product, ProductComponent, ProductStatus, ProductType};
|
||||
pub use shareholder::{Shareholder, ShareholderType};
|
||||
pub use product::{Product, ProductType, ProductStatus, ProductComponent};
|
||||
|
||||
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")]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ProductType represents the type of a product
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
||||
|
@ -1,15 +1,15 @@
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Engine, Module, Dynamic, EvalAltResult, Position, INT};
|
||||
use std::sync::Arc;
|
||||
use std::mem;
|
||||
use crate::db::Collection; // For db.set and db.get_by_id
|
||||
use crate::db::hero::OurDB;
|
||||
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::{Company, CompanyStatus, BusinessType};
|
||||
use crate::models::biz::shareholder::{Shareholder, ShareholderType};
|
||||
use crate::models::biz::product::{Product, ProductType, ProductStatus, ProductComponent};
|
||||
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;
|
||||
@ -21,12 +21,12 @@ type RhaiSaleItem = SaleItem;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_|
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
@ -39,42 +39,60 @@ mod rhai_biz_module {
|
||||
|
||||
// Company builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn company_name(company: &mut RhaiCompany, name: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
pub fn company_name(
|
||||
company: &mut RhaiCompany,
|
||||
name: String,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.name(name);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "fiscal_year_end", return_raw, global, pure)]
|
||||
pub fn company_fiscal_year_end(company: &mut RhaiCompany, fiscal_year_end: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
pub fn company_fiscal_year_end(
|
||||
company: &mut RhaiCompany,
|
||||
fiscal_year_end: String,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.fiscal_year_end(fiscal_year_end);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "registration_number", return_raw, global, pure)]
|
||||
pub fn company_registration_number(company: &mut RhaiCompany, reg_num: String) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
pub fn company_registration_number(
|
||||
company: &mut RhaiCompany,
|
||||
reg_num: String,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.registration_number(reg_num);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "incorporation_date", return_raw, global, pure)]
|
||||
pub fn company_incorporation_date(company: &mut RhaiCompany, date: i64) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
pub fn company_incorporation_date(
|
||||
company: &mut RhaiCompany,
|
||||
date: i64,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.incorporation_date(date);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn company_status(company: &mut RhaiCompany, status: CompanyStatus) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
pub fn company_status(
|
||||
company: &mut RhaiCompany,
|
||||
status: CompanyStatus,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.status(status);
|
||||
Ok(company.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "business_type", return_raw, global, pure)]
|
||||
pub fn company_business_type(company: &mut RhaiCompany, business_type: BusinessType) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
pub fn company_business_type(
|
||||
company: &mut RhaiCompany,
|
||||
business_type: BusinessType,
|
||||
) -> Result<RhaiCompany, Box<EvalAltResult>> {
|
||||
let owned_company = mem::take(company);
|
||||
*company = owned_company.business_type(business_type);
|
||||
Ok(company.clone())
|
||||
@ -134,14 +152,20 @@ mod rhai_biz_module {
|
||||
|
||||
// Shareholder builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn shareholder_name(shareholder: &mut RhaiShareholder, name: String) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
pub fn shareholder_name(
|
||||
shareholder: &mut RhaiShareholder,
|
||||
name: String,
|
||||
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
let owned_shareholder = mem::take(shareholder);
|
||||
*shareholder = owned_shareholder.name(name);
|
||||
Ok(shareholder.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "company_id", return_raw, global, pure)]
|
||||
pub fn shareholder_company_id(shareholder: &mut RhaiShareholder, company_id: i64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
pub fn shareholder_company_id(
|
||||
shareholder: &mut RhaiShareholder,
|
||||
company_id: i64,
|
||||
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
let company_id_u32 = id_from_i64_to_u32(company_id)?;
|
||||
let owned_shareholder = mem::take(shareholder);
|
||||
*shareholder = owned_shareholder.company_id(company_id_u32);
|
||||
@ -149,14 +173,19 @@ mod rhai_biz_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "share_count", return_raw, global, pure)]
|
||||
pub fn shareholder_share_count(shareholder: &mut RhaiShareholder, share_count: f64) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
let owned_shareholder = mem::take(shareholder);
|
||||
pub fn shareholder_share_count(
|
||||
shareholder: &mut RhaiShareholder,
|
||||
share_count: f64,
|
||||
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
shareholder.shares = share_count;
|
||||
Ok(shareholder.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "type_", return_raw, global, pure)]
|
||||
pub fn shareholder_type(shareholder: &mut RhaiShareholder, type_: ShareholderType) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
pub fn shareholder_type(
|
||||
shareholder: &mut RhaiShareholder,
|
||||
type_: ShareholderType,
|
||||
) -> Result<RhaiShareholder, Box<EvalAltResult>> {
|
||||
let owned_shareholder = mem::take(shareholder);
|
||||
*shareholder = owned_shareholder.type_(type_);
|
||||
Ok(shareholder.clone())
|
||||
@ -196,21 +225,30 @@ mod rhai_biz_module {
|
||||
|
||||
// ProductComponent builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn product_component_name(component: &mut RhaiProductComponent, name: String) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
pub fn product_component_name(
|
||||
component: &mut RhaiProductComponent,
|
||||
name: String,
|
||||
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
let owned_component = mem::take(component);
|
||||
*component = owned_component.name(name);
|
||||
Ok(component.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn product_component_description(component: &mut RhaiProductComponent, description: String) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
pub fn product_component_description(
|
||||
component: &mut RhaiProductComponent,
|
||||
description: String,
|
||||
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
let owned_component = mem::take(component);
|
||||
*component = owned_component.description(description);
|
||||
Ok(component.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "quantity", return_raw, global, pure)]
|
||||
pub fn product_component_quantity(component: &mut RhaiProductComponent, quantity: i64) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
pub fn product_component_quantity(
|
||||
component: &mut RhaiProductComponent,
|
||||
quantity: i64,
|
||||
) -> Result<RhaiProductComponent, Box<EvalAltResult>> {
|
||||
let owned_component = mem::take(component);
|
||||
*component = owned_component.quantity(quantity as u32);
|
||||
Ok(component.clone())
|
||||
@ -240,77 +278,110 @@ mod rhai_biz_module {
|
||||
|
||||
// Product builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn product_name(product: &mut RhaiProduct, name: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_name(
|
||||
product: &mut RhaiProduct,
|
||||
name: String,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.name(name);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn product_description(product: &mut RhaiProduct, description: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_description(
|
||||
product: &mut RhaiProduct,
|
||||
description: String,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.description(description);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "price", return_raw, global, pure)]
|
||||
pub fn product_price(product: &mut RhaiProduct, price: f64) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_price(
|
||||
product: &mut RhaiProduct,
|
||||
price: f64,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.price(price);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "type_", return_raw, global, pure)]
|
||||
pub fn product_type(product: &mut RhaiProduct, type_: ProductType) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_type(
|
||||
product: &mut RhaiProduct,
|
||||
type_: ProductType,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.type_(type_);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "category", return_raw, global, pure)]
|
||||
pub fn product_category(product: &mut RhaiProduct, category: String) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_category(
|
||||
product: &mut RhaiProduct,
|
||||
category: String,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.category(category);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn product_status(product: &mut RhaiProduct, status: ProductStatus) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_status(
|
||||
product: &mut RhaiProduct,
|
||||
status: ProductStatus,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.status(status);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "max_amount", return_raw, global, pure)]
|
||||
pub fn product_max_amount(product: &mut RhaiProduct, max_amount: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_max_amount(
|
||||
product: &mut RhaiProduct,
|
||||
max_amount: i64,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.max_amount(max_amount as u16);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "purchase_till", return_raw, global, pure)]
|
||||
pub fn product_purchase_till(product: &mut RhaiProduct, purchase_till: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_purchase_till(
|
||||
product: &mut RhaiProduct,
|
||||
purchase_till: i64,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.purchase_till(purchase_till);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "active_till", return_raw, global, pure)]
|
||||
pub fn product_active_till(product: &mut RhaiProduct, active_till: i64) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_active_till(
|
||||
product: &mut RhaiProduct,
|
||||
active_till: i64,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.active_till(active_till);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "add_component", return_raw, global, pure)]
|
||||
pub fn product_add_component(product: &mut RhaiProduct, component: RhaiProductComponent) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_add_component(
|
||||
product: &mut RhaiProduct,
|
||||
component: RhaiProductComponent,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.add_component(component);
|
||||
Ok(product.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "components", return_raw, global, pure)]
|
||||
pub fn product_components(product: &mut RhaiProduct, components: Vec<RhaiProductComponent>) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
pub fn product_components(
|
||||
product: &mut RhaiProduct,
|
||||
components: Vec<RhaiProductComponent>,
|
||||
) -> Result<RhaiProduct, Box<EvalAltResult>> {
|
||||
let owned_product = mem::take(product);
|
||||
*product = owned_product.components(components);
|
||||
Ok(product.clone())
|
||||
@ -384,7 +455,12 @@ mod rhai_biz_module {
|
||||
|
||||
#[rhai_fn(name = "get_product_comments")]
|
||||
pub fn get_product_comments(product: &mut RhaiProduct) -> Vec<i64> {
|
||||
product.base_data.comments.iter().map(|&id| id as i64).collect()
|
||||
product
|
||||
.base_data
|
||||
.comments
|
||||
.iter()
|
||||
.map(|&id| id as i64)
|
||||
.collect()
|
||||
}
|
||||
|
||||
// --- SaleItem Functions ---
|
||||
@ -395,28 +471,39 @@ mod rhai_biz_module {
|
||||
|
||||
// SaleItem builder methods
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn sale_item_name(item: &mut RhaiSaleItem, name: String) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
pub fn sale_item_name(
|
||||
item: &mut RhaiSaleItem,
|
||||
name: String,
|
||||
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
let owned_item = mem::take(item);
|
||||
*item = owned_item.name(name);
|
||||
Ok(item.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "price", return_raw, global, pure)]
|
||||
pub fn sale_item_price(item: &mut RhaiSaleItem, price: f64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
let owned_item = mem::take(item);
|
||||
pub fn sale_item_price(
|
||||
item: &mut RhaiSaleItem,
|
||||
price: f64,
|
||||
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
item.unit_price = price;
|
||||
Ok(item.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "quantity", return_raw, global, pure)]
|
||||
pub fn sale_item_quantity(item: &mut RhaiSaleItem, quantity: i64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
pub fn sale_item_quantity(
|
||||
item: &mut RhaiSaleItem,
|
||||
quantity: i64,
|
||||
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
let owned_item = mem::take(item);
|
||||
*item = owned_item.quantity(quantity.try_into().unwrap());
|
||||
Ok(item.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "product_id", return_raw, global, pure)]
|
||||
pub fn sale_item_product_id(item: &mut RhaiSaleItem, product_id: i64) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
pub fn sale_item_product_id(
|
||||
item: &mut RhaiSaleItem,
|
||||
product_id: i64,
|
||||
) -> Result<RhaiSaleItem, Box<EvalAltResult>> {
|
||||
let product_id_u32 = id_from_i64_to_u32(product_id)?;
|
||||
let owned_item = mem::take(item);
|
||||
*item = owned_item.product_id(product_id_u32);
|
||||
@ -451,28 +538,39 @@ mod rhai_biz_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "transaction_id", return_raw, global, pure)]
|
||||
pub fn sale_transaction_id(sale: &mut RhaiSale, transaction_id: u32) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
let owned_sale = mem::take(sale);
|
||||
pub fn sale_transaction_id(
|
||||
sale: &mut RhaiSale,
|
||||
transaction_id: u32,
|
||||
) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
sale.transaction_id = transaction_id;
|
||||
Ok(sale.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn sale_status(sale: &mut RhaiSale, status: SaleStatus) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
pub fn sale_status(
|
||||
sale: &mut RhaiSale,
|
||||
status: SaleStatus,
|
||||
) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
let owned_sale = mem::take(sale);
|
||||
*sale = owned_sale.status(status);
|
||||
Ok(sale.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "add_item", return_raw, global, pure)]
|
||||
pub fn sale_add_item(sale: &mut RhaiSale, item: RhaiSaleItem) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
pub fn sale_add_item(
|
||||
sale: &mut RhaiSale,
|
||||
item: RhaiSaleItem,
|
||||
) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
let owned_sale = mem::take(sale);
|
||||
*sale = owned_sale.add_item(item);
|
||||
Ok(sale.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "items", return_raw, global, pure)]
|
||||
pub fn sale_items(sale: &mut RhaiSale, items: Vec<RhaiSaleItem>) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
pub fn sale_items(
|
||||
sale: &mut RhaiSale,
|
||||
items: Vec<RhaiSaleItem>,
|
||||
) -> Result<RhaiSale, Box<EvalAltResult>> {
|
||||
let owned_sale = mem::take(sale);
|
||||
*sale = owned_sale.items(items);
|
||||
Ok(sale.clone())
|
||||
@ -511,7 +609,11 @@ mod rhai_biz_module {
|
||||
|
||||
#[rhai_fn(name = "get_sale_comments")]
|
||||
pub fn get_sale_comments(sale: &mut RhaiSale) -> Vec<i64> {
|
||||
sale.base_data.comments.iter().map(|&id| id as i64).collect()
|
||||
sale.base_data
|
||||
.comments
|
||||
.iter()
|
||||
.map(|&id| id as i64)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@ -527,99 +629,163 @@ pub fn register_biz_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
|
||||
// Add database functions for Company
|
||||
let db_for_set_company = Arc::clone(&db);
|
||||
db_module.set_native_fn("set_company", move |company: Company| -> Result<INT, Box<EvalAltResult>> {
|
||||
let company_collection_set = db_for_set_company.collection::<Company>().expect("Failed to get company collection for set in closure");
|
||||
company_collection_set.set(&company)
|
||||
db_module.set_native_fn(
|
||||
"set_company",
|
||||
move |company: Company| -> Result<INT, Box<EvalAltResult>> {
|
||||
let company_collection_set = db_for_set_company
|
||||
.collection::<Company>()
|
||||
.expect("Failed to get company collection for set in closure");
|
||||
company_collection_set
|
||||
.set(&company)
|
||||
.map(|(id_val, _)| id_val as INT)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save company: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_for_get_company = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_company_by_id", move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let company_collection_get = db_for_get_company.collection::<Company>().expect("Failed to get company collection for get in closure");
|
||||
db_module.set_native_fn(
|
||||
"get_company_by_id",
|
||||
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let company_collection_get = db_for_get_company
|
||||
.collection::<Company>()
|
||||
.expect("Failed to get company collection for get in closure");
|
||||
let id_u32 = id_from_i64_to_u32(id)?;
|
||||
company_collection_get.get_by_id(id_u32)
|
||||
company_collection_get
|
||||
.get_by_id(id_u32)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get company with id {}: {:?}", id, e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add database functions for Shareholder
|
||||
let db_for_set_shareholder = Arc::clone(&db);
|
||||
db_module.set_native_fn("set_shareholder", move |shareholder: Shareholder| -> Result<INT, Box<EvalAltResult>> {
|
||||
let shareholder_collection_set = db_for_set_shareholder.collection::<Shareholder>().expect("Failed to get shareholder collection for set in closure");
|
||||
shareholder_collection_set.set(&shareholder)
|
||||
db_module.set_native_fn(
|
||||
"set_shareholder",
|
||||
move |shareholder: Shareholder| -> Result<INT, Box<EvalAltResult>> {
|
||||
let shareholder_collection_set = db_for_set_shareholder
|
||||
.collection::<Shareholder>()
|
||||
.expect("Failed to get shareholder collection for set in closure");
|
||||
shareholder_collection_set
|
||||
.set(&shareholder)
|
||||
.map(|(id_val, _)| id_val as INT)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save shareholder: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_for_get_shareholder = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_shareholder_by_id", move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let shareholder_collection_get = db_for_get_shareholder.collection::<Shareholder>().expect("Failed to get shareholder collection for get in closure");
|
||||
db_module.set_native_fn(
|
||||
"get_shareholder_by_id",
|
||||
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let shareholder_collection_get = db_for_get_shareholder
|
||||
.collection::<Shareholder>()
|
||||
.expect("Failed to get shareholder collection for get in closure");
|
||||
let id_u32 = id_from_i64_to_u32(id)?;
|
||||
shareholder_collection_get.get_by_id(id_u32)
|
||||
shareholder_collection_get
|
||||
.get_by_id(id_u32)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get shareholder with id {}: {:?}", id, e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add database functions for Product
|
||||
let db_for_set_product = Arc::clone(&db);
|
||||
db_module.set_native_fn("set_product", move |product: Product| -> Result<INT, Box<EvalAltResult>> {
|
||||
let product_collection_set = db_for_set_product.collection::<Product>().expect("Failed to get product collection for set in closure");
|
||||
product_collection_set.set(&product)
|
||||
db_module.set_native_fn(
|
||||
"set_product",
|
||||
move |product: Product| -> Result<INT, Box<EvalAltResult>> {
|
||||
let product_collection_set = db_for_set_product
|
||||
.collection::<Product>()
|
||||
.expect("Failed to get product collection for set in closure");
|
||||
product_collection_set
|
||||
.set(&product)
|
||||
.map(|(id_val, _)| id_val as INT)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save product: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_for_get_product = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_product_by_id", move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let product_collection_get = db_for_get_product.collection::<Product>().expect("Failed to get product collection for get in closure");
|
||||
db_module.set_native_fn(
|
||||
"get_product_by_id",
|
||||
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let product_collection_get = db_for_get_product
|
||||
.collection::<Product>()
|
||||
.expect("Failed to get product collection for get in closure");
|
||||
let id_u32 = id_from_i64_to_u32(id)?;
|
||||
product_collection_get.get_by_id(id_u32)
|
||||
product_collection_get
|
||||
.get_by_id(id_u32)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get product with id {}: {:?}", id, e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add database functions for Sale
|
||||
let db_for_set_sale = Arc::clone(&db);
|
||||
db_module.set_native_fn("set_sale", move |sale: Sale| -> Result<INT, Box<EvalAltResult>> {
|
||||
let sale_collection_set = db_for_set_sale.collection::<Sale>().expect("Failed to get sale collection for set in closure");
|
||||
sale_collection_set.set(&sale)
|
||||
db_module.set_native_fn(
|
||||
"set_sale",
|
||||
move |sale: Sale| -> Result<INT, Box<EvalAltResult>> {
|
||||
let sale_collection_set = db_for_set_sale
|
||||
.collection::<Sale>()
|
||||
.expect("Failed to get sale collection for set in closure");
|
||||
sale_collection_set
|
||||
.set(&sale)
|
||||
.map(|(id_val, _)| id_val as INT)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save sale: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_for_get_sale = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_sale_by_id", move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let sale_collection_get = db_for_get_sale.collection::<Sale>().expect("Failed to get sale collection for get in closure");
|
||||
db_module.set_native_fn(
|
||||
"get_sale_by_id",
|
||||
move |id: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let sale_collection_get = db_for_get_sale
|
||||
.collection::<Sale>()
|
||||
.expect("Failed to get sale collection for get in closure");
|
||||
let id_u32 = id_from_i64_to_u32(id)?;
|
||||
sale_collection_get.get_by_id(id_u32)
|
||||
sale_collection_get
|
||||
.get_by_id(id_u32)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get sale with id {}: {:?}", id, e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Register the database module globally
|
||||
engine.register_global_module(db_module.into());
|
||||
|
@ -1,5 +1,5 @@
|
||||
use heromodels_core::{BaseModelData, BaseModelDataOps, Model};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_core::{BaseModelData, Model, BaseModelDataOps};
|
||||
|
||||
/// Represents the status of a sale.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ShareholderType {
|
||||
|
@ -134,7 +134,11 @@ impl Event {
|
||||
/// Adds an attendee to the event
|
||||
pub fn add_attendee(mut self, attendee: Attendee) -> Self {
|
||||
// Prevent duplicate attendees by contact_id
|
||||
if !self.attendees.iter().any(|a| a.contact_id == attendee.contact_id) {
|
||||
if !self
|
||||
.attendees
|
||||
.iter()
|
||||
.any(|a| a.contact_id == attendee.contact_id)
|
||||
{
|
||||
self.attendees.push(attendee);
|
||||
}
|
||||
self
|
||||
@ -148,18 +152,18 @@ impl Event {
|
||||
|
||||
/// Updates the status of an existing attendee
|
||||
pub fn update_attendee_status(mut self, contact_id: u32, status: AttendanceStatus) -> Self {
|
||||
if let Some(attendee) = self.attendees.iter_mut().find(|a| a.contact_id == contact_id) {
|
||||
if let Some(attendee) = self
|
||||
.attendees
|
||||
.iter_mut()
|
||||
.find(|a| a.contact_id == contact_id)
|
||||
{
|
||||
attendee.status = status;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Reschedules the event to new start and end times
|
||||
pub fn reschedule(
|
||||
mut self,
|
||||
new_start_time: i64,
|
||||
new_end_time: i64,
|
||||
) -> Self {
|
||||
pub fn reschedule(mut self, new_start_time: i64, new_end_time: i64) -> Self {
|
||||
// Basic validation: end_time should be after start_time
|
||||
if new_end_time > new_start_time {
|
||||
self.start_time = new_start_time;
|
||||
@ -236,7 +240,8 @@ impl Calendar {
|
||||
|
||||
/// Removes an event from the calendar by its ID
|
||||
pub fn remove_event(mut self, event_id_to_remove: i64) -> Self {
|
||||
self.events.retain(|&event_id_in_vec| event_id_in_vec != event_id_to_remove);
|
||||
self.events
|
||||
.retain(|&event_id_in_vec| event_id_in_vec != event_id_to_remove);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ 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::{Calendar, Event, Attendee, AttendanceStatus};
|
||||
pub use self::calendar::{AttendanceStatus, Attendee, Calendar, Event};
|
||||
pub use rhai::register_calendar_rhai_module;
|
||||
|
@ -1,24 +1,24 @@
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
|
||||
use std::sync::Arc;
|
||||
use std::mem;
|
||||
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::{Event, Attendee, Calendar, AttendanceStatus};
|
||||
use super::calendar::{AttendanceStatus, Attendee, Calendar, Event};
|
||||
type RhaiEvent = Event;
|
||||
type RhaiAttendee = Attendee;
|
||||
type RhaiCalendar = Calendar;
|
||||
use crate::db::hero::OurDB;
|
||||
use crate::db::Collection;
|
||||
use crate::db::hero::OurDB;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_|
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
@ -31,7 +31,10 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Sets the event title
|
||||
#[rhai_fn(name = "title", return_raw, global, pure)]
|
||||
pub fn event_title(event: &mut RhaiEvent, title: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
pub fn event_title(
|
||||
event: &mut RhaiEvent,
|
||||
title: String,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.title(title);
|
||||
Ok(event.clone())
|
||||
@ -39,7 +42,10 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Sets the event description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn event_description(event: &mut RhaiEvent, description: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
pub fn event_description(
|
||||
event: &mut RhaiEvent,
|
||||
description: String,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.description(description);
|
||||
Ok(event.clone())
|
||||
@ -47,7 +53,10 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Sets the event location
|
||||
#[rhai_fn(name = "location", return_raw, global, pure)]
|
||||
pub fn event_location(event: &mut RhaiEvent, location: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
pub fn event_location(
|
||||
event: &mut RhaiEvent,
|
||||
location: String,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.location(location);
|
||||
Ok(event.clone())
|
||||
@ -55,7 +64,10 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Adds an attendee to the event
|
||||
#[rhai_fn(name = "add_attendee", return_raw, global, pure)]
|
||||
pub fn event_add_attendee(event: &mut RhaiEvent, attendee: RhaiAttendee) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
pub fn event_add_attendee(
|
||||
event: &mut RhaiEvent,
|
||||
attendee: RhaiAttendee,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
// Use take to get ownership of the event
|
||||
let owned_event = mem::take(event);
|
||||
*event = owned_event.add_attendee(attendee);
|
||||
@ -64,12 +76,16 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Reschedules the event with new start and end times
|
||||
#[rhai_fn(name = "reschedule", return_raw, global, pure)]
|
||||
pub fn event_reschedule(event: &mut RhaiEvent, new_start_time: i64, new_end_time: i64) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
pub fn event_reschedule(
|
||||
event: &mut RhaiEvent,
|
||||
new_start_time: i64,
|
||||
new_end_time: i64,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
// Validate timestamps
|
||||
if new_end_time <= new_start_time {
|
||||
return Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
"End time must be after start time".into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
)));
|
||||
}
|
||||
|
||||
@ -81,7 +97,11 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Updates an attendee's status in the event
|
||||
#[rhai_fn(name = "update_attendee_status", return_raw, global, pure)]
|
||||
pub fn event_update_attendee_status(event: &mut RhaiEvent, contact_id: i64, status_str: String) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
pub fn event_update_attendee_status(
|
||||
event: &mut RhaiEvent,
|
||||
contact_id: i64,
|
||||
status_str: String,
|
||||
) -> Result<RhaiEvent, Box<EvalAltResult>> {
|
||||
let status_enum = AttendanceStatus::from_string(&status_str)
|
||||
.map_err(|_| Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Invalid attendance status: '{}'. Expected one of: Pending, Accepted, Declined, Tentative", status_str).into(),
|
||||
@ -96,24 +116,42 @@ mod rhai_calendar_module {
|
||||
|
||||
// Event Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_event_id(event: &mut RhaiEvent) -> i64 { event.base_data.id as i64 }
|
||||
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 }
|
||||
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 }
|
||||
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() }
|
||||
pub fn get_event_title(event: &mut RhaiEvent) -> String {
|
||||
event.title.clone()
|
||||
}
|
||||
#[rhai_fn(get = "description", pure)]
|
||||
pub fn get_event_description(event: &mut RhaiEvent) -> Option<String> { event.description.clone() }
|
||||
pub fn get_event_description(event: &mut RhaiEvent) -> Option<String> {
|
||||
event.description.clone()
|
||||
}
|
||||
#[rhai_fn(get = "start_time", pure)]
|
||||
pub fn get_event_start_time(event: &mut RhaiEvent) -> i64 { event.start_time }
|
||||
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 }
|
||||
pub fn get_event_end_time(event: &mut RhaiEvent) -> i64 {
|
||||
event.end_time
|
||||
}
|
||||
#[rhai_fn(get = "location", pure)]
|
||||
pub fn get_event_location(event: &mut RhaiEvent) -> Option<String> { event.location.clone() }
|
||||
pub fn get_event_location(event: &mut RhaiEvent) -> Option<String> {
|
||||
event.location.clone()
|
||||
}
|
||||
#[rhai_fn(get = "attendees", pure)]
|
||||
pub fn get_event_attendees(event: &mut RhaiEvent) -> Vec<RhaiAttendee> { event.attendees.clone() }
|
||||
pub fn get_event_attendees(event: &mut RhaiEvent) -> Vec<RhaiAttendee> {
|
||||
event.attendees.clone()
|
||||
}
|
||||
|
||||
// --- Attendee Functions ---
|
||||
#[rhai_fn(name = "new_attendee")]
|
||||
@ -123,7 +161,10 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Sets the contact ID for an attendee
|
||||
#[rhai_fn(name = "with_contact_id", return_raw, global, pure)]
|
||||
pub fn attendee_with_contact_id(attendee: &mut RhaiAttendee, contact_id: i64) -> Result<RhaiAttendee, Box<EvalAltResult>> {
|
||||
pub fn attendee_with_contact_id(
|
||||
attendee: &mut RhaiAttendee,
|
||||
contact_id: i64,
|
||||
) -> Result<RhaiAttendee, Box<EvalAltResult>> {
|
||||
let new_contact_id = id_from_i64_to_u32(contact_id).unwrap_or(0);
|
||||
let owned_attendee = mem::replace(attendee, Attendee::new(0));
|
||||
*attendee = Attendee::new(new_contact_id);
|
||||
@ -133,7 +174,10 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Sets the status for an attendee
|
||||
#[rhai_fn(name = "with_status", return_raw, global, pure)]
|
||||
pub fn attendee_with_status(attendee: &mut RhaiAttendee, status_str: String) -> Result<RhaiAttendee, Box<EvalAltResult>> {
|
||||
pub fn attendee_with_status(
|
||||
attendee: &mut RhaiAttendee,
|
||||
status_str: String,
|
||||
) -> Result<RhaiAttendee, Box<EvalAltResult>> {
|
||||
let status_enum = AttendanceStatus::from_string(&status_str)
|
||||
.map_err(|_| Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Invalid attendance status: '{}'. Expected one of: Accepted, Declined, Tentative, NoResponse", status_str).into(),
|
||||
@ -149,9 +193,13 @@ mod rhai_calendar_module {
|
||||
|
||||
// Attendee Getters
|
||||
#[rhai_fn(get = "contact_id", pure)]
|
||||
pub fn get_attendee_contact_id(attendee: &mut RhaiAttendee) -> i64 { attendee.contact_id as i64 }
|
||||
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() }
|
||||
pub fn get_attendee_status(attendee: &mut RhaiAttendee) -> String {
|
||||
attendee.status.to_string()
|
||||
}
|
||||
|
||||
// --- Calendar Functions ---
|
||||
#[rhai_fn(name = "new_calendar", return_raw)]
|
||||
@ -162,7 +210,10 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Sets the calendar name
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn calendar_name(calendar: &mut RhaiCalendar, name: String) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
pub fn calendar_name(
|
||||
calendar: &mut RhaiCalendar,
|
||||
name: String,
|
||||
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
// Create a default Calendar to replace the taken one
|
||||
let default_calendar = Calendar::new(None, "");
|
||||
|
||||
@ -174,7 +225,10 @@ mod rhai_calendar_module {
|
||||
|
||||
/// Sets the calendar description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn calendar_description(calendar: &mut RhaiCalendar, description: String) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
pub fn calendar_description(
|
||||
calendar: &mut RhaiCalendar,
|
||||
description: String,
|
||||
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
// Create a default Calendar to replace the taken one
|
||||
let default_calendar = Calendar::new(None, "");
|
||||
|
||||
@ -186,7 +240,10 @@ mod rhai_calendar_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "add_event_to_calendar", return_raw, global, pure)]
|
||||
pub fn calendar_add_event(calendar: &mut RhaiCalendar, event: RhaiEvent) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
pub fn calendar_add_event(
|
||||
calendar: &mut RhaiCalendar,
|
||||
event: RhaiEvent,
|
||||
) -> Result<RhaiCalendar, Box<EvalAltResult>> {
|
||||
// Create a default Calendar to replace the taken one
|
||||
let default_calendar = Calendar::new(None, "");
|
||||
|
||||
@ -197,7 +254,10 @@ mod rhai_calendar_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "remove_event_from_calendar", return_raw)]
|
||||
pub fn calendar_remove_event(calendar: &mut RhaiCalendar, event_id: i64) -> Result<(), Box<EvalAltResult>> {
|
||||
pub fn calendar_remove_event(
|
||||
calendar: &mut RhaiCalendar,
|
||||
event_id: i64,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
// Create a default Calendar to replace the taken one
|
||||
let default_calendar = Calendar::new(None, "");
|
||||
|
||||
@ -209,26 +269,37 @@ mod rhai_calendar_module {
|
||||
|
||||
// Calendar Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_calendar_id(calendar: &mut RhaiCalendar) -> i64 { calendar.base_data.id as i64 }
|
||||
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() }
|
||||
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 }
|
||||
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 }
|
||||
pub fn get_calendar_modified_at(calendar: &mut RhaiCalendar) -> i64 {
|
||||
calendar.base_data.modified_at
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "events", pure)]
|
||||
pub fn get_calendar_events(calendar: &mut RhaiCalendar) -> Vec<i64> { calendar.events.clone() }
|
||||
pub fn get_calendar_events(calendar: &mut RhaiCalendar) -> Vec<i64> {
|
||||
calendar.events.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "description", pure)]
|
||||
pub fn get_calendar_description(calendar: &mut RhaiCalendar) -> Option<String> { calendar.description.clone() }
|
||||
pub fn get_calendar_description(calendar: &mut RhaiCalendar) -> Option<String> {
|
||||
calendar.description.clone()
|
||||
}
|
||||
|
||||
// Calendar doesn't have an owner_id field in the current implementation
|
||||
// pub fn get_calendar_owner_id(calendar: &mut RhaiCalendar) -> i64 { calendar.owner_id as i64 }
|
||||
|
||||
}
|
||||
|
||||
pub fn register_calendar_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
@ -241,108 +312,170 @@ pub fn register_calendar_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_set_event = db.clone();
|
||||
db_module.set_native_fn("save_event", move |event: Event| -> Result<Event, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_event",
|
||||
move |event: Event| -> Result<Event, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_event.set(&event)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_event: {}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone_set_event.set(&event).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_event: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_event = db.clone();
|
||||
db_module.set_native_fn("delete_event", move |event: Event| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_event",
|
||||
move |event: Event| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_event.collection::<Event>()
|
||||
let result = db_clone_delete_event
|
||||
.collection::<Event>()
|
||||
.expect("can open event collection")
|
||||
.delete_by_id(event.base_data.id)
|
||||
.expect("can delete event");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_event = db.clone();
|
||||
db_module.set_native_fn("get_event_by_id", move |id_i64: INT| -> Result<Event, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_event_by_id",
|
||||
move |id_i64: INT| -> Result<Event, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_event.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_event_by_id: {}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Event with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone_get_event
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_event_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Event with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_set_calendar = db.clone();
|
||||
db_module.set_native_fn("save_calendar", move |calendar: Calendar| -> Result<Calendar, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_calendar",
|
||||
move |calendar: Calendar| -> Result<Calendar, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_calendar.set(&calendar)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_calendar: {}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone_set_calendar.set(&calendar).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_calendar: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated calendar with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_calendar = db.clone();
|
||||
db_module.set_native_fn("delete_calendar", move |calendar: Calendar| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_calendar",
|
||||
move |calendar: Calendar| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_calendar.collection::<Calendar>()
|
||||
let result = db_clone_delete_calendar
|
||||
.collection::<Calendar>()
|
||||
.expect("can open calendar collection")
|
||||
.delete_by_id(calendar.base_data.id)
|
||||
.expect("can delete event");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_calendar = db.clone();
|
||||
db_module.set_native_fn("get_calendar_by_id", move |id_i64: INT| -> Result<Calendar, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_calendar_by_id",
|
||||
move |id_i64: INT| -> Result<Calendar, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_calendar.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_calendar_by_id: {}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Calendar with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone_get_calendar
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_calendar_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Calendar with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_calendars function to get all calendars
|
||||
let db_clone_list_calendars = db.clone();
|
||||
db_module.set_native_fn("list_calendars", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_calendars.collection::<Calendar>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_calendars",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_calendars
|
||||
.collection::<Calendar>()
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get calendar collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let calendars = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let calendars = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all calendars: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for calendar in calendars {
|
||||
array.push(Dynamic::from(calendar));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_events function to get all events
|
||||
let db_clone_list_events = db.clone();
|
||||
db_module.set_native_fn("list_events", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_events.collection::<Event>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_events",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_events.collection::<Event>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get event collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let events = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let events = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all events: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
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());
|
||||
|
@ -19,6 +19,8 @@ pub struct Circle {
|
||||
pub description: Option<String>,
|
||||
/// List of related circles
|
||||
pub circles: Vec<String>,
|
||||
/// List of members in the circle (their public keys)
|
||||
pub members: Vec<String>,
|
||||
/// Logo URL or symbol for the circle
|
||||
pub logo: Option<String>,
|
||||
/// Theme settings for the circle (colors, styling, etc.)
|
||||
@ -35,6 +37,7 @@ impl Circle {
|
||||
description: None,
|
||||
circles: Vec::new(),
|
||||
logo: None,
|
||||
members: Vec::new(),
|
||||
theme: HashMap::new(),
|
||||
}
|
||||
}
|
||||
@ -83,4 +86,13 @@ impl Circle {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a member to the circle
|
||||
pub fn add_member(mut self, member: String) -> Self {
|
||||
// Prevent duplicate members
|
||||
if !self.members.iter().any(|a| *a == member) {
|
||||
self.members.push(member);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
@ -3,5 +3,5 @@ pub mod circle;
|
||||
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::circle::{Circle};
|
||||
pub use self::circle::Circle;
|
||||
pub use rhai::register_circle_rhai_module;
|
||||
|
@ -1,16 +1,16 @@
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array, CustomType};
|
||||
use std::sync::Arc;
|
||||
use std::mem;
|
||||
use crate::db::Db;
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Array, CustomType, Dynamic, Engine, EvalAltResult, INT, Module, Position};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::circle::{Circle};
|
||||
use super::circle::Circle;
|
||||
type RhaiCircle = Circle;
|
||||
use crate::db::hero::OurDB;
|
||||
use crate::db::Collection;
|
||||
use crate::db::hero::OurDB;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use serde_json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Registers a `.json()` method for any type `T` that implements the required traits.
|
||||
fn register_json_method<T>(engine: &mut Engine)
|
||||
@ -31,12 +31,12 @@ where
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_|
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
@ -49,7 +49,10 @@ mod rhai_circle_module {
|
||||
|
||||
/// Sets the circle title
|
||||
#[rhai_fn(name = "title", return_raw, global, pure)]
|
||||
pub fn circle_title(circle: &mut RhaiCircle, title: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
pub fn circle_title(
|
||||
circle: &mut RhaiCircle,
|
||||
title: String,
|
||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
let owned_circle = mem::take(circle);
|
||||
*circle = owned_circle.title(title);
|
||||
Ok(circle.clone())
|
||||
@ -57,7 +60,10 @@ mod rhai_circle_module {
|
||||
|
||||
/// Sets the circle ws_url
|
||||
#[rhai_fn(name = "ws_url", return_raw, global, pure)]
|
||||
pub fn circle_ws_url(circle: &mut RhaiCircle, ws_url: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
pub fn circle_ws_url(
|
||||
circle: &mut RhaiCircle,
|
||||
ws_url: String,
|
||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
let owned_circle = mem::take(circle);
|
||||
*circle = owned_circle.ws_url(ws_url);
|
||||
Ok(circle.clone())
|
||||
@ -65,7 +71,10 @@ mod rhai_circle_module {
|
||||
|
||||
/// Sets the circle description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn circle_description(circle: &mut RhaiCircle, description: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
pub fn circle_description(
|
||||
circle: &mut RhaiCircle,
|
||||
description: String,
|
||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
let owned_circle = mem::take(circle);
|
||||
*circle = owned_circle.description(description);
|
||||
Ok(circle.clone())
|
||||
@ -73,7 +82,10 @@ mod rhai_circle_module {
|
||||
|
||||
/// Sets the circle logo
|
||||
#[rhai_fn(name = "logo", return_raw, global, pure)]
|
||||
pub fn circle_logo(circle: &mut RhaiCircle, logo: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
pub fn circle_logo(
|
||||
circle: &mut RhaiCircle,
|
||||
logo: String,
|
||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
let owned_circle = mem::take(circle);
|
||||
*circle = owned_circle.logo(logo);
|
||||
Ok(circle.clone())
|
||||
@ -81,7 +93,10 @@ mod rhai_circle_module {
|
||||
|
||||
/// Sets the circle theme
|
||||
#[rhai_fn(name = "theme", return_raw, global, pure)]
|
||||
pub fn circle_theme(circle: &mut RhaiCircle, theme: HashMap<String, String>) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
pub fn circle_theme(
|
||||
circle: &mut RhaiCircle,
|
||||
theme: HashMap<String, String>,
|
||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
let owned_circle = mem::take(circle);
|
||||
*circle = owned_circle.theme(theme);
|
||||
Ok(circle.clone())
|
||||
@ -89,33 +104,66 @@ mod rhai_circle_module {
|
||||
|
||||
/// Adds an attendee to the circle
|
||||
#[rhai_fn(name = "add_circle", return_raw, global, pure)]
|
||||
pub fn circle_add_circle(circle: &mut RhaiCircle, added_circle: String) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
pub fn circle_add_circle(
|
||||
circle: &mut RhaiCircle,
|
||||
added_circle: String,
|
||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
// Use take to get ownership of the circle
|
||||
let owned_circle = mem::take(circle);
|
||||
*circle = owned_circle.add_circle(added_circle);
|
||||
Ok(circle.clone())
|
||||
}
|
||||
|
||||
/// Adds an attendee to the circle
|
||||
#[rhai_fn(name = "add_member", return_raw, global, pure)]
|
||||
pub fn circle_add_member(
|
||||
circle: &mut RhaiCircle,
|
||||
added_member: String,
|
||||
) -> Result<RhaiCircle, Box<EvalAltResult>> {
|
||||
// Use take to get ownership of the circle
|
||||
let owned_circle = mem::take(circle);
|
||||
*circle = owned_circle.add_member(added_member);
|
||||
Ok(circle.clone())
|
||||
}
|
||||
|
||||
// Circle Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_circle_id(circle: &mut RhaiCircle) -> i64 { circle.base_data.id as i64 }
|
||||
pub fn get_circle_id(circle: &mut RhaiCircle) -> i64 {
|
||||
circle.base_data.id as i64
|
||||
}
|
||||
#[rhai_fn(get = "created_at", pure)]
|
||||
pub fn get_circle_created_at(circle: &mut RhaiCircle) -> i64 { circle.base_data.created_at }
|
||||
pub fn get_circle_created_at(circle: &mut RhaiCircle) -> i64 {
|
||||
circle.base_data.created_at
|
||||
}
|
||||
#[rhai_fn(get = "modified_at", pure)]
|
||||
pub fn get_circle_modified_at(circle: &mut RhaiCircle) -> i64 { circle.base_data.modified_at }
|
||||
pub fn get_circle_modified_at(circle: &mut RhaiCircle) -> i64 {
|
||||
circle.base_data.modified_at
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "title", pure)]
|
||||
pub fn get_circle_title(circle: &mut RhaiCircle) -> String { circle.title.clone() }
|
||||
pub fn get_circle_title(circle: &mut RhaiCircle) -> String {
|
||||
circle.title.clone()
|
||||
}
|
||||
#[rhai_fn(get = "description", pure)]
|
||||
pub fn get_circle_description(circle: &mut RhaiCircle) -> Option<String> { circle.description.clone() }
|
||||
pub fn get_circle_description(circle: &mut RhaiCircle) -> Option<String> {
|
||||
circle.description.clone()
|
||||
}
|
||||
#[rhai_fn(get = "circles", pure)]
|
||||
pub fn get_circle_circles(circle: &mut RhaiCircle) -> Vec<String> { circle.circles.clone() }
|
||||
pub fn get_circle_circles(circle: &mut RhaiCircle) -> Vec<String> {
|
||||
circle.circles.clone()
|
||||
}
|
||||
#[rhai_fn(get = "ws_url", pure)]
|
||||
pub fn get_circle_ws_url(circle: &mut RhaiCircle) -> String { circle.ws_url.clone() }
|
||||
pub fn get_circle_ws_url(circle: &mut RhaiCircle) -> String {
|
||||
circle.ws_url.clone()
|
||||
}
|
||||
#[rhai_fn(get = "logo", pure)]
|
||||
pub fn get_circle_logo(circle: &mut RhaiCircle) -> Option<String> { circle.logo.clone() }
|
||||
pub fn get_circle_logo(circle: &mut RhaiCircle) -> Option<String> {
|
||||
circle.logo.clone()
|
||||
}
|
||||
#[rhai_fn(get = "theme", pure)]
|
||||
pub fn get_circle_theme(circle: &mut RhaiCircle) -> HashMap<String, String> { circle.theme.clone() }
|
||||
pub fn get_circle_theme(circle: &mut RhaiCircle) -> HashMap<String, String> {
|
||||
circle.theme.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_circle_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
@ -128,71 +176,111 @@ pub fn register_circle_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_set_circle = db.clone();
|
||||
db_module.set_native_fn("save_circle", move |circle: Circle| -> Result<Circle, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_circle",
|
||||
move |circle: Circle| -> Result<Circle, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_circle.set(&circle)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_circle: {}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone_set_circle.set(&circle).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_circle: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated circle with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
register_json_method::<Circle>(engine);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_circle = db.clone();
|
||||
db_module.set_native_fn("delete_circle", move |circle: Circle| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_circle",
|
||||
move |circle: Circle| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_circle.collection::<Circle>()
|
||||
let result = db_clone_delete_circle
|
||||
.collection::<Circle>()
|
||||
.expect("can open circle collection")
|
||||
.delete_by_id(circle.base_data.id)
|
||||
.expect("can delete circle");
|
||||
|
||||
// Return the updated circle with the correct ID
|
||||
Ok(result)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_circle = db.clone();
|
||||
db_module.set_native_fn("get_circle", move || -> Result<Circle, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_circle",
|
||||
move || -> Result<Circle, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let all_circles: Vec<Circle> = db_clone_get_circle.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_circle: {}", e).into(), Position::NONE)))?;
|
||||
let all_circles: Vec<Circle> = db_clone_get_circle.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_circle: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
if let Some(first_circle) = all_circles.first() {
|
||||
Ok(first_circle.clone())
|
||||
} else {
|
||||
Err(Box::new(EvalAltResult::ErrorRuntime("Circle not found".into(), Position::NONE)))
|
||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
"Circle not found".into(),
|
||||
Position::NONE,
|
||||
)))
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_circle_by_id = db.clone();
|
||||
db_module.set_native_fn("get_circle_by_id", move |id_i64: INT| -> Result<Circle, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_circle_by_id",
|
||||
move |id_i64: INT| -> Result<Circle, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_circle_by_id.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_circle_by_id: {}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Circle with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone_get_circle_by_id
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_circle_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Circle with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_circles function to get all circles
|
||||
let db_clone_list_circles = db.clone();
|
||||
db_module.set_native_fn("list_circles", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_circles.collection::<Circle>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_circles",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_circles.collection::<Circle>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get circle collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let circles = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let circles = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all circles: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for circle in circles {
|
||||
array.push(Dynamic::from(circle));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Register the database module globally
|
||||
engine.register_global_module(db_module.into());
|
||||
|
@ -1,23 +1,23 @@
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
|
||||
use std::sync::Arc;
|
||||
use std::mem;
|
||||
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::{Group, Contact};
|
||||
use super::contact::{Contact, Group};
|
||||
type RhaiGroup = Group;
|
||||
type RhaiContact = Contact;
|
||||
use crate::db::hero::OurDB;
|
||||
use crate::db::Collection;
|
||||
use crate::db::hero::OurDB;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_|
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
@ -30,7 +30,10 @@ mod rhai_contact_module {
|
||||
|
||||
/// Sets the event title
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn group_name(group: &mut RhaiGroup, name: String) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
pub fn group_name(
|
||||
group: &mut RhaiGroup,
|
||||
name: String,
|
||||
) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
let owned_group = mem::take(group);
|
||||
*group = owned_group.name(name);
|
||||
Ok(group.clone())
|
||||
@ -38,7 +41,10 @@ mod rhai_contact_module {
|
||||
|
||||
/// Sets the event description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn group_description(group: &mut RhaiGroup, description: String) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
pub fn group_description(
|
||||
group: &mut RhaiGroup,
|
||||
description: String,
|
||||
) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
let owned_group = mem::take(group);
|
||||
*group = owned_group.description(description);
|
||||
Ok(group.clone())
|
||||
@ -46,7 +52,10 @@ mod rhai_contact_module {
|
||||
|
||||
/// Adds an attendee to the event
|
||||
#[rhai_fn(name = "add_contact", return_raw, global, pure)]
|
||||
pub fn group_add_contact(group: &mut RhaiGroup, contact_id: i64) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
pub fn group_add_contact(
|
||||
group: &mut RhaiGroup,
|
||||
contact_id: i64,
|
||||
) -> Result<RhaiGroup, Box<EvalAltResult>> {
|
||||
// Use take to get ownership of the event
|
||||
let owned_group = mem::take(group);
|
||||
*group = owned_group.add_contact(contact_id as u32);
|
||||
@ -54,21 +63,37 @@ mod rhai_contact_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(get = "contacts", pure)]
|
||||
pub fn get_group_contacts(group: &mut RhaiGroup) -> Vec<i64> { group.contacts.clone().into_iter().map(|id| id as i64).collect() }
|
||||
pub fn get_group_contacts(group: &mut RhaiGroup) -> Vec<i64> {
|
||||
group
|
||||
.contacts
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|id| id as i64)
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Group Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_group_id(group: &mut RhaiGroup) -> i64 { group.base_data.id as i64 }
|
||||
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 }
|
||||
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 }
|
||||
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() }
|
||||
pub fn get_group_name(group: &mut RhaiGroup) -> String {
|
||||
group.name.clone()
|
||||
}
|
||||
#[rhai_fn(get = "description", pure)]
|
||||
pub fn get_group_description(group: &mut RhaiGroup) -> Option<String> { group.description.clone() }
|
||||
|
||||
pub fn get_group_description(group: &mut RhaiGroup) -> Option<String> {
|
||||
group.description.clone()
|
||||
}
|
||||
|
||||
// --- Contact Functions ---
|
||||
#[rhai_fn(name = "new_contact", return_raw)]
|
||||
@ -79,7 +104,10 @@ mod rhai_contact_module {
|
||||
|
||||
/// Sets the contact name
|
||||
#[rhai_fn(name = "name", return_raw, global, pure)]
|
||||
pub fn contact_name(contact: &mut RhaiContact, name: String) -> Result<RhaiContact, Box<EvalAltResult>> {
|
||||
pub fn contact_name(
|
||||
contact: &mut RhaiContact,
|
||||
name: String,
|
||||
) -> Result<RhaiContact, Box<EvalAltResult>> {
|
||||
// Create a default Contact to replace the taken one
|
||||
let default_contact = Contact::new();
|
||||
|
||||
@ -91,7 +119,10 @@ mod rhai_contact_module {
|
||||
|
||||
/// Sets the contact description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn contact_description(contact: &mut RhaiContact, description: String) -> Result<RhaiContact, Box<EvalAltResult>> {
|
||||
pub fn contact_description(
|
||||
contact: &mut RhaiContact,
|
||||
description: String,
|
||||
) -> Result<RhaiContact, Box<EvalAltResult>> {
|
||||
// Create a default Contact to replace the taken one
|
||||
let default_contact = Contact::new();
|
||||
|
||||
@ -103,16 +134,24 @@ mod rhai_contact_module {
|
||||
|
||||
// Contact Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_contact_id(contact: &mut RhaiContact) -> i64 { contact.base_data.id as i64 }
|
||||
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() }
|
||||
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 }
|
||||
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 get_contact_modified_at(contact: &mut RhaiContact) -> i64 {
|
||||
contact.base_data.modified_at
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_contact_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
@ -125,108 +164,170 @@ pub fn register_contact_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_set_group = db.clone();
|
||||
db_module.set_native_fn("save_group", move |group: Group| -> Result<Group, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_group",
|
||||
move |group: Group| -> Result<Group, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_group.set(&group)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_group: {}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone_set_group.set(&group).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_group: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_group = db.clone();
|
||||
db_module.set_native_fn("delete_group", move |group: Group| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_group",
|
||||
move |group: Group| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_group.collection::<Group>()
|
||||
let result = db_clone_delete_group
|
||||
.collection::<Group>()
|
||||
.expect("can open group collection")
|
||||
.delete_by_id(group.base_data.id)
|
||||
.expect("can delete group");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_group = db.clone();
|
||||
db_module.set_native_fn("get_group_by_id", move |id_i64: INT| -> Result<Group, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_group_by_id",
|
||||
move |id_i64: INT| -> Result<Group, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_group.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_event_by_id: {}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Event with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone_get_group
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_event_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Event with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_set_contact = db.clone();
|
||||
db_module.set_native_fn("save_contact", move |contact: Contact| -> Result<Contact, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_contact",
|
||||
move |contact: Contact| -> Result<Contact, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_set_contact.set(&contact)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error set_contact: {}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone_set_contact.set(&contact).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error set_contact: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated contact with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Manually register database functions as they need to capture 'db'
|
||||
let db_clone_delete_contact = db.clone();
|
||||
db_module.set_native_fn("delete_contact", move |contact: Contact| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_contact",
|
||||
move |contact: Contact| -> Result<(), Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone_delete_contact.collection::<Contact>()
|
||||
let result = db_clone_delete_contact
|
||||
.collection::<Contact>()
|
||||
.expect("can open contact collection")
|
||||
.delete_by_id(contact.base_data.id)
|
||||
.expect("can delete event");
|
||||
|
||||
// Return the updated event with the correct ID
|
||||
Ok(result)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get_contact = db.clone();
|
||||
db_module.set_native_fn("get_contact_by_id", move |id_i64: INT| -> Result<Contact, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_contact_by_id",
|
||||
move |id_i64: INT| -> Result<Contact, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone_get_contact.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_contact_by_id: {}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Contact with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone_get_contact
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_contact_by_id: {}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Contact with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_contacts function to get all contacts
|
||||
let db_clone_list_contacts = db.clone();
|
||||
db_module.set_native_fn("list_contacts", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_contacts.collection::<Contact>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_contacts",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_contacts
|
||||
.collection::<Contact>()
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get contact collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let contacts = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let contacts = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all contacts: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for contact in contacts {
|
||||
array.push(Dynamic::from(contact));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Add list_events function to get all events
|
||||
let db_clone_list_groups = db.clone();
|
||||
db_module.set_native_fn("list_groups", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_groups.collection::<Group>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_groups",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone_list_groups.collection::<Group>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get group collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let groups = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let groups = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all groups: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
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());
|
||||
|
@ -4,4 +4,3 @@ pub mod model;
|
||||
|
||||
// Re-export key types for convenience
|
||||
pub use comment::Comment;
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
|
@ -1,9 +1,9 @@
|
||||
// heromodels/src/models/finance/account.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use heromodels_derive::model;
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::asset::Asset;
|
||||
|
||||
@ -87,6 +87,6 @@ impl Account {
|
||||
/// Find an asset by name
|
||||
pub fn find_asset_by_name(&self, _name: &str) -> Option<&Asset> {
|
||||
// TODO: implement
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
// heromodels/src/models/finance/asset.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use heromodels_derive::model;
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// AssetType defines the type of blockchain asset
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
|
@ -1,10 +1,10 @@
|
||||
// heromodels/src/models/finance/marketplace.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::asset::AssetType;
|
||||
|
||||
@ -53,8 +53,7 @@ impl Default for BidStatus {
|
||||
}
|
||||
|
||||
/// Bid represents a bid on an auction listing
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType, Default)]
|
||||
pub struct Bid {
|
||||
pub listing_id: String, // ID of the listing this bid belongs to
|
||||
pub bidder_id: u32, // ID of the user who placed the bid
|
||||
|
@ -9,4 +9,4 @@ pub mod rhai;
|
||||
// Re-export main models for easier access
|
||||
pub use self::account::Account;
|
||||
pub use self::asset::{Asset, AssetType};
|
||||
pub use self::marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus};
|
||||
pub use self::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
|
||||
|
@ -1,12 +1,12 @@
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
|
||||
use std::sync::Arc;
|
||||
use std::mem;
|
||||
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::{Listing, Bid, ListingStatus, ListingType, BidStatus};
|
||||
use super::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
|
||||
|
||||
use crate::db::hero::OurDB;
|
||||
use crate::db::{Collection, Db};
|
||||
@ -18,12 +18,12 @@ type RhaiBid = Bid;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_|
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions for enum conversions
|
||||
@ -45,8 +45,12 @@ fn string_to_asset_type(s: &str) -> Result<AssetType, Box<EvalAltResult>> {
|
||||
"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
|
||||
format!(
|
||||
"Invalid asset type: '{}'. Expected one of: Native, Erc20, Erc721, Erc1155",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
@ -68,8 +72,12 @@ fn string_to_listing_status(s: &str) -> Result<ListingStatus, Box<EvalAltResult>
|
||||
"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
|
||||
format!(
|
||||
"Invalid listing status: '{}'. Expected one of: Active, Sold, Cancelled, Expired",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
@ -89,8 +97,12 @@ fn string_to_listing_type(s: &str) -> Result<ListingType, Box<EvalAltResult>> {
|
||||
"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
|
||||
format!(
|
||||
"Invalid listing type: '{}'. Expected one of: FixedPrice, Auction, Exchange",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
@ -112,8 +124,12 @@ fn string_to_bid_status(s: &str) -> Result<BidStatus, Box<EvalAltResult>> {
|
||||
"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
|
||||
format!(
|
||||
"Invalid bid status: '{}'. Expected one of: Active, Accepted, Rejected, Cancelled",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))),
|
||||
}
|
||||
}
|
||||
@ -181,7 +197,10 @@ mod account_module {
|
||||
|
||||
// Setters using builder pattern with proper mutability handling
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn name(account: &mut RhaiAccount, name: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
pub fn name(
|
||||
account: &mut RhaiAccount,
|
||||
name: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.name(name);
|
||||
*account = acc;
|
||||
@ -189,7 +208,10 @@ mod account_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn user_id(account: &mut RhaiAccount, user_id: INT) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
pub fn user_id(
|
||||
account: &mut RhaiAccount,
|
||||
user_id: INT,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let user_id = id_from_i64_to_u32(user_id)?;
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.user_id(user_id);
|
||||
@ -198,7 +220,10 @@ mod account_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn description(account: &mut RhaiAccount, description: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
pub fn description(
|
||||
account: &mut RhaiAccount,
|
||||
description: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.description(description);
|
||||
*account = acc;
|
||||
@ -206,7 +231,10 @@ mod account_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn ledger(account: &mut RhaiAccount, ledger: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
pub fn ledger(
|
||||
account: &mut RhaiAccount,
|
||||
ledger: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.ledger(ledger);
|
||||
*account = acc;
|
||||
@ -214,7 +242,10 @@ mod account_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn address(account: &mut RhaiAccount, address: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
pub fn address(
|
||||
account: &mut RhaiAccount,
|
||||
address: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.address(address);
|
||||
*account = acc;
|
||||
@ -222,7 +253,10 @@ mod account_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn pubkey(account: &mut RhaiAccount, pubkey: String) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
pub fn pubkey(
|
||||
account: &mut RhaiAccount,
|
||||
pubkey: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.pubkey(pubkey);
|
||||
*account = acc;
|
||||
@ -230,7 +264,10 @@ mod account_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn add_asset(account: &mut RhaiAccount, asset_id: INT) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
pub fn add_asset(
|
||||
account: &mut RhaiAccount,
|
||||
asset_id: INT,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let asset_id = id_from_i64_to_u32(asset_id)?;
|
||||
let mut acc = mem::take(account);
|
||||
acc = acc.add_asset(asset_id);
|
||||
@ -301,7 +338,10 @@ mod asset_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn description(asset: &mut RhaiAsset, description: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
pub fn description(
|
||||
asset: &mut RhaiAsset,
|
||||
description: String,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.description(description);
|
||||
*asset = ast;
|
||||
@ -317,7 +357,10 @@ mod asset_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn address(asset: &mut RhaiAsset, address: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
pub fn address(
|
||||
asset: &mut RhaiAsset,
|
||||
address: String,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.address(address);
|
||||
*asset = ast;
|
||||
@ -325,7 +368,10 @@ mod asset_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn asset_type(asset: &mut RhaiAsset, asset_type_str: String) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
pub fn asset_type(
|
||||
asset: &mut RhaiAsset,
|
||||
asset_type_str: String,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let asset_type_enum = string_to_asset_type(&asset_type_str)?;
|
||||
let mut ast = mem::take(asset);
|
||||
ast = ast.asset_type(asset_type_enum);
|
||||
@ -338,7 +384,7 @@ mod asset_module {
|
||||
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
|
||||
Position::NONE,
|
||||
)));
|
||||
}
|
||||
let mut ast = mem::take(asset);
|
||||
@ -441,7 +487,10 @@ mod listing_module {
|
||||
|
||||
// Setters using builder pattern with proper mutability handling
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn seller_id(listing: &mut RhaiListing, seller_id: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn seller_id(
|
||||
listing: &mut RhaiListing,
|
||||
seller_id: INT,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let seller_id = id_from_i64_to_u32(seller_id)?;
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.seller_id(seller_id);
|
||||
@ -450,7 +499,10 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn asset_id(listing: &mut RhaiListing, asset_id: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn asset_id(
|
||||
listing: &mut RhaiListing,
|
||||
asset_id: INT,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let asset_id = id_from_i64_to_u32(asset_id)?;
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.asset_id(asset_id);
|
||||
@ -467,7 +519,10 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn currency(listing: &mut RhaiListing, currency: String) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn currency(
|
||||
listing: &mut RhaiListing,
|
||||
currency: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.currency(currency);
|
||||
*listing = lst;
|
||||
@ -475,7 +530,10 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn listing_type(listing: &mut RhaiListing, listing_type_str: String) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn listing_type(
|
||||
listing: &mut RhaiListing,
|
||||
listing_type_str: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let listing_type_enum = string_to_listing_type(&listing_type_str)?;
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.listing_type(listing_type_enum);
|
||||
@ -484,7 +542,10 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn title(listing: &mut RhaiListing, title: String) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn title(
|
||||
listing: &mut RhaiListing,
|
||||
title: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.title(title);
|
||||
*listing = lst;
|
||||
@ -492,7 +553,10 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn description(listing: &mut RhaiListing, description: String) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn description(
|
||||
listing: &mut RhaiListing,
|
||||
description: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.description(description);
|
||||
*listing = lst;
|
||||
@ -500,7 +564,10 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn image_url(listing: &mut RhaiListing, image_url: String) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn image_url(
|
||||
listing: &mut RhaiListing,
|
||||
image_url: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.image_url(Some(image_url));
|
||||
*listing = lst;
|
||||
@ -508,14 +575,20 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn expires_at(listing: &mut RhaiListing, end_date_ts: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn expires_at(
|
||||
listing: &mut RhaiListing,
|
||||
end_date_ts: INT,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
use chrono::TimeZone;
|
||||
let end_date = chrono::Utc.timestamp_opt(end_date_ts, 0)
|
||||
let end_date = chrono::Utc
|
||||
.timestamp_opt(end_date_ts, 0)
|
||||
.single()
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Invalid timestamp: {}", end_date_ts).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.expires_at(Some(end_date));
|
||||
@ -524,7 +597,10 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn add_tag(listing: &mut RhaiListing, tag: String) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn add_tag(
|
||||
listing: &mut RhaiListing,
|
||||
tag: String,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let mut lst = mem::take(listing);
|
||||
lst = lst.add_tag(tag);
|
||||
*listing = lst;
|
||||
@ -532,26 +608,32 @@ mod listing_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn add_bid(listing: &mut RhaiListing, bid: RhaiBid) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn add_bid(
|
||||
listing: &mut RhaiListing,
|
||||
bid: RhaiBid,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let lst = mem::take(listing);
|
||||
match lst.clone().add_bid(bid) {
|
||||
Ok(updated_lst) => {
|
||||
*listing = updated_lst;
|
||||
Ok(listing.clone())
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
// Put back the original listing since the add_bid failed
|
||||
*listing = lst;
|
||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to add bid: {}", err).into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn complete_sale(listing: &mut RhaiListing, buyer_id: INT) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
pub fn complete_sale(
|
||||
listing: &mut RhaiListing,
|
||||
buyer_id: INT,
|
||||
) -> Result<RhaiListing, Box<EvalAltResult>> {
|
||||
let buyer_id = id_from_i64_to_u32(buyer_id)?;
|
||||
let lst = mem::take(listing);
|
||||
|
||||
@ -563,13 +645,13 @@ mod listing_module {
|
||||
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
|
||||
Position::NONE,
|
||||
)))
|
||||
}
|
||||
}
|
||||
@ -582,13 +664,13 @@ mod listing_module {
|
||||
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
|
||||
Position::NONE,
|
||||
)))
|
||||
}
|
||||
}
|
||||
@ -647,7 +729,10 @@ mod bid_module {
|
||||
|
||||
// Setters using builder pattern with proper mutability handling
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn listing_id(bid: &mut RhaiBid, listing_id: String) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
pub fn listing_id(
|
||||
bid: &mut RhaiBid,
|
||||
listing_id: String,
|
||||
) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
let mut b = mem::take(bid);
|
||||
b = b.listing_id(listing_id);
|
||||
*bid = b;
|
||||
@ -680,7 +765,10 @@ mod bid_module {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw, global)]
|
||||
pub fn update_status(bid: &mut RhaiBid, status_str: String) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
pub fn update_status(
|
||||
bid: &mut RhaiBid,
|
||||
status_str: String,
|
||||
) -> Result<RhaiBid, Box<EvalAltResult>> {
|
||||
let status = string_to_bid_status(&status_str)?;
|
||||
let mut b = mem::take(bid);
|
||||
b = b.status(status);
|
||||
@ -706,13 +794,21 @@ pub fn register_finance_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
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("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("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("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("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) ---
|
||||
@ -720,63 +816,153 @@ pub fn register_finance_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
|
||||
// Account DB functions
|
||||
let db_set_account = Arc::clone(&db);
|
||||
db_module.set_native_fn("set_account", move |account: Account| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_account.collection::<Account>().map_err(|e|
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Account collection: {:?}", e).into(), Position::NONE)) )?;
|
||||
collection.set(&account)
|
||||
db_module.set_native_fn(
|
||||
"set_account",
|
||||
move |account: Account| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_account.collection::<Account>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Account collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.set(&account)
|
||||
.map(|(id, _)| id as INT)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to save Account: {:?}", e).into(), Position::NONE)))
|
||||
});
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save Account: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_get_account = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_account_by_id", move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_account_by_id",
|
||||
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_rhai)?;
|
||||
let collection = db_get_account.collection::<Account>().map_err(|e|
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Account collection: {:?}", e).into(), Position::NONE)) )?;
|
||||
collection.get_by_id(id_u32)
|
||||
.map(|opt_account| opt_account.map(Dynamic::from).unwrap_or_else(|| Dynamic::UNIT))
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Account with ID {}: {:?}", id_rhai, e).into(), Position::NONE)))
|
||||
});
|
||||
let collection = db_get_account.collection::<Account>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Account collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.get_by_id(id_u32)
|
||||
.map(|opt_account| {
|
||||
opt_account
|
||||
.map(Dynamic::from)
|
||||
.unwrap_or_else(|| Dynamic::UNIT)
|
||||
})
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Account with ID {}: {:?}", id_rhai, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Asset DB functions
|
||||
let db_set_asset = Arc::clone(&db);
|
||||
db_module.set_native_fn("set_asset", move |asset: Asset| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_asset.collection::<Asset>().map_err(|e|
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Asset collection: {:?}", e).into(), Position::NONE)) )?;
|
||||
collection.set(&asset)
|
||||
db_module.set_native_fn(
|
||||
"set_asset",
|
||||
move |asset: Asset| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_asset.collection::<Asset>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Asset collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.set(&asset)
|
||||
.map(|(id, _)| id as INT)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to save Asset: {:?}", e).into(), Position::NONE)))
|
||||
});
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save Asset: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_get_asset = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_asset_by_id", move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_asset_by_id",
|
||||
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_rhai)?;
|
||||
let collection = db_get_asset.collection::<Asset>().map_err(|e|
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Asset collection: {:?}", e).into(), Position::NONE)) )?;
|
||||
collection.get_by_id(id_u32)
|
||||
.map(|opt_asset| opt_asset.map(Dynamic::from).unwrap_or_else(|| Dynamic::UNIT))
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Asset with ID {}: {:?}", id_rhai, e).into(), Position::NONE)))
|
||||
});
|
||||
let collection = db_get_asset.collection::<Asset>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Asset collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.get_by_id(id_u32)
|
||||
.map(|opt_asset| {
|
||||
opt_asset
|
||||
.map(Dynamic::from)
|
||||
.unwrap_or_else(|| Dynamic::UNIT)
|
||||
})
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Asset with ID {}: {:?}", id_rhai, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Listing DB functions
|
||||
let db_set_listing = Arc::clone(&db);
|
||||
db_module.set_native_fn("set_listing", move |listing: Listing| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_listing.collection::<Listing>().map_err(|e|
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Listing collection: {:?}", e).into(), Position::NONE)) )?;
|
||||
collection.set(&listing)
|
||||
db_module.set_native_fn(
|
||||
"set_listing",
|
||||
move |listing: Listing| -> Result<INT, Box<EvalAltResult>> {
|
||||
let collection = db_set_listing.collection::<Listing>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Listing collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.set(&listing)
|
||||
.map(|(id, _)| id as INT)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to save Listing: {:?}", e).into(), Position::NONE)))
|
||||
});
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to save Listing: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_get_listing = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_listing_by_id", move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_listing_by_id",
|
||||
move |id_rhai: INT| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_rhai)?;
|
||||
let collection = db_get_listing.collection::<Listing>().map_err(|e|
|
||||
Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Listing collection: {:?}", e).into(), Position::NONE)) )?;
|
||||
collection.get_by_id(id_u32)
|
||||
.map(|opt_listing| opt_listing.map(Dynamic::from).unwrap_or_else(|| Dynamic::UNIT))
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("Failed to get Listing with ID {}: {:?}", id_rhai, e).into(), Position::NONE)))
|
||||
});
|
||||
let collection = db_get_listing.collection::<Listing>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Listing collection: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection
|
||||
.get_by_id(id_u32)
|
||||
.map(|opt_listing| {
|
||||
opt_listing
|
||||
.map(Dynamic::from)
|
||||
.unwrap_or_else(|| Dynamic::UNIT)
|
||||
})
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get Listing with ID {}: {:?}", id_rhai, e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_global_module(db_module.into());
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::flow_step::FlowStep;
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::flow_step::FlowStep;
|
||||
|
||||
/// Represents a signing flow.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default)]
|
||||
|
@ -1,11 +1,11 @@
|
||||
// Export flow model submodules
|
||||
pub mod flow;
|
||||
pub mod flow_step;
|
||||
pub mod signature_requirement;
|
||||
pub mod rhai;
|
||||
pub mod signature_requirement;
|
||||
|
||||
// Re-export key types for convenience
|
||||
pub use flow::Flow;
|
||||
pub use flow_step::FlowStep;
|
||||
pub use signature_requirement::SignatureRequirement;
|
||||
pub use rhai::register_flow_rhai_module;
|
||||
pub use signature_requirement::SignatureRequirement;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rhai::plugin::*;
|
||||
use rhai::{Engine, EvalAltResult, Position, Module, INT, Dynamic, Array};
|
||||
use std::sync::Arc;
|
||||
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;
|
||||
@ -9,18 +9,18 @@ use super::signature_requirement::SignatureRequirement;
|
||||
type RhaiFlow = Flow;
|
||||
type RhaiFlowStep = FlowStep;
|
||||
type RhaiSignatureRequirement = SignatureRequirement;
|
||||
use crate::db::hero::OurDB;
|
||||
use crate::db::Collection;
|
||||
use crate::db::Db;
|
||||
use crate::db::hero::OurDB;
|
||||
|
||||
// Helper to convert i64 from Rhai to u32 for IDs
|
||||
fn id_from_i64_to_u32(id_i64: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
u32::try_from(id_i64).map_err(|_|
|
||||
u32::try_from(id_i64).map_err(|_| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Failed to convert ID '{}' to u32", id_i64).into(),
|
||||
Position::NONE
|
||||
Position::NONE,
|
||||
))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
@ -41,7 +41,10 @@ mod rhai_flow_module {
|
||||
|
||||
/// Sets the flow status
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn flow_status(flow: &mut RhaiFlow, status: String) -> Result<RhaiFlow, Box<EvalAltResult>> {
|
||||
pub fn flow_status(
|
||||
flow: &mut RhaiFlow,
|
||||
status: String,
|
||||
) -> Result<RhaiFlow, Box<EvalAltResult>> {
|
||||
let owned_flow = mem::replace(flow, Flow::new("")); // Dummy for replacement
|
||||
*flow = owned_flow.status(status);
|
||||
Ok(flow.clone())
|
||||
@ -49,7 +52,10 @@ mod rhai_flow_module {
|
||||
|
||||
/// Adds a step to the flow
|
||||
#[rhai_fn(name = "add_step", return_raw, global, pure)]
|
||||
pub fn flow_add_step(flow: &mut RhaiFlow, step: RhaiFlowStep) -> Result<RhaiFlow, Box<EvalAltResult>> {
|
||||
pub fn flow_add_step(
|
||||
flow: &mut RhaiFlow,
|
||||
step: RhaiFlowStep,
|
||||
) -> Result<RhaiFlow, Box<EvalAltResult>> {
|
||||
let owned_flow = mem::replace(flow, Flow::new("")); // Dummy for replacement
|
||||
*flow = owned_flow.add_step(step);
|
||||
Ok(flow.clone())
|
||||
@ -57,20 +63,36 @@ mod rhai_flow_module {
|
||||
|
||||
// Flow Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_id(flow: &mut RhaiFlow) -> i64 { flow.base_data.id as i64 }
|
||||
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 }
|
||||
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 }
|
||||
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() }
|
||||
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() }
|
||||
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() }
|
||||
pub fn get_status(flow: &mut RhaiFlow) -> String {
|
||||
flow.status.clone()
|
||||
}
|
||||
#[rhai_fn(get = "steps", pure)]
|
||||
pub fn get_steps(flow: &mut RhaiFlow) -> Array {
|
||||
flow.steps.iter().cloned().map(Dynamic::from).collect::<Array>()
|
||||
flow.steps
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Dynamic::from)
|
||||
.collect::<Array>()
|
||||
}
|
||||
|
||||
// --- FlowStep Functions ---
|
||||
@ -81,14 +103,17 @@ mod rhai_flow_module {
|
||||
let mut flow_step = FlowStep::default();
|
||||
flow_step.step_order = step_order;
|
||||
Dynamic::from(flow_step)
|
||||
},
|
||||
Err(err) => Dynamic::from(err.to_string())
|
||||
}
|
||||
Err(err) => Dynamic::from(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the flow step description
|
||||
#[rhai_fn(name = "description", return_raw, global, pure)]
|
||||
pub fn flow_step_description(step: &mut RhaiFlowStep, description: String) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
|
||||
pub fn flow_step_description(
|
||||
step: &mut RhaiFlowStep,
|
||||
description: String,
|
||||
) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
|
||||
let owned_step = mem::replace(step, FlowStep::default()); // Use Default trait
|
||||
*step = owned_step.description(description);
|
||||
Ok(step.clone())
|
||||
@ -96,7 +121,10 @@ mod rhai_flow_module {
|
||||
|
||||
/// Sets the flow step status
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn flow_step_status(step: &mut RhaiFlowStep, status: String) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
|
||||
pub fn flow_step_status(
|
||||
step: &mut RhaiFlowStep,
|
||||
status: String,
|
||||
) -> Result<RhaiFlowStep, Box<EvalAltResult>> {
|
||||
let owned_step = mem::replace(step, FlowStep::default()); // Use Default trait
|
||||
*step = owned_step.status(status);
|
||||
Ok(step.clone())
|
||||
@ -104,11 +132,17 @@ mod rhai_flow_module {
|
||||
|
||||
// FlowStep Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_step_id(step: &mut RhaiFlowStep) -> i64 { step.base_data.id as i64 }
|
||||
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 }
|
||||
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 }
|
||||
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 {
|
||||
@ -117,14 +151,22 @@ mod rhai_flow_module {
|
||||
}
|
||||
}
|
||||
#[rhai_fn(get = "step_order", pure)]
|
||||
pub fn get_step_order(step: &mut RhaiFlowStep) -> i64 { step.step_order as i64 }
|
||||
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() }
|
||||
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 {
|
||||
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();
|
||||
@ -132,14 +174,17 @@ mod rhai_flow_module {
|
||||
signature_requirement.public_key = public_key;
|
||||
signature_requirement.message = message;
|
||||
Dynamic::from(signature_requirement)
|
||||
},
|
||||
Err(err) => Dynamic::from(err.to_string())
|
||||
}
|
||||
Err(err) => Dynamic::from(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the signed_by field
|
||||
#[rhai_fn(name = "signed_by", return_raw, global, pure)]
|
||||
pub fn signature_requirement_signed_by(sr: &mut RhaiSignatureRequirement, signed_by: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
pub fn signature_requirement_signed_by(
|
||||
sr: &mut RhaiSignatureRequirement,
|
||||
signed_by: String,
|
||||
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
|
||||
*sr = owned_sr.signed_by(signed_by);
|
||||
Ok(sr.clone())
|
||||
@ -147,7 +192,10 @@ mod rhai_flow_module {
|
||||
|
||||
/// Sets the signature field
|
||||
#[rhai_fn(name = "signature", return_raw, global, pure)]
|
||||
pub fn signature_requirement_signature(sr: &mut RhaiSignatureRequirement, signature: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
pub fn signature_requirement_signature(
|
||||
sr: &mut RhaiSignatureRequirement,
|
||||
signature: String,
|
||||
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
|
||||
*sr = owned_sr.signature(signature);
|
||||
Ok(sr.clone())
|
||||
@ -155,7 +203,10 @@ mod rhai_flow_module {
|
||||
|
||||
/// Sets the status field
|
||||
#[rhai_fn(name = "status", return_raw, global, pure)]
|
||||
pub fn signature_requirement_status(sr: &mut RhaiSignatureRequirement, status: String) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
pub fn signature_requirement_status(
|
||||
sr: &mut RhaiSignatureRequirement,
|
||||
status: String,
|
||||
) -> Result<RhaiSignatureRequirement, Box<EvalAltResult>> {
|
||||
let owned_sr = mem::replace(sr, SignatureRequirement::default()); // Use Default trait
|
||||
*sr = owned_sr.status(status);
|
||||
Ok(sr.clone())
|
||||
@ -163,17 +214,29 @@ mod rhai_flow_module {
|
||||
|
||||
// SignatureRequirement Getters
|
||||
#[rhai_fn(get = "id", pure)]
|
||||
pub fn get_sr_id(sr: &mut RhaiSignatureRequirement) -> i64 { sr.base_data.id as i64 }
|
||||
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 }
|
||||
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 }
|
||||
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 }
|
||||
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() }
|
||||
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() }
|
||||
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 {
|
||||
@ -189,7 +252,9 @@ mod rhai_flow_module {
|
||||
}
|
||||
}
|
||||
#[rhai_fn(get = "status", pure)]
|
||||
pub fn get_sr_status(sr: &mut RhaiSignatureRequirement) -> String { sr.status.clone() }
|
||||
pub fn get_sr_status(sr: &mut RhaiSignatureRequirement) -> String {
|
||||
sr.status.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the flow module with the Rhai engine
|
||||
@ -199,168 +264,265 @@ pub fn register_flow_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
|
||||
// Flow database functions
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("save_flow", move |flow: Flow| -> Result<Flow, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_flow",
|
||||
move |flow: Flow| -> Result<Flow, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone.set(&flow)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error save_flow: {:?}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone.set(&flow).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error save_flow: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated flow with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_flow_by_id", move |id_i64: INT| -> Result<Flow, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_flow_by_id",
|
||||
move |id_i64: INT| -> Result<Flow, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_flow_by_id: {:?}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("Flow with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_flow_by_id: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Flow with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("delete_flow", move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_flow",
|
||||
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
let collection = db_clone.collection::<Flow>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
let collection = db_clone.collection::<Flow>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get flow collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
collection.delete_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
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
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("list_flows", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<Flow>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_flows",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<Flow>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get flow collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let flows = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let flows = collection.get_all().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get all flows: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for flow in flows {
|
||||
array.push(Dynamic::from(flow));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// FlowStep database functions
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("save_flow_step", move |step: FlowStep| -> Result<FlowStep, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_flow_step",
|
||||
move |step: FlowStep| -> Result<FlowStep, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone.set(&step)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error save_flow_step: {:?}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone.set(&step).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error save_flow_step: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated flow step with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_flow_step_by_id", move |id_i64: INT| -> Result<FlowStep, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_flow_step_by_id",
|
||||
move |id_i64: INT| -> Result<FlowStep, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_flow_step_by_id: {:?}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("FlowStep with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_flow_step_by_id: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("FlowStep with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("delete_flow_step", move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_flow_step",
|
||||
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
let collection = db_clone.collection::<FlowStep>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
let collection = db_clone.collection::<FlowStep>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get flow step collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
collection.delete_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
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
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("list_flow_steps", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<FlowStep>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_flow_steps",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<FlowStep>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get flow step collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let steps = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
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
|
||||
)))?;
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
let mut array = Array::new();
|
||||
for step in steps {
|
||||
array.push(Dynamic::from(step));
|
||||
}
|
||||
Ok(Dynamic::from(array))
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// SignatureRequirement database functions
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("save_signature_requirement", move |sr: SignatureRequirement| -> Result<SignatureRequirement, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"save_signature_requirement",
|
||||
move |sr: SignatureRequirement| -> Result<SignatureRequirement, Box<EvalAltResult>> {
|
||||
// Use the Collection trait method directly
|
||||
let result = db_clone.set(&sr)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error save_signature_requirement: {:?}", e).into(), Position::NONE)))?;
|
||||
let result = db_clone.set(&sr).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error save_signature_requirement: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the updated signature requirement with the correct ID
|
||||
Ok(result.1)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("get_signature_requirement_by_id", move |id_i64: INT| -> Result<SignatureRequirement, Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"get_signature_requirement_by_id",
|
||||
move |id_i64: INT| -> Result<SignatureRequirement, Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
db_clone.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(format!("DB Error get_signature_requirement_by_id: {:?}", e).into(), Position::NONE)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorRuntime(format!("SignatureRequirement with ID {} not found", id_u32).into(), Position::NONE)))
|
||||
});
|
||||
db_clone
|
||||
.get_by_id(id_u32)
|
||||
.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("DB Error get_signature_requirement_by_id: {:?}", e).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("SignatureRequirement with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("delete_signature_requirement", move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
db_module.set_native_fn(
|
||||
"delete_signature_requirement",
|
||||
move |id_i64: INT| -> Result<(), Box<EvalAltResult>> {
|
||||
let id_u32 = id_from_i64_to_u32(id_i64)?;
|
||||
// Use the Collection trait method directly
|
||||
let collection = db_clone.collection::<SignatureRequirement>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
let collection = db_clone.collection::<SignatureRequirement>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get signature requirement collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
collection.delete_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to delete SignatureRequirement (ID: {}): {:?}", id_u32, e).into(),
|
||||
Position::NONE
|
||||
)))
|
||||
});
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
collection.delete_by_id(id_u32).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!(
|
||||
"Failed to delete SignatureRequirement (ID: {}): {:?}",
|
||||
id_u32, e
|
||||
)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone = Arc::clone(&db);
|
||||
db_module.set_native_fn("list_signature_requirements", move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<SignatureRequirement>()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
db_module.set_native_fn(
|
||||
"list_signature_requirements",
|
||||
move || -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let collection = db_clone.collection::<SignatureRequirement>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get signature requirement collection: {:?}", e).into(),
|
||||
Position::NONE
|
||||
)))?;
|
||||
let srs = collection.get_all()
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
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
|
||||
)))?;
|
||||
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());
|
||||
|
@ -32,7 +32,12 @@ pub struct SignatureRequirement {
|
||||
|
||||
impl SignatureRequirement {
|
||||
/// Create a new signature requirement.
|
||||
pub fn new(_id: u32, flow_step_id: u32, public_key: impl ToString, message: impl ToString) -> Self {
|
||||
pub fn new(
|
||||
_id: u32,
|
||||
flow_step_id: u32,
|
||||
public_key: impl ToString,
|
||||
message: impl ToString,
|
||||
) -> Self {
|
||||
Self {
|
||||
base_data: BaseModelData::new(),
|
||||
flow_step_id,
|
||||
|
@ -4,5 +4,5 @@ pub mod proposal;
|
||||
|
||||
pub mod attached_file;
|
||||
|
||||
pub use self::proposal::{Proposal, Ballot, VoteOption, ProposalStatus, VoteEventStatus};
|
||||
pub use self::attached_file::AttachedFile;
|
||||
pub use self::proposal::{Ballot, Proposal, ProposalStatus, VoteEventStatus, VoteOption};
|
||||
|
@ -5,9 +5,9 @@ use heromodels_derive::model; // For #[model]
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use heromodels_core::BaseModelData;
|
||||
use crate::models::core::Comment;
|
||||
use super::AttachedFile;
|
||||
use crate::models::core::Comment;
|
||||
use heromodels_core::BaseModelData;
|
||||
|
||||
// --- Enums ---
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use std::fmt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
// --- Enums ---
|
||||
|
||||
|
@ -1,51 +1,60 @@
|
||||
use rhai::{
|
||||
Dynamic, Engine, EvalAltResult, NativeCallContext, Position, Module, Array,
|
||||
};
|
||||
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::models::legal::{
|
||||
Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus,
|
||||
};
|
||||
|
||||
use crate::db::Collection; // Import the Collection trait
|
||||
|
||||
// --- Helper Functions for ID and Timestamp Conversion ---
|
||||
fn i64_to_u32(val: i64, context_pos: Position, field_name: &str, object_name: &str) -> Result<u32, Box<EvalAltResult>> {
|
||||
fn i64_to_u32(
|
||||
val: i64,
|
||||
context_pos: Position,
|
||||
field_name: &str,
|
||||
object_name: &str,
|
||||
) -> Result<u32, Box<EvalAltResult>> {
|
||||
val.try_into().map_err(|_e| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to u32",
|
||||
field_name,
|
||||
object_name,
|
||||
val
|
||||
field_name, object_name, val
|
||||
),
|
||||
context_pos,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn i64_to_u64(val: i64, context_pos: Position, field_name: &str, object_name: &str) -> Result<u64, Box<EvalAltResult>> {
|
||||
fn i64_to_u64(
|
||||
val: i64,
|
||||
context_pos: Position,
|
||||
field_name: &str,
|
||||
object_name: &str,
|
||||
) -> Result<u64, Box<EvalAltResult>> {
|
||||
val.try_into().map_err(|_e| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to u64",
|
||||
field_name,
|
||||
object_name,
|
||||
val
|
||||
field_name, object_name, val
|
||||
),
|
||||
context_pos,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn i64_to_i32(val: i64, context_pos: Position, field_name: &str, object_name: &str) -> Result<i32, Box<EvalAltResult>> {
|
||||
fn i64_to_i32(
|
||||
val: i64,
|
||||
context_pos: Position,
|
||||
field_name: &str,
|
||||
object_name: &str,
|
||||
) -> Result<i32, Box<EvalAltResult>> {
|
||||
val.try_into().map_err(|_e| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"Conversion error for field '{}' in object '{}': cannot convert i64 ({}) to i32",
|
||||
field_name,
|
||||
object_name,
|
||||
val
|
||||
field_name, object_name, val
|
||||
),
|
||||
context_pos,
|
||||
))
|
||||
@ -77,23 +86,69 @@ pub fn register_legal_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
engine.register_type_with_name::<ContractRevision>("ContractRevision");
|
||||
engine.register_fn(
|
||||
"new_contract_revision",
|
||||
move |context: NativeCallContext, version_i64: i64, content: String, created_at_i64: i64, created_by: String| -> Result<ContractRevision, Box<EvalAltResult>> {
|
||||
let version = i64_to_u32(version_i64, context.position(), "version", "new_contract_revision")?;
|
||||
let created_at = i64_to_u64(created_at_i64, context.position(), "created_at", "new_contract_revision")?;
|
||||
Ok(ContractRevision::new(version, content, created_at, created_by))
|
||||
}
|
||||
move |context: NativeCallContext,
|
||||
version_i64: i64,
|
||||
content: String,
|
||||
created_at_i64: i64,
|
||||
created_by: String|
|
||||
-> Result<ContractRevision, Box<EvalAltResult>> {
|
||||
let version = i64_to_u32(
|
||||
version_i64,
|
||||
context.call_position(),
|
||||
"version",
|
||||
"new_contract_revision",
|
||||
)?;
|
||||
let created_at = i64_to_u64(
|
||||
created_at_i64,
|
||||
context.call_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 {
|
||||
engine.register_fn(
|
||||
"comments",
|
||||
|mut revision: ContractRevision, comments: String| -> ContractRevision {
|
||||
revision.comments = Some(comments);
|
||||
revision
|
||||
});
|
||||
engine.register_get("version", |revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> { Ok(revision.version as i64) });
|
||||
engine.register_get("content", |revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> { Ok(revision.content.clone()) });
|
||||
engine.register_get("created_at", |revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> { Ok(revision.created_at as i64) });
|
||||
engine.register_get("created_by", |revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> { Ok(revision.created_by.clone()) });
|
||||
engine.register_get("comments", |revision: &mut ContractRevision| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(revision.comments.clone().map_or(Dynamic::UNIT, Dynamic::from))
|
||||
});
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"version",
|
||||
|revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(revision.version as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"content",
|
||||
|revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(revision.content.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_at",
|
||||
|revision: &mut ContractRevision| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(revision.created_at as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_by",
|
||||
|revision: &mut ContractRevision| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(revision.created_by.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"comments",
|
||||
|revision: &mut ContractRevision| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(revision
|
||||
.comments
|
||||
.clone()
|
||||
.map_or(Dynamic::UNIT, Dynamic::from))
|
||||
},
|
||||
);
|
||||
|
||||
// --- ContractSigner ---
|
||||
engine.register_type_with_name::<ContractSigner>("ContractSigner");
|
||||
@ -101,165 +156,458 @@ pub fn register_legal_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
"new_contract_signer",
|
||||
|id: String, name: String, email: String| -> ContractSigner {
|
||||
ContractSigner::new(id, name, email)
|
||||
}
|
||||
},
|
||||
);
|
||||
engine.register_fn("status", |signer: ContractSigner, status: SignerStatus| -> ContractSigner { signer.status(status) });
|
||||
engine.register_fn("signed_at", |context: NativeCallContext, signer: ContractSigner, signed_at_i64: i64| -> Result<ContractSigner, Box<EvalAltResult>> {
|
||||
let signed_at_u64 = i64_to_u64(signed_at_i64, context.position(), "signed_at", "ContractSigner.signed_at")?;
|
||||
engine.register_fn(
|
||||
"status",
|
||||
|signer: ContractSigner, status: SignerStatus| -> ContractSigner { signer.status(status) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"signed_at",
|
||||
|context: NativeCallContext,
|
||||
signer: ContractSigner,
|
||||
signed_at_i64: i64|
|
||||
-> Result<ContractSigner, Box<EvalAltResult>> {
|
||||
let signed_at_u64 = i64_to_u64(
|
||||
signed_at_i64,
|
||||
context.call_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_fn(
|
||||
"clear_signed_at",
|
||||
|signer: ContractSigner| -> ContractSigner { signer.clear_signed_at() },
|
||||
);
|
||||
engine.register_fn(
|
||||
"comments",
|
||||
|signer: ContractSigner, comments: String| -> ContractSigner { signer.comments(comments) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"clear_comments",
|
||||
|signer: ContractSigner| -> ContractSigner { signer.clear_comments() },
|
||||
);
|
||||
|
||||
engine.register_get("id", |signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> { Ok(signer.id.clone()) });
|
||||
engine.register_get("name", |signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> { Ok(signer.name.clone()) });
|
||||
engine.register_get("email", |signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> { Ok(signer.email.clone()) });
|
||||
engine.register_get("status", |signer: &mut ContractSigner| -> Result<SignerStatus, Box<EvalAltResult>> { Ok(signer.status.clone()) });
|
||||
engine.register_get("signed_at_ts", |signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(signer.signed_at.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
});
|
||||
engine.register_get("comments", |signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
engine.register_get(
|
||||
"id",
|
||||
|signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(signer.id.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"name",
|
||||
|signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(signer.name.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"email",
|
||||
|signer: &mut ContractSigner| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(signer.email.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"status",
|
||||
|signer: &mut ContractSigner| -> Result<SignerStatus, Box<EvalAltResult>> {
|
||||
Ok(signer.status.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"signed_at_ts",
|
||||
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(signer
|
||||
.signed_at
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"comments",
|
||||
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(signer.comments.clone().map_or(Dynamic::UNIT, Dynamic::from))
|
||||
});
|
||||
engine.register_get("signed_at", |signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(signer.signed_at.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts)))
|
||||
});
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"signed_at",
|
||||
|signer: &mut ContractSigner| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(signer
|
||||
.signed_at
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts)))
|
||||
},
|
||||
);
|
||||
|
||||
// --- Contract ---
|
||||
engine.register_type_with_name::<Contract>("Contract");
|
||||
engine.register_fn(
|
||||
"new_contract",
|
||||
move |context: NativeCallContext, base_id_i64: i64, contract_id: String| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let base_id = i64_to_u32(base_id_i64, context.position(), "base_id", "new_contract")?;
|
||||
move |context: NativeCallContext,
|
||||
base_id_i64: i64,
|
||||
contract_id: String|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let base_id = i64_to_u32(
|
||||
base_id_i64,
|
||||
context.call_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("title", |contract: Contract, title: String| -> Contract {
|
||||
contract.title(title)
|
||||
});
|
||||
engine.register_fn(
|
||||
"description",
|
||||
|contract: Contract, description: String| -> Contract { contract.description(description) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"contract_type",
|
||||
|contract: Contract, contract_type: String| -> Contract {
|
||||
contract.contract_type(contract_type)
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"status",
|
||||
|contract: Contract, status: ContractStatus| -> Contract { contract.status(status) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"created_by",
|
||||
|contract: Contract, created_by: String| -> Contract { contract.created_by(created_by) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"terms_and_conditions",
|
||||
|contract: Contract, terms: String| -> Contract { contract.terms_and_conditions(terms) },
|
||||
);
|
||||
|
||||
engine.register_fn("start_date", |context: NativeCallContext, contract: Contract, start_date_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let start_date_u64 = i64_to_u64(start_date_i64, context.position(), "start_date", "Contract.start_date")?;
|
||||
engine.register_fn(
|
||||
"start_date",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
start_date_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let start_date_u64 = i64_to_u64(
|
||||
start_date_i64,
|
||||
context.call_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("clear_start_date", |contract: Contract| -> Contract { contract.clear_start_date() });
|
||||
|
||||
engine.register_fn("end_date", |context: NativeCallContext, contract: Contract, end_date_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let end_date_u64 = i64_to_u64(end_date_i64, context.position(), "end_date", "Contract.end_date")?;
|
||||
engine.register_fn(
|
||||
"end_date",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
end_date_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let end_date_u64 = i64_to_u64(
|
||||
end_date_i64,
|
||||
context.call_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("clear_end_date", |contract: Contract| -> Contract { contract.clear_end_date() });
|
||||
|
||||
engine.register_fn("renewal_period_days", |context: NativeCallContext, contract: Contract, days_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let days_i32 = i64_to_i32(days_i64, context.position(), "renewal_period_days", "Contract.renewal_period_days")?;
|
||||
engine.register_fn(
|
||||
"renewal_period_days",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
days_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let days_i32 = i64_to_i32(
|
||||
days_i64,
|
||||
context.call_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(
|
||||
"clear_renewal_period_days",
|
||||
|contract: Contract| -> Contract { contract.clear_renewal_period_days() },
|
||||
);
|
||||
|
||||
engine.register_fn("next_renewal_date", |context: NativeCallContext, contract: Contract, date_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let date_u64 = i64_to_u64(date_i64, context.position(), "next_renewal_date", "Contract.next_renewal_date")?;
|
||||
engine.register_fn(
|
||||
"next_renewal_date",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
date_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let date_u64 = i64_to_u64(
|
||||
date_i64,
|
||||
context.call_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(
|
||||
"clear_next_renewal_date",
|
||||
|contract: Contract| -> Contract { contract.clear_next_renewal_date() },
|
||||
);
|
||||
|
||||
engine.register_fn("add_signer", |contract: Contract, signer: ContractSigner| -> Contract { contract.add_signer(signer) });
|
||||
engine.register_fn("signers", |contract: Contract, signers_array: Array| -> Contract {
|
||||
let signers_vec = signers_array.into_iter().filter_map(|s| s.try_cast::<ContractSigner>()).collect();
|
||||
engine.register_fn(
|
||||
"add_signer",
|
||||
|contract: Contract, signer: ContractSigner| -> Contract { contract.add_signer(signer) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"signers",
|
||||
|contract: Contract, signers_array: Array| -> Contract {
|
||||
let signers_vec = signers_array
|
||||
.into_iter()
|
||||
.filter_map(|s| s.try_cast::<ContractSigner>())
|
||||
.collect();
|
||||
contract.signers(signers_vec)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("add_revision", |contract: Contract, revision: ContractRevision| -> Contract { contract.add_revision(revision) });
|
||||
engine.register_fn("revisions", |contract: Contract, revisions_array: Array| -> Contract {
|
||||
let revisions_vec = revisions_array.into_iter().filter_map(|r| r.try_cast::<ContractRevision>()).collect();
|
||||
engine.register_fn(
|
||||
"add_revision",
|
||||
|contract: Contract, revision: ContractRevision| -> Contract {
|
||||
contract.add_revision(revision)
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"revisions",
|
||||
|contract: Contract, revisions_array: Array| -> Contract {
|
||||
let revisions_vec = revisions_array
|
||||
.into_iter()
|
||||
.filter_map(|r| r.try_cast::<ContractRevision>())
|
||||
.collect();
|
||||
contract.revisions(revisions_vec)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("current_version", |context: NativeCallContext, contract: Contract, version_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let version_u32 = i64_to_u32(version_i64, context.position(), "current_version", "Contract.current_version")?;
|
||||
engine.register_fn(
|
||||
"current_version",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
version_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let version_u32 = i64_to_u32(
|
||||
version_i64,
|
||||
context.call_position(),
|
||||
"current_version",
|
||||
"Contract.current_version",
|
||||
)?;
|
||||
Ok(contract.current_version(version_u32))
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("last_signed_date", |context: NativeCallContext, contract: Contract, date_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let date_u64 = i64_to_u64(date_i64, context.position(), "last_signed_date", "Contract.last_signed_date")?;
|
||||
engine.register_fn(
|
||||
"last_signed_date",
|
||||
|context: NativeCallContext,
|
||||
contract: Contract,
|
||||
date_i64: i64|
|
||||
-> Result<Contract, Box<EvalAltResult>> {
|
||||
let date_u64 = i64_to_u64(
|
||||
date_i64,
|
||||
context.call_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()
|
||||
});
|
||||
engine.register_fn("clear_last_signed_date", |contract: Contract| -> Contract { contract.clear_last_signed_date() });
|
||||
|
||||
// Getters for Contract
|
||||
engine.register_get("id", |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> { Ok(contract.base_data.id as i64) });
|
||||
engine.register_get("created_at_ts", |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> { Ok(contract.base_data.created_at as i64) });
|
||||
engine.register_get("updated_at_ts", |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> { Ok(contract.base_data.modified_at as i64) });
|
||||
engine.register_get("contract_id", |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> { Ok(contract.contract_id.clone()) });
|
||||
engine.register_get("title", |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> { Ok(contract.title.clone()) });
|
||||
engine.register_get("description", |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> { Ok(contract.description.clone()) });
|
||||
engine.register_get("contract_type", |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> { Ok(contract.contract_type.clone()) });
|
||||
engine.register_get("status", |contract: &mut Contract| -> Result<ContractStatus, Box<EvalAltResult>> { Ok(contract.status.clone()) });
|
||||
engine.register_get("created_by", |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> { Ok(contract.created_by.clone()) });
|
||||
engine.register_get("terms_and_conditions", |contract: &mut Contract| -> Result<String, Box<EvalAltResult>> { Ok(contract.terms_and_conditions.clone()) });
|
||||
engine.register_get(
|
||||
"id",
|
||||
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(contract.base_data.id as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_at_ts",
|
||||
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(contract.base_data.created_at as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"updated_at_ts",
|
||||
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(contract.base_data.modified_at as i64)
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"contract_id",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.contract_id.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"title",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.title.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"description",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.description.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"contract_type",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.contract_type.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"status",
|
||||
|contract: &mut Contract| -> Result<ContractStatus, Box<EvalAltResult>> {
|
||||
Ok(contract.status.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_by",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.created_by.clone())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"terms_and_conditions",
|
||||
|contract: &mut Contract| -> Result<String, Box<EvalAltResult>> {
|
||||
Ok(contract.terms_and_conditions.clone())
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_get("start_date", |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract.start_date.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
});
|
||||
engine.register_get("end_date", |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract.end_date.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
});
|
||||
engine.register_get("renewal_period_days", |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract.renewal_period_days.map_or(Dynamic::UNIT, |days| Dynamic::from(days as i64)))
|
||||
});
|
||||
engine.register_get("next_renewal_date", |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract.next_renewal_date.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
});
|
||||
engine.register_get("last_signed_date", |contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract.last_signed_date.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
});
|
||||
engine.register_get(
|
||||
"start_date",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.start_date
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"end_date",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.end_date
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"renewal_period_days",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.renewal_period_days
|
||||
.map_or(Dynamic::UNIT, |days| Dynamic::from(days as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"next_renewal_date",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.next_renewal_date
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"last_signed_date",
|
||||
|contract: &mut Contract| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok(contract
|
||||
.last_signed_date
|
||||
.map_or(Dynamic::UNIT, |ts| Dynamic::from(ts as i64)))
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_get("current_version", |contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> { Ok(contract.current_version as i64) });
|
||||
engine.register_get(
|
||||
"current_version",
|
||||
|contract: &mut Contract| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(contract.current_version as i64)
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_get("signers", |contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
|
||||
let rhai_array = contract.signers.iter().cloned().map(Dynamic::from).collect::<Array>();
|
||||
engine.register_get(
|
||||
"signers",
|
||||
|contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
|
||||
let rhai_array = contract
|
||||
.signers
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Dynamic::from)
|
||||
.collect::<Array>();
|
||||
Ok(rhai_array)
|
||||
});
|
||||
engine.register_get("revisions", |contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
|
||||
let rhai_array = contract.revisions.iter().cloned().map(Dynamic::from).collect::<Array>();
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"revisions",
|
||||
|contract: &mut Contract| -> Result<Array, Box<EvalAltResult>> {
|
||||
let rhai_array = contract
|
||||
.revisions
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Dynamic::from)
|
||||
.collect::<Array>();
|
||||
Ok(rhai_array)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Method set_status
|
||||
engine.register_fn("set_contract_status", |contract: &mut Contract, status: ContractStatus| {
|
||||
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",
|
||||
engine.register_fn(
|
||||
"set_contract",
|
||||
move |contract: Contract| -> Result<(), Box<EvalAltResult>> {
|
||||
captured_db_for_set.set(&contract).map(|_| ()).map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to set Contract (ID: {}): {}", contract.base_data.id, e).into(),
|
||||
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",
|
||||
engine.register_fn(
|
||||
"get_contract_by_id",
|
||||
move |context: NativeCallContext, id_i64: i64| -> Result<Contract, Box<EvalAltResult>> {
|
||||
let id_u32 = i64_to_u32(id_i64, context.position(), "id", "get_contract_by_id")?;
|
||||
let id_u32 = i64_to_u32(id_i64, context.call_position(), "id", "get_contract_by_id")?;
|
||||
|
||||
captured_db_for_get.get_by_id(id_u32)
|
||||
.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(
|
||||
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(
|
||||
))
|
||||
})?
|
||||
.ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Contract with ID {} not found", id_u32).into(),
|
||||
Position::NONE,
|
||||
)))
|
||||
});
|
||||
))
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents a collection of library items.
|
||||
#[model]
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,41 +3,41 @@ pub mod core;
|
||||
pub mod userexample;
|
||||
// pub mod productexample; // Temporarily remove as files are missing
|
||||
pub mod access;
|
||||
pub mod calendar;
|
||||
pub mod contact;
|
||||
pub mod circle;
|
||||
pub mod governance;
|
||||
pub mod finance;
|
||||
pub mod library;
|
||||
pub mod legal;
|
||||
pub mod flow;
|
||||
pub mod biz;
|
||||
pub mod calendar;
|
||||
pub mod circle;
|
||||
pub mod contact;
|
||||
pub mod finance;
|
||||
pub mod flow;
|
||||
pub mod governance;
|
||||
pub mod legal;
|
||||
pub mod library;
|
||||
pub mod projects;
|
||||
|
||||
// Re-export key types for convenience
|
||||
pub use core::Comment;
|
||||
pub use userexample::User;
|
||||
// pub use productexample::Product; // Temporarily remove
|
||||
pub use calendar::{Calendar, Event, Attendee, AttendanceStatus};
|
||||
pub use circle::{Circle};
|
||||
pub use governance::{Proposal, ProposalStatus, VoteEventStatus, Ballot, VoteOption, AttachedFile};
|
||||
pub use finance::{Account, Asset, AssetType};
|
||||
pub use finance::marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus};
|
||||
pub use legal::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
|
||||
pub use flow::{Flow, FlowStep, SignatureRequirement};
|
||||
pub use biz::{Sale, SaleItem, SaleStatus};
|
||||
pub use library::items::{Image, Pdf, Markdown};
|
||||
pub use calendar::{AttendanceStatus, Attendee, Calendar, Event};
|
||||
pub use circle::Circle;
|
||||
pub use finance::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
|
||||
pub use finance::{Account, Asset, AssetType};
|
||||
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};
|
||||
|
||||
pub use flow::register_flow_rhai_module;
|
||||
#[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 biz::register_biz_rhai_module;
|
||||
pub use library::register_library_rhai_module;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use projects::register_projects_rhai_module;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use library::register_library_rhai_module;
|
||||
|
@ -1,8 +1,8 @@
|
||||
// heromodels/src/models/projects/base.rs
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_core::{BaseModelData, Model, BaseModelDataOps};
|
||||
use heromodels_core::{BaseModelData, BaseModelDataOps, Model};
|
||||
#[cfg(feature = "rhai")]
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::Display; // Made unconditional as Display derive is used on non-rhai-gated enums
|
||||
|
||||
// --- Enums ---
|
||||
@ -178,7 +178,6 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[cfg_attr(feature = "rhai", derive(CustomType))]
|
||||
pub struct Label {
|
||||
@ -226,4 +225,3 @@ impl Label {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::base::Status as ProjectStatus; // Using the generic project status for now
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
// heromodels/src/models/projects/mod.rs
|
||||
|
||||
pub mod base;
|
||||
pub mod task_enums;
|
||||
pub mod task;
|
||||
pub mod epic;
|
||||
pub mod sprint_enums;
|
||||
pub mod sprint;
|
||||
pub mod sprint_enums;
|
||||
pub mod task;
|
||||
pub mod task_enums;
|
||||
// pub mod epic;
|
||||
// pub mod issue;
|
||||
// pub mod kanban;
|
||||
@ -13,11 +13,11 @@ pub mod sprint;
|
||||
// pub mod story;
|
||||
|
||||
pub use base::*;
|
||||
pub use task_enums::*;
|
||||
pub use task::*;
|
||||
pub use epic::*;
|
||||
pub use sprint_enums::*;
|
||||
pub use sprint::*;
|
||||
pub use sprint_enums::*;
|
||||
pub use task::*;
|
||||
pub use task_enums::*;
|
||||
// pub use epic::*;
|
||||
// pub use issue::*;
|
||||
// pub use kanban::*;
|
||||
|
@ -1,14 +1,13 @@
|
||||
// heromodels/src/models/projects/rhai.rs
|
||||
|
||||
use rhai::{Engine, EvalAltResult, Dynamic, Position};
|
||||
use std::sync::Arc;
|
||||
use crate::db::hero::OurDB;
|
||||
use heromodels_core::{Model, BaseModelDataOps};
|
||||
use crate::db::{Db, Collection};
|
||||
|
||||
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::{Project, /* Label, */ Priority, Status, ItemType}; // Label commented out as it's unused for now
|
||||
use super::base::{ItemType, /* Label, */ Priority, Project, Status}; // Label commented out as it's unused for now
|
||||
|
||||
// Helper function for ID conversion (if needed, similar to other rhai.rs files)
|
||||
fn id_from_i64(val: i64) -> Result<u32, Box<EvalAltResult>> {
|
||||
@ -78,46 +77,141 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
});
|
||||
|
||||
// Multi-argument constructor (renamed)
|
||||
engine.register_fn("new_project_with_details", |id_i64: i64, name: String, description: String, owner_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(Project::new(id_from_i64(id_i64)?, name, description, id_from_i64(owner_id_i64)?))
|
||||
});
|
||||
engine.register_fn(
|
||||
"new_project_with_details",
|
||||
|id_i64: i64,
|
||||
name: String,
|
||||
description: String,
|
||||
owner_id_i64: i64|
|
||||
-> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(Project::new(
|
||||
id_from_i64(id_i64)?,
|
||||
name,
|
||||
description,
|
||||
id_from_i64(owner_id_i64)?,
|
||||
))
|
||||
},
|
||||
);
|
||||
|
||||
// Getters for Project
|
||||
engine.register_get("id", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.get_id() as i64) });
|
||||
engine.register_get("name", |p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.name.clone()) });
|
||||
engine.register_get("description", |p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.description.clone()) });
|
||||
engine.register_get("owner_id", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.owner_id as i64) });
|
||||
engine.register_get("member_ids", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.member_ids.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
engine.register_get("id", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> {
|
||||
Ok(p.get_id() as i64)
|
||||
});
|
||||
engine.register_get("board_ids", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.board_ids.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("sprint_ids", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.sprint_ids.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("epic_ids", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.epic_ids.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("tags", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.tags.iter().map(|tag| rhai::Dynamic::from(tag.clone())).collect())
|
||||
});
|
||||
engine.register_get("created_at", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.created_at) });
|
||||
engine.register_get("modified_at", |p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.modified_at) });
|
||||
engine.register_get("comments", |p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.base_data.comments.iter().map(|&id| rhai::Dynamic::from(id as i64)).collect())
|
||||
});
|
||||
engine.register_get("status", |p: &mut Project| -> Result<Status, Box<EvalAltResult>> { Ok(p.status.clone()) });
|
||||
engine.register_get("priority", |p: &mut Project| -> Result<Priority, Box<EvalAltResult>> { Ok(p.priority.clone()) });
|
||||
engine.register_get("item_type", |p: &mut Project| -> Result<ItemType, Box<EvalAltResult>> { Ok(p.item_type.clone()) });
|
||||
engine.register_get(
|
||||
"name",
|
||||
|p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.name.clone()) },
|
||||
);
|
||||
engine.register_get(
|
||||
"description",
|
||||
|p: &mut Project| -> Result<String, Box<EvalAltResult>> { Ok(p.description.clone()) },
|
||||
);
|
||||
engine.register_get(
|
||||
"owner_id",
|
||||
|p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.owner_id as i64) },
|
||||
);
|
||||
engine.register_get(
|
||||
"member_ids",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.member_ids
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"board_ids",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.board_ids
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"sprint_ids",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.sprint_ids
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"epic_ids",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.epic_ids
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"tags",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.tags
|
||||
.iter()
|
||||
.map(|tag| rhai::Dynamic::from(tag.clone()))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"created_at",
|
||||
|p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.created_at) },
|
||||
);
|
||||
engine.register_get(
|
||||
"modified_at",
|
||||
|p: &mut Project| -> Result<i64, Box<EvalAltResult>> { Ok(p.base_data.modified_at) },
|
||||
);
|
||||
engine.register_get(
|
||||
"comments",
|
||||
|p: &mut Project| -> Result<rhai::Array, Box<EvalAltResult>> {
|
||||
Ok(p.base_data
|
||||
.comments
|
||||
.iter()
|
||||
.map(|&id| rhai::Dynamic::from(id as i64))
|
||||
.collect())
|
||||
},
|
||||
);
|
||||
engine.register_get(
|
||||
"status",
|
||||
|p: &mut Project| -> Result<Status, Box<EvalAltResult>> { Ok(p.status.clone()) },
|
||||
);
|
||||
engine.register_get(
|
||||
"priority",
|
||||
|p: &mut Project| -> Result<Priority, Box<EvalAltResult>> { Ok(p.priority.clone()) },
|
||||
);
|
||||
engine.register_get(
|
||||
"item_type",
|
||||
|p: &mut Project| -> Result<ItemType, Box<EvalAltResult>> { Ok(p.item_type.clone()) },
|
||||
);
|
||||
|
||||
// Builder methods for Project
|
||||
// let db_clone = db.clone(); // This was unused
|
||||
engine.register_fn("name", |p: Project, name: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.name(name)) });
|
||||
engine.register_fn("description", |p: Project, description: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.description(description)) });
|
||||
engine.register_fn("owner_id", |p: Project, owner_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.owner_id(id_from_i64(owner_id_i64)?)) });
|
||||
engine.register_fn("add_member_id", |p: Project, member_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_member_id(id_from_i64(member_id_i64)?)) });
|
||||
engine.register_fn("member_ids", |p: Project, member_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
engine.register_fn(
|
||||
"name",
|
||||
|p: Project, name: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.name(name)) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"description",
|
||||
|p: Project, description: String| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.description(description))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"owner_id",
|
||||
|p: Project, owner_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.owner_id(id_from_i64(owner_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_member_id",
|
||||
|p: Project, member_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_member_id(id_from_i64(member_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"member_ids",
|
||||
|p: Project, member_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = member_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
@ -132,9 +226,17 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.member_ids(ids))
|
||||
});
|
||||
engine.register_fn("add_board_id", |p: Project, board_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_board_id(id_from_i64(board_id_i64)?)) });
|
||||
engine.register_fn("board_ids", |p: Project, board_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_board_id",
|
||||
|p: Project, board_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_board_id(id_from_i64(board_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"board_ids",
|
||||
|p: Project, board_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = board_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
@ -149,9 +251,17 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.board_ids(ids))
|
||||
});
|
||||
engine.register_fn("add_sprint_id", |p: Project, sprint_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_sprint_id(id_from_i64(sprint_id_i64)?)) });
|
||||
engine.register_fn("sprint_ids", |p: Project, sprint_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_sprint_id",
|
||||
|p: Project, sprint_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_sprint_id(id_from_i64(sprint_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"sprint_ids",
|
||||
|p: Project, sprint_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = sprint_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
@ -166,9 +276,17 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.sprint_ids(ids))
|
||||
});
|
||||
engine.register_fn("add_epic_id", |p: Project, epic_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_epic_id(id_from_i64(epic_id_i64)?)) });
|
||||
engine.register_fn("epic_ids", |p: Project, epic_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_epic_id",
|
||||
|p: Project, epic_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_epic_id(id_from_i64(epic_id_i64)?))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"epic_ids",
|
||||
|p: Project, epic_ids_i64: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let ids = epic_ids_i64
|
||||
.into_iter()
|
||||
.map(|id_dyn: Dynamic| {
|
||||
@ -183,13 +301,20 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
})
|
||||
.collect::<Result<Vec<u32>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.epic_ids(ids))
|
||||
});
|
||||
engine.register_fn("add_tag", |p: Project, tag: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_tag(tag)) });
|
||||
engine.register_fn("tags", |p: Project, tags_dyn: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"add_tag",
|
||||
|p: Project, tag: String| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_tag(tag)) },
|
||||
);
|
||||
engine.register_fn(
|
||||
"tags",
|
||||
|p: Project, tags_dyn: rhai::Array| -> Result<Project, Box<EvalAltResult>> {
|
||||
let tags_vec = tags_dyn
|
||||
.into_iter()
|
||||
.map(|tag_dyn: Dynamic| {
|
||||
tag_dyn.clone().into_string().map_err(|_err| { // _err is Rhai's internal error, we create a new one
|
||||
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(),
|
||||
@ -199,17 +324,40 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
})
|
||||
.collect::<Result<Vec<String>, Box<EvalAltResult>>>()?;
|
||||
Ok(p.tags(tags_vec))
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
engine.register_fn("status", |p: Project, status: Status| -> Result<Project, Box<EvalAltResult>> { Ok(p.status(status)) });
|
||||
engine.register_fn("priority", |p: Project, priority: Priority| -> Result<Project, Box<EvalAltResult>> { Ok(p.priority(priority)) });
|
||||
engine.register_fn("item_type", |p: Project, item_type: ItemType| -> Result<Project, Box<EvalAltResult>> { Ok(p.item_type(item_type)) });
|
||||
engine.register_fn(
|
||||
"status",
|
||||
|p: Project, status: Status| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.status(status))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"priority",
|
||||
|p: Project, priority: Priority| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.priority(priority))
|
||||
},
|
||||
);
|
||||
engine.register_fn(
|
||||
"item_type",
|
||||
|p: Project, item_type: ItemType| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.item_type(item_type))
|
||||
},
|
||||
);
|
||||
// Base ModelData builders
|
||||
engine.register_fn("add_base_comment", |p: Project, comment_id_i64: i64| -> Result<Project, Box<EvalAltResult>> { Ok(p.add_base_comment(id_from_i64(comment_id_i64)?)) });
|
||||
engine.register_fn(
|
||||
"add_base_comment",
|
||||
|p: Project, comment_id_i64: i64| -> Result<Project, Box<EvalAltResult>> {
|
||||
Ok(p.add_base_comment(id_from_i64(comment_id_i64)?))
|
||||
},
|
||||
);
|
||||
|
||||
// --- Database Interaction Functions ---
|
||||
let db_clone_set = db.clone();
|
||||
engine.register_fn("set_project", move |project: Project| -> Result<(), Box<EvalAltResult>> {
|
||||
engine.register_fn(
|
||||
"set_project",
|
||||
move |project: Project| -> Result<(), Box<EvalAltResult>> {
|
||||
let collection = db_clone_set.collection::<Project>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
"Failed to access project collection".to_string(),
|
||||
@ -223,10 +371,13 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
format!("DB operation failed: {:?}", e).into(),
|
||||
))
|
||||
})
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let db_clone_get = db.clone();
|
||||
engine.register_fn("get_project_by_id", move |id_i64: i64| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
engine.register_fn(
|
||||
"get_project_by_id",
|
||||
move |id_i64: i64| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let id = id_from_i64(id_i64)?;
|
||||
let collection = db_clone_get.collection::<Project>().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorSystem(
|
||||
@ -243,7 +394,8 @@ pub fn register_projects_rhai_module(engine: &mut Engine, db: Arc<OurDB>) {
|
||||
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
|
||||
|
@ -3,8 +3,8 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::sprint_enums::SprintStatus; // Import our new enum
|
||||
|
||||
|
@ -3,10 +3,10 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::{CustomType, TypeBuilder}; // Assuming rhai might be used
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize}; // Assuming rhai might be used
|
||||
|
||||
use super::task_enums::{TaskStatus, TaskPriority}; // Import our new enums
|
||||
use super::task_enums::{TaskPriority, TaskStatus}; // Import our new enums
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||
#[model] // This will provide id, created_at, updated_at via base_data
|
||||
|
@ -15,9 +15,9 @@ rand = "0.8.5"
|
||||
criterion = "0.5.1"
|
||||
tempfile = "3.8.0"
|
||||
|
||||
[[bench]]
|
||||
name = "ourdb_benchmarks"
|
||||
harness = false
|
||||
# [[bench]]
|
||||
# name = "ourdb_benchmarks"
|
||||
# harness = false
|
||||
|
||||
[[example]]
|
||||
name = "basic_usage"
|
||||
|
@ -52,21 +52,31 @@ fn key_value_mode_example(base_path: &PathBuf) -> Result<(), ourdb::Error> {
|
||||
// Store data with custom IDs
|
||||
for (i, &id) in custom_ids.iter().enumerate() {
|
||||
let data = format!("Record with custom ID {}", id);
|
||||
db.set(OurDBSetArgs { id: Some(id), data: data.as_bytes() })?;
|
||||
println!("Stored record {} with custom ID: {}", i+1, id);
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: data.as_bytes(),
|
||||
})?;
|
||||
println!("Stored record {} with custom ID: {}", i + 1, id);
|
||||
}
|
||||
|
||||
// Retrieve data by custom IDs
|
||||
for &id in &custom_ids {
|
||||
let retrieved = db.get(id)?;
|
||||
println!("Retrieved ID {}: {}", id, String::from_utf8_lossy(&retrieved));
|
||||
println!(
|
||||
"Retrieved ID {}: {}",
|
||||
id,
|
||||
String::from_utf8_lossy(&retrieved)
|
||||
);
|
||||
}
|
||||
|
||||
// Update and track history
|
||||
let id_to_update = custom_ids[2]; // ID 300
|
||||
for i in 1..=3 {
|
||||
let updated_data = format!("Updated record {} (version {})", id_to_update, i);
|
||||
db.set(OurDBSetArgs { id: Some(id_to_update), data: updated_data.as_bytes() })?;
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id_to_update),
|
||||
data: updated_data.as_bytes(),
|
||||
})?;
|
||||
println!("Updated ID {} (version {})", id_to_update, i);
|
||||
}
|
||||
|
||||
@ -106,7 +116,10 @@ fn incremental_mode_example(base_path: &PathBuf) -> Result<(), ourdb::Error> {
|
||||
// Store multiple records and collect assigned IDs
|
||||
for i in 1..=5 {
|
||||
let data = format!("Auto-increment record {}", i);
|
||||
let id = db.set(OurDBSetArgs { id: None, data: data.as_bytes() })?;
|
||||
let id = db.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: data.as_bytes(),
|
||||
})?;
|
||||
assigned_ids.push(id);
|
||||
println!("Stored record {} with auto-assigned ID: {}", i, id);
|
||||
}
|
||||
@ -118,7 +131,11 @@ fn incremental_mode_example(base_path: &PathBuf) -> Result<(), ourdb::Error> {
|
||||
// Retrieve all records
|
||||
for &id in &assigned_ids {
|
||||
let retrieved = db.get(id)?;
|
||||
println!("Retrieved ID {}: {}", id, String::from_utf8_lossy(&retrieved));
|
||||
println!(
|
||||
"Retrieved ID {}: {}",
|
||||
id,
|
||||
String::from_utf8_lossy(&retrieved)
|
||||
);
|
||||
}
|
||||
|
||||
db.close()?;
|
||||
@ -157,15 +174,20 @@ fn performance_benchmark(base_path: &PathBuf) -> Result<(), ourdb::Error> {
|
||||
|
||||
let mut ids = Vec::with_capacity(num_operations);
|
||||
for _ in 0..num_operations {
|
||||
let id = db.set(OurDBSetArgs { id: None, data: &test_data })?;
|
||||
let id = db.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: &test_data,
|
||||
})?;
|
||||
ids.push(id);
|
||||
}
|
||||
|
||||
let write_duration = start.elapsed();
|
||||
let writes_per_second = num_operations as f64 / write_duration.as_secs_f64();
|
||||
println!("Write performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
println!(
|
||||
"Write performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
writes_per_second,
|
||||
write_duration.as_secs_f64() * 1000.0 / num_operations as f64);
|
||||
write_duration.as_secs_f64() * 1000.0 / num_operations as f64
|
||||
);
|
||||
|
||||
// Benchmark read operations
|
||||
println!("Benchmarking {} read operations...", num_operations);
|
||||
@ -177,23 +199,30 @@ fn performance_benchmark(base_path: &PathBuf) -> Result<(), ourdb::Error> {
|
||||
|
||||
let read_duration = start.elapsed();
|
||||
let reads_per_second = num_operations as f64 / read_duration.as_secs_f64();
|
||||
println!("Read performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
println!(
|
||||
"Read performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
reads_per_second,
|
||||
read_duration.as_secs_f64() * 1000.0 / num_operations as f64);
|
||||
read_duration.as_secs_f64() * 1000.0 / num_operations as f64
|
||||
);
|
||||
|
||||
// Benchmark update operations
|
||||
println!("Benchmarking {} update operations...", num_operations);
|
||||
let start = Instant::now();
|
||||
|
||||
for &id in &ids {
|
||||
db.set(OurDBSetArgs { id: Some(id), data: &test_data })?;
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: &test_data,
|
||||
})?;
|
||||
}
|
||||
|
||||
let update_duration = start.elapsed();
|
||||
let updates_per_second = num_operations as f64 / update_duration.as_secs_f64();
|
||||
println!("Update performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
println!(
|
||||
"Update performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
updates_per_second,
|
||||
update_duration.as_secs_f64() * 1000.0 / num_operations as f64);
|
||||
update_duration.as_secs_f64() * 1000.0 / num_operations as f64
|
||||
);
|
||||
|
||||
db.close()?;
|
||||
println!("Performance benchmark completed");
|
||||
|
@ -20,23 +20,40 @@ fn main() -> Result<(), ourdb::Error> {
|
||||
|
||||
// Store some data with auto-generated IDs
|
||||
let data1 = b"First record";
|
||||
let id1 = db.set(OurDBSetArgs { id: None, data: data1 })?;
|
||||
let id1 = db.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: data1,
|
||||
})?;
|
||||
println!("Stored first record with ID: {}", id1);
|
||||
|
||||
let data2 = b"Second record";
|
||||
let id2 = db.set(OurDBSetArgs { id: None, data: data2 })?;
|
||||
let id2 = db.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: data2,
|
||||
})?;
|
||||
println!("Stored second record with ID: {}", id2);
|
||||
|
||||
// Retrieve and print the data
|
||||
let retrieved1 = db.get(id1)?;
|
||||
println!("Retrieved ID {}: {}", id1, String::from_utf8_lossy(&retrieved1));
|
||||
println!(
|
||||
"Retrieved ID {}: {}",
|
||||
id1,
|
||||
String::from_utf8_lossy(&retrieved1)
|
||||
);
|
||||
|
||||
let retrieved2 = db.get(id2)?;
|
||||
println!("Retrieved ID {}: {}", id2, String::from_utf8_lossy(&retrieved2));
|
||||
println!(
|
||||
"Retrieved ID {}: {}",
|
||||
id2,
|
||||
String::from_utf8_lossy(&retrieved2)
|
||||
);
|
||||
|
||||
// Update a record to demonstrate history tracking
|
||||
let updated_data = b"Updated first record";
|
||||
db.set(OurDBSetArgs { id: Some(id1), data: updated_data })?;
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id1),
|
||||
data: updated_data,
|
||||
})?;
|
||||
println!("Updated record with ID: {}", id1);
|
||||
|
||||
// Get history for the updated record
|
||||
|
@ -42,19 +42,27 @@ fn main() -> Result<(), ourdb::Error> {
|
||||
let test_data = vec![b'A'; 100];
|
||||
|
||||
// Benchmark write operations
|
||||
println!("Benchmarking {} write operations (incremental: {}, keysize: {})...",
|
||||
num_operations, incremental_mode, keysize);
|
||||
println!(
|
||||
"Benchmarking {} write operations (incremental: {}, keysize: {})...",
|
||||
num_operations, incremental_mode, keysize
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
let mut ids = Vec::with_capacity(num_operations);
|
||||
for _ in 0..num_operations {
|
||||
let id = if incremental_mode {
|
||||
db.set(OurDBSetArgs { id: None, data: &test_data })?
|
||||
db.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: &test_data,
|
||||
})?
|
||||
} else {
|
||||
// In non-incremental mode, we need to provide IDs
|
||||
let id = ids.len() as u32 + 1;
|
||||
db.set(OurDBSetArgs { id: Some(id), data: &test_data })?;
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: &test_data,
|
||||
})?;
|
||||
id
|
||||
};
|
||||
ids.push(id);
|
||||
@ -63,9 +71,11 @@ fn main() -> Result<(), ourdb::Error> {
|
||||
let write_duration = start.elapsed();
|
||||
let writes_per_second = num_operations as f64 / write_duration.as_secs_f64();
|
||||
|
||||
println!("Write performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
println!(
|
||||
"Write performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
writes_per_second,
|
||||
write_duration.as_secs_f64() * 1000.0 / num_operations as f64);
|
||||
write_duration.as_secs_f64() * 1000.0 / num_operations as f64
|
||||
);
|
||||
|
||||
// Benchmark read operations
|
||||
println!("Benchmarking {} read operations...", num_operations);
|
||||
@ -79,9 +89,11 @@ fn main() -> Result<(), ourdb::Error> {
|
||||
let read_duration = start.elapsed();
|
||||
let reads_per_second = num_operations as f64 / read_duration.as_secs_f64();
|
||||
|
||||
println!("Read performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
println!(
|
||||
"Read performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
reads_per_second,
|
||||
read_duration.as_secs_f64() * 1000.0 / num_operations as f64);
|
||||
read_duration.as_secs_f64() * 1000.0 / num_operations as f64
|
||||
);
|
||||
|
||||
// Benchmark update operations
|
||||
println!("Benchmarking {} update operations...", num_operations);
|
||||
@ -89,15 +101,20 @@ fn main() -> Result<(), ourdb::Error> {
|
||||
let start = Instant::now();
|
||||
|
||||
for &id in &ids {
|
||||
db.set(OurDBSetArgs { id: Some(id), data: &test_data })?;
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: &test_data,
|
||||
})?;
|
||||
}
|
||||
|
||||
let update_duration = start.elapsed();
|
||||
let updates_per_second = num_operations as f64 / update_duration.as_secs_f64();
|
||||
|
||||
println!("Update performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
println!(
|
||||
"Update performance: {:.2} ops/sec ({:.2} ms/op)",
|
||||
updates_per_second,
|
||||
update_duration.as_secs_f64() * 1000.0 / num_operations as f64);
|
||||
update_duration.as_secs_f64() * 1000.0 / num_operations as f64
|
||||
);
|
||||
|
||||
// Clean up
|
||||
db.close()?;
|
||||
|
@ -30,7 +30,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Store some data
|
||||
let test_data = b"Hello, OurDB!";
|
||||
let id = db.set(OurDBSetArgs { id: None, data: test_data })?;
|
||||
let id = db.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: test_data,
|
||||
})?;
|
||||
println!("\nStored data with ID: {}", id);
|
||||
|
||||
// Retrieve the data
|
||||
@ -39,12 +42,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Update the data
|
||||
let updated_data = b"Updated data in OurDB!";
|
||||
db.set(OurDBSetArgs { id: Some(id), data: updated_data })?;
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: updated_data,
|
||||
})?;
|
||||
println!("\nUpdated data with ID: {}", id);
|
||||
|
||||
// Retrieve the updated data
|
||||
let retrieved = db.get(id)?;
|
||||
println!("Retrieved updated data: {}", String::from_utf8_lossy(&retrieved));
|
||||
println!(
|
||||
"Retrieved updated data: {}",
|
||||
String::from_utf8_lossy(&retrieved)
|
||||
);
|
||||
|
||||
// Get history
|
||||
let history = db.get_history(id, 2)?;
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
|
||||
|
||||
use crc32fast::Hasher;
|
||||
|
||||
use crate::error::Error;
|
||||
@ -27,10 +26,7 @@ impl OurDB {
|
||||
}
|
||||
|
||||
// Open the file fresh
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(&path)?;
|
||||
let file = OpenOptions::new().read(true).write(true).open(&path)?;
|
||||
|
||||
self.file = Some(file);
|
||||
self.file_nr = file_nr;
|
||||
@ -80,12 +76,18 @@ impl OurDB {
|
||||
}
|
||||
|
||||
/// Stores data at the specified ID with history tracking
|
||||
pub(crate) fn set_(&mut self, id: u32, old_location: Location, data: &[u8]) -> Result<(), Error> {
|
||||
pub(crate) fn set_(
|
||||
&mut self,
|
||||
id: u32,
|
||||
old_location: Location,
|
||||
data: &[u8],
|
||||
) -> Result<(), Error> {
|
||||
// Validate data size - maximum is u16::MAX (65535 bytes or ~64KB)
|
||||
if data.len() > u16::MAX as usize {
|
||||
return Err(Error::InvalidOperation(
|
||||
format!("Data size exceeds maximum allowed size of {} bytes", u16::MAX)
|
||||
));
|
||||
return Err(Error::InvalidOperation(format!(
|
||||
"Data size exceeds maximum allowed size of {} bytes",
|
||||
u16::MAX
|
||||
)));
|
||||
}
|
||||
|
||||
// Get file number to use
|
||||
@ -95,15 +97,15 @@ impl OurDB {
|
||||
self.db_file_select(file_nr)?;
|
||||
|
||||
// Get current file position for lookup
|
||||
let file = self.file.as_mut().ok_or_else(|| Error::Other("No file open".to_string()))?;
|
||||
let file = self
|
||||
.file
|
||||
.as_mut()
|
||||
.ok_or_else(|| Error::Other("No file open".to_string()))?;
|
||||
file.seek(SeekFrom::End(0))?;
|
||||
let position = file.stream_position()? as u32;
|
||||
|
||||
// Create new location
|
||||
let new_location = Location {
|
||||
file_nr,
|
||||
position,
|
||||
};
|
||||
let new_location = Location { file_nr, position };
|
||||
|
||||
// Calculate CRC of data
|
||||
let crc = calculate_crc(data);
|
||||
@ -144,13 +146,19 @@ impl OurDB {
|
||||
/// Retrieves data at the specified location
|
||||
pub(crate) fn get_(&mut self, location: Location) -> Result<Vec<u8>, Error> {
|
||||
if location.position == 0 {
|
||||
return Err(Error::NotFound(format!("Record not found, location: {:?}", location)));
|
||||
return Err(Error::NotFound(format!(
|
||||
"Record not found, location: {:?}",
|
||||
location
|
||||
)));
|
||||
}
|
||||
|
||||
// Select the file
|
||||
self.db_file_select(location.file_nr)?;
|
||||
|
||||
let file = self.file.as_mut().ok_or_else(|| Error::Other("No file open".to_string()))?;
|
||||
let file = self
|
||||
.file
|
||||
.as_mut()
|
||||
.ok_or_else(|| Error::Other("No file open".to_string()))?;
|
||||
|
||||
// Read header
|
||||
file.seek(SeekFrom::Start(location.position as u64))?;
|
||||
@ -173,7 +181,9 @@ impl OurDB {
|
||||
// Verify CRC
|
||||
let calculated_crc = calculate_crc(&data);
|
||||
if calculated_crc != stored_crc {
|
||||
return Err(Error::DataCorruption("CRC mismatch: data corruption detected".to_string()));
|
||||
return Err(Error::DataCorruption(
|
||||
"CRC mismatch: data corruption detected".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
@ -188,7 +198,10 @@ impl OurDB {
|
||||
// Select the file
|
||||
self.db_file_select(location.file_nr)?;
|
||||
|
||||
let file = self.file.as_mut().ok_or_else(|| Error::Other("No file open".to_string()))?;
|
||||
let file = self
|
||||
.file
|
||||
.as_mut()
|
||||
.ok_or_else(|| Error::Other("No file open".to_string()))?;
|
||||
|
||||
// Skip size and CRC (6 bytes)
|
||||
file.seek(SeekFrom::Start(location.position as u64 + 6))?;
|
||||
@ -210,7 +223,10 @@ impl OurDB {
|
||||
// Select the file
|
||||
self.db_file_select(location.file_nr)?;
|
||||
|
||||
let file = self.file.as_mut().ok_or_else(|| Error::Other("No file open".to_string()))?;
|
||||
let file = self
|
||||
.file
|
||||
.as_mut()
|
||||
.ok_or_else(|| Error::Other("No file open".to_string()))?;
|
||||
|
||||
// Read size first
|
||||
file.seek(SeekFrom::Start(location.position as u64))?;
|
||||
@ -335,7 +351,11 @@ mod tests {
|
||||
let test_data = b"Test data for backend operations";
|
||||
let id = 1;
|
||||
|
||||
db.set(OurDBSetArgs { id: Some(id), data: test_data }).unwrap();
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: test_data,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let retrieved = db.get(id).unwrap();
|
||||
assert_eq!(retrieved, test_data);
|
||||
|
@ -1,7 +1,7 @@
|
||||
mod backend;
|
||||
mod error;
|
||||
mod location;
|
||||
mod lookup;
|
||||
mod backend;
|
||||
|
||||
pub use error::Error;
|
||||
pub use location::Location;
|
||||
@ -110,7 +110,7 @@ impl OurDB {
|
||||
let location = self.lookup.get(id)?;
|
||||
if location.position == 0 {
|
||||
return Err(Error::InvalidOperation(
|
||||
"Cannot set ID for insertions when incremental mode is enabled".to_string()
|
||||
"Cannot set ID for insertions when incremental mode is enabled".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@ -124,9 +124,11 @@ impl OurDB {
|
||||
}
|
||||
} else {
|
||||
// Using key-value mode
|
||||
let id = args.id.ok_or_else(|| Error::InvalidOperation(
|
||||
"ID must be provided when incremental is disabled".to_string()
|
||||
))?;
|
||||
let id = args.id.ok_or_else(|| {
|
||||
Error::InvalidOperation(
|
||||
"ID must be provided when incremental is disabled".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let location = self.lookup.get(id)?;
|
||||
self.set_(id, location, args.data)?;
|
||||
@ -179,7 +181,9 @@ impl OurDB {
|
||||
/// Returns the next ID which will be used when storing in incremental mode
|
||||
pub fn get_next_id(&mut self) -> Result<u32, Error> {
|
||||
if !self.incremental_mode {
|
||||
return Err(Error::InvalidOperation("Incremental mode is not enabled".to_string()));
|
||||
return Err(Error::InvalidOperation(
|
||||
"Incremental mode is not enabled".to_string(),
|
||||
));
|
||||
}
|
||||
self.lookup.get_next_id()
|
||||
}
|
||||
@ -212,7 +216,8 @@ impl OurDB {
|
||||
}
|
||||
|
||||
fn save(&mut self) -> Result<(), Error> {
|
||||
self.lookup.export_sparse(&self.lookup_dump_path().to_string_lossy())?;
|
||||
self.lookup
|
||||
.export_sparse(&self.lookup_dump_path().to_string_lossy())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -251,14 +256,23 @@ mod tests {
|
||||
|
||||
// Test set and get
|
||||
let test_data = b"Hello, OurDB!";
|
||||
let id = db.set(OurDBSetArgs { id: None, data: test_data }).unwrap();
|
||||
let id = db
|
||||
.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: test_data,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let retrieved = db.get(id).unwrap();
|
||||
assert_eq!(retrieved, test_data);
|
||||
|
||||
// Test update
|
||||
let updated_data = b"Updated data";
|
||||
db.set(OurDBSetArgs { id: Some(id), data: updated_data }).unwrap();
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: updated_data,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let retrieved = db.get(id).unwrap();
|
||||
assert_eq!(retrieved, updated_data);
|
||||
|
@ -22,13 +22,18 @@ impl Location {
|
||||
pub fn from_bytes(bytes: &[u8], keysize: u8) -> Result<Self, Error> {
|
||||
// Validate keysize
|
||||
if ![2, 3, 4, 6].contains(&keysize) {
|
||||
return Err(Error::InvalidOperation(format!("Invalid keysize: {}", keysize)));
|
||||
return Err(Error::InvalidOperation(format!(
|
||||
"Invalid keysize: {}",
|
||||
keysize
|
||||
)));
|
||||
}
|
||||
|
||||
// Create padded bytes
|
||||
let mut padded = vec![0u8; keysize as usize];
|
||||
if bytes.len() > keysize as usize {
|
||||
return Err(Error::InvalidOperation("Input bytes exceed keysize".to_string()));
|
||||
return Err(Error::InvalidOperation(
|
||||
"Input bytes exceed keysize".to_string(),
|
||||
));
|
||||
}
|
||||
let start_idx = keysize as usize - bytes.len();
|
||||
|
||||
@ -49,34 +54,39 @@ impl Location {
|
||||
// Verify limits
|
||||
if location.position > 0xFFFF {
|
||||
return Err(Error::InvalidOperation(
|
||||
"Position exceeds max value for keysize=2 (max 65535)".to_string()
|
||||
"Position exceeds max value for keysize=2 (max 65535)".to_string(),
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
3 => {
|
||||
// Only position, 3 bytes big endian
|
||||
location.position = u32::from(padded[0]) << 16 | u32::from(padded[1]) << 8 | u32::from(padded[2]);
|
||||
location.position =
|
||||
u32::from(padded[0]) << 16 | u32::from(padded[1]) << 8 | u32::from(padded[2]);
|
||||
location.file_nr = 0;
|
||||
|
||||
// Verify limits
|
||||
if location.position > 0xFFFFFF {
|
||||
return Err(Error::InvalidOperation(
|
||||
"Position exceeds max value for keysize=3 (max 16777215)".to_string()
|
||||
"Position exceeds max value for keysize=3 (max 16777215)".to_string(),
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
4 => {
|
||||
// Only position, 4 bytes big endian
|
||||
location.position = u32::from(padded[0]) << 24 | u32::from(padded[1]) << 16
|
||||
| u32::from(padded[2]) << 8 | u32::from(padded[3]);
|
||||
location.position = u32::from(padded[0]) << 24
|
||||
| u32::from(padded[1]) << 16
|
||||
| u32::from(padded[2]) << 8
|
||||
| u32::from(padded[3]);
|
||||
location.file_nr = 0;
|
||||
},
|
||||
}
|
||||
6 => {
|
||||
// 2 bytes file_nr + 4 bytes position, all big endian
|
||||
location.file_nr = u16::from(padded[0]) << 8 | u16::from(padded[1]);
|
||||
location.position = u32::from(padded[2]) << 24 | u32::from(padded[3]) << 16
|
||||
| u32::from(padded[4]) << 8 | u32::from(padded[5]);
|
||||
},
|
||||
location.position = u32::from(padded[2]) << 24
|
||||
| u32::from(padded[3]) << 16
|
||||
| u32::from(padded[4]) << 8
|
||||
| u32::from(padded[5]);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,10 @@ impl LookupTable {
|
||||
pub fn new(config: LookupConfig) -> Result<Self, Error> {
|
||||
// Verify keysize is valid
|
||||
if ![2, 3, 4, 6].contains(&config.keysize) {
|
||||
return Err(Error::InvalidOperation(format!("Invalid keysize: {}", config.keysize)));
|
||||
return Err(Error::InvalidOperation(format!(
|
||||
"Invalid keysize: {}",
|
||||
config.keysize
|
||||
)));
|
||||
}
|
||||
|
||||
let incremental = if config.incremental_mode {
|
||||
@ -98,7 +101,9 @@ impl LookupTable {
|
||||
if start_pos + entry_size as u64 > file_size {
|
||||
return Err(Error::LookupError(format!(
|
||||
"Invalid read for get in lut: {}: {} would exceed file size {}",
|
||||
self.lookuppath, start_pos + entry_size as u64, file_size
|
||||
self.lookuppath,
|
||||
start_pos + entry_size as u64,
|
||||
file_size
|
||||
)));
|
||||
}
|
||||
|
||||
@ -142,7 +147,7 @@ impl LookupTable {
|
||||
|
||||
if id > incremental {
|
||||
return Err(Error::InvalidOperation(
|
||||
"Cannot set ID for insertions when incremental mode is enabled".to_string()
|
||||
"Cannot set ID for insertions when incremental mode is enabled".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -151,46 +156,57 @@ impl LookupTable {
|
||||
let location_bytes = match self.keysize {
|
||||
2 => {
|
||||
if location.file_nr != 0 {
|
||||
return Err(Error::InvalidOperation("file_nr must be 0 for keysize=2".to_string()));
|
||||
return Err(Error::InvalidOperation(
|
||||
"file_nr must be 0 for keysize=2".to_string(),
|
||||
));
|
||||
}
|
||||
if location.position > 0xFFFF {
|
||||
return Err(Error::InvalidOperation(
|
||||
"position exceeds max value for keysize=2 (max 65535)".to_string()
|
||||
"position exceeds max value for keysize=2 (max 65535)".to_string(),
|
||||
));
|
||||
}
|
||||
vec![(location.position >> 8) as u8, location.position as u8]
|
||||
},
|
||||
}
|
||||
3 => {
|
||||
if location.file_nr != 0 {
|
||||
return Err(Error::InvalidOperation("file_nr must be 0 for keysize=3".to_string()));
|
||||
return Err(Error::InvalidOperation(
|
||||
"file_nr must be 0 for keysize=3".to_string(),
|
||||
));
|
||||
}
|
||||
if location.position > 0xFFFFFF {
|
||||
return Err(Error::InvalidOperation(
|
||||
"position exceeds max value for keysize=3 (max 16777215)".to_string()
|
||||
"position exceeds max value for keysize=3 (max 16777215)".to_string(),
|
||||
));
|
||||
}
|
||||
vec![
|
||||
(location.position >> 16) as u8,
|
||||
(location.position >> 8) as u8,
|
||||
location.position as u8
|
||||
location.position as u8,
|
||||
]
|
||||
},
|
||||
}
|
||||
4 => {
|
||||
if location.file_nr != 0 {
|
||||
return Err(Error::InvalidOperation("file_nr must be 0 for keysize=4".to_string()));
|
||||
return Err(Error::InvalidOperation(
|
||||
"file_nr must be 0 for keysize=4".to_string(),
|
||||
));
|
||||
}
|
||||
vec![
|
||||
(location.position >> 24) as u8,
|
||||
(location.position >> 16) as u8,
|
||||
(location.position >> 8) as u8,
|
||||
location.position as u8
|
||||
location.position as u8,
|
||||
]
|
||||
},
|
||||
}
|
||||
6 => {
|
||||
// Full location with file_nr and position
|
||||
location.to_bytes()
|
||||
},
|
||||
_ => return Err(Error::InvalidOperation(format!("Invalid keysize: {}", self.keysize))),
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::InvalidOperation(format!(
|
||||
"Invalid keysize: {}",
|
||||
self.keysize
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
if !self.lookuppath.is_empty() {
|
||||
@ -224,9 +240,9 @@ impl LookupTable {
|
||||
|
||||
/// Gets the next available ID in incremental mode
|
||||
pub fn get_next_id(&self) -> Result<u32, Error> {
|
||||
let incremental = self.incremental.ok_or_else(||
|
||||
let incremental = self.incremental.ok_or_else(|| {
|
||||
Error::InvalidOperation("Lookup table not in incremental mode".to_string())
|
||||
)?;
|
||||
})?;
|
||||
|
||||
let table_size = if !self.lookuppath.is_empty() {
|
||||
let data_path = Path::new(&self.lookuppath).join(DATA_FILE_NAME);
|
||||
@ -244,9 +260,9 @@ impl LookupTable {
|
||||
|
||||
/// Increments the index in incremental mode
|
||||
pub fn increment_index(&mut self) -> Result<(), Error> {
|
||||
let mut incremental = self.incremental.ok_or_else(||
|
||||
let mut incremental = self.incremental.ok_or_else(|| {
|
||||
Error::InvalidOperation("Lookup table not in incremental mode".to_string())
|
||||
)?;
|
||||
})?;
|
||||
|
||||
incremental += 1;
|
||||
self.incremental = Some(incremental);
|
||||
@ -344,7 +360,7 @@ impl LookupTable {
|
||||
|
||||
if data.len() % record_size != 0 {
|
||||
return Err(Error::DataCorruption(
|
||||
"Invalid sparse data format: size mismatch".to_string()
|
||||
"Invalid sparse data format: size mismatch".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@ -447,9 +463,9 @@ fn get_incremental_info(config: &LookupConfig) -> Result<u32, Error> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
use super::*;
|
||||
use std::env::temp_dir;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
fn get_temp_dir() -> PathBuf {
|
||||
|
@ -1,9 +1,9 @@
|
||||
use ourdb::{OurDB, OurDBConfig, OurDBSetArgs};
|
||||
use rand;
|
||||
use std::env::temp_dir;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use rand;
|
||||
|
||||
// Helper function to create a unique temporary directory for tests
|
||||
fn get_temp_dir() -> PathBuf {
|
||||
@ -33,22 +33,30 @@ fn test_basic_operations() {
|
||||
incremental_mode: true,
|
||||
file_size: None,
|
||||
keysize: None,
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
|
||||
// Test set and get
|
||||
let test_data = b"Hello, OurDB!";
|
||||
let id = db.set(OurDBSetArgs { id: None, data: test_data }).unwrap();
|
||||
let id = db
|
||||
.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: test_data,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let retrieved = db.get(id).unwrap();
|
||||
assert_eq!(retrieved, test_data);
|
||||
|
||||
// Test update
|
||||
let updated_data = b"Updated data";
|
||||
db.set(OurDBSetArgs { id: Some(id), data: updated_data }).unwrap();
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: updated_data,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let retrieved = db.get(id).unwrap();
|
||||
assert_eq!(retrieved, updated_data);
|
||||
@ -71,14 +79,13 @@ fn test_basic_operations() {
|
||||
fn test_key_value_mode() {
|
||||
let temp_dir = get_temp_dir();
|
||||
|
||||
|
||||
// Create a new database with key-value mode
|
||||
let config = OurDBConfig {
|
||||
path: temp_dir.clone(),
|
||||
incremental_mode: false,
|
||||
file_size: None,
|
||||
keysize: None,
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
@ -86,7 +93,11 @@ fn test_key_value_mode() {
|
||||
// Test set with explicit ID
|
||||
let test_data = b"Key-value data";
|
||||
let id = 42;
|
||||
db.set(OurDBSetArgs { id: Some(id), data: test_data }).unwrap();
|
||||
db.set(OurDBSetArgs {
|
||||
id: Some(id),
|
||||
data: test_data,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let retrieved = db.get(id).unwrap();
|
||||
assert_eq!(retrieved, test_data);
|
||||
@ -108,18 +119,27 @@ fn test_incremental_mode() {
|
||||
incremental_mode: true,
|
||||
file_size: None,
|
||||
keysize: None,
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
|
||||
// Test auto-increment IDs
|
||||
let data1 = b"First record";
|
||||
let id1 = db.set(OurDBSetArgs { id: None, data: data1 }).unwrap();
|
||||
let id1 = db
|
||||
.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: data1,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let data2 = b"Second record";
|
||||
let id2 = db.set(OurDBSetArgs { id: None, data: data2 }).unwrap();
|
||||
let id2 = db
|
||||
.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: data2,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// IDs should be sequential
|
||||
assert_eq!(id2, id1 + 1);
|
||||
@ -136,7 +156,6 @@ fn test_incremental_mode() {
|
||||
fn test_persistence() {
|
||||
let temp_dir = get_temp_dir();
|
||||
|
||||
|
||||
// Create data in a new database
|
||||
{
|
||||
let config = OurDBConfig {
|
||||
@ -144,13 +163,18 @@ fn test_persistence() {
|
||||
incremental_mode: true,
|
||||
file_size: None,
|
||||
keysize: None,
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
|
||||
let test_data = b"Persistent data";
|
||||
let id = db.set(OurDBSetArgs { id: None, data: test_data }).unwrap();
|
||||
let id = db
|
||||
.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: test_data,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Explicitly close the database
|
||||
db.close().unwrap();
|
||||
@ -166,7 +190,7 @@ fn test_persistence() {
|
||||
incremental_mode: true,
|
||||
file_size: None,
|
||||
keysize: None,
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
@ -198,14 +222,19 @@ fn test_different_keysizes() {
|
||||
incremental_mode: true,
|
||||
file_size: None,
|
||||
keysize: Some(*keysize),
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
|
||||
// Test basic operations
|
||||
let test_data = b"Keysize test data";
|
||||
let id = db.set(OurDBSetArgs { id: None, data: test_data }).unwrap();
|
||||
let id = db
|
||||
.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: test_data,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let retrieved = db.get(id).unwrap();
|
||||
assert_eq!(retrieved, test_data);
|
||||
@ -225,7 +254,7 @@ fn test_large_data() {
|
||||
incremental_mode: true,
|
||||
file_size: None,
|
||||
keysize: None,
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
@ -234,7 +263,12 @@ fn test_large_data() {
|
||||
let large_data = vec![b'X'; 60 * 1024];
|
||||
|
||||
// Store and retrieve large data
|
||||
let id = db.set(OurDBSetArgs { id: None, data: &large_data }).unwrap();
|
||||
let id = db
|
||||
.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: &large_data,
|
||||
})
|
||||
.unwrap();
|
||||
let retrieved = db.get(id).unwrap();
|
||||
|
||||
assert_eq!(retrieved.len(), large_data.len());
|
||||
@ -254,7 +288,7 @@ fn test_exceed_size_limit() {
|
||||
incremental_mode: true,
|
||||
file_size: None,
|
||||
keysize: None,
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
@ -263,10 +297,16 @@ fn test_exceed_size_limit() {
|
||||
let oversized_data = vec![b'X'; 70 * 1024];
|
||||
|
||||
// Attempt to store data that exceeds the size limit
|
||||
let result = db.set(OurDBSetArgs { id: None, data: &oversized_data });
|
||||
let result = db.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: &oversized_data,
|
||||
});
|
||||
|
||||
// Verify that an error is returned
|
||||
assert!(result.is_err(), "Expected an error when storing data larger than 64KB");
|
||||
assert!(
|
||||
result.is_err(),
|
||||
"Expected an error when storing data larger than 64KB"
|
||||
);
|
||||
|
||||
// Clean up
|
||||
db.destroy().unwrap();
|
||||
@ -276,14 +316,13 @@ fn test_exceed_size_limit() {
|
||||
fn test_multiple_files() {
|
||||
let temp_dir = get_temp_dir();
|
||||
|
||||
|
||||
// Create a new database with small file size to force multiple files
|
||||
let config = OurDBConfig {
|
||||
path: temp_dir.clone(),
|
||||
incremental_mode: true,
|
||||
file_size: Some(1024), // Very small file size (1KB)
|
||||
keysize: Some(6), // 6-byte keysize for multiple files
|
||||
reset: None
|
||||
reset: None,
|
||||
};
|
||||
|
||||
let mut db = OurDB::new(config).unwrap();
|
||||
@ -294,7 +333,12 @@ fn test_multiple_files() {
|
||||
|
||||
let mut ids = Vec::new();
|
||||
for _ in 0..10 {
|
||||
let id = db.set(OurDBSetArgs { id: None, data: &test_data }).unwrap();
|
||||
let id = db
|
||||
.set(OurDBSetArgs {
|
||||
id: None,
|
||||
data: &test_data,
|
||||
})
|
||||
.unwrap();
|
||||
ids.push(id);
|
||||
}
|
||||
|
||||
@ -305,7 +349,8 @@ fn test_multiple_files() {
|
||||
}
|
||||
|
||||
// Verify multiple files were created
|
||||
let files = fs::read_dir(&temp_dir).unwrap()
|
||||
let files = fs::read_dir(&temp_dir)
|
||||
.unwrap()
|
||||
.filter_map(Result::ok)
|
||||
.filter(|entry| {
|
||||
let path = entry.path();
|
||||
@ -313,7 +358,11 @@ fn test_multiple_files() {
|
||||
})
|
||||
.count();
|
||||
|
||||
assert!(files > 1, "Expected multiple database files, found {}", files);
|
||||
assert!(
|
||||
files > 1,
|
||||
"Expected multiple database files, found {}",
|
||||
files
|
||||
);
|
||||
|
||||
// Clean up
|
||||
db.destroy().unwrap();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, format_ident};
|
||||
use syn::{parse_macro_input, ItemFn, FnArg, Pat, PatType, ReturnType, parse_quote};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, parse_quote, FnArg, ItemFn, Pat, PatType, ReturnType};
|
||||
|
||||
/// Procedural macro that generates a Rhai client function for a Rust function.
|
||||
///
|
||||
@ -69,7 +69,10 @@ pub fn rhai(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
};
|
||||
|
||||
// Generate parameter formatting for the Rhai script
|
||||
let param_format_strings = param_names.iter().zip(param_types.iter()).map(|(name, ty)| {
|
||||
let param_format_strings = param_names
|
||||
.iter()
|
||||
.zip(param_types.iter())
|
||||
.map(|(name, ty)| {
|
||||
let type_str = quote! { #ty }.to_string();
|
||||
|
||||
// Handle different parameter types
|
||||
@ -86,7 +89,11 @@ pub fn rhai(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
quote! {
|
||||
format!("{}", #name as i64)
|
||||
}
|
||||
} else if type_str.contains("i64") || type_str.contains("u64") || type_str.contains("f32") || type_str.contains("f64") {
|
||||
} else if type_str.contains("i64")
|
||||
|| type_str.contains("u64")
|
||||
|| type_str.contains("f32")
|
||||
|| type_str.contains("f64")
|
||||
{
|
||||
// Other numeric types
|
||||
quote! {
|
||||
format!("{}", #name)
|
||||
@ -235,7 +242,10 @@ pub fn rhai_advanced(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
};
|
||||
|
||||
// Generate parameter formatting for the Rhai script
|
||||
let param_format_expressions = param_names.iter().zip(param_types.iter()).map(|(name, ty)| {
|
||||
let param_format_expressions = param_names
|
||||
.iter()
|
||||
.zip(param_types.iter())
|
||||
.map(|(name, ty)| {
|
||||
let type_str = quote! { #ty }.to_string();
|
||||
|
||||
// Handle different parameter types
|
||||
@ -263,11 +273,13 @@ pub fn rhai_advanced(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
format!("{:?}", #name)
|
||||
}
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Determine if the return type needs conversion
|
||||
let return_type_str = quote! { #return_type }.to_string();
|
||||
let needs_return_conversion = return_type_str.contains("i32") || return_type_str.contains("u32");
|
||||
let needs_return_conversion =
|
||||
return_type_str.contains("i32") || return_type_str.contains("u32");
|
||||
|
||||
// Generate the client function with appropriate type conversions
|
||||
let client_fn = if needs_return_conversion {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use tst::TST;
|
||||
use std::time::Instant;
|
||||
use tst::TST;
|
||||
|
||||
fn main() -> Result<(), tst::Error> {
|
||||
// Create a temporary directory for the database
|
||||
|
@ -1,6 +1,6 @@
|
||||
use tst::TST;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::io::{self, Write};
|
||||
use std::time::{Duration, Instant};
|
||||
use tst::TST;
|
||||
|
||||
// Function to generate a test value of specified size
|
||||
fn generate_test_value(index: usize, size: usize) -> Vec<u8> {
|
||||
@ -77,10 +77,13 @@ fn main() -> Result<(), tst::Error> {
|
||||
|
||||
insertion_times.push((i + 1, batch_duration));
|
||||
|
||||
print!("\rProgress: {}/{} records ({:.2}%) - {:.2} records/sec",
|
||||
i + 1, TOTAL_RECORDS,
|
||||
print!(
|
||||
"\rProgress: {}/{} records ({:.2}%) - {:.2} records/sec",
|
||||
i + 1,
|
||||
TOTAL_RECORDS,
|
||||
(i + 1) as f64 / TOTAL_RECORDS as f64 * 100.0,
|
||||
records_per_second);
|
||||
records_per_second
|
||||
);
|
||||
io::stdout().flush().unwrap();
|
||||
|
||||
last_batch_time = Instant::now();
|
||||
@ -90,19 +93,27 @@ fn main() -> Result<(), tst::Error> {
|
||||
|
||||
let total_duration = start_time.elapsed();
|
||||
println!("\n\nPerformance Summary:");
|
||||
println!("Total time to insert {} records: {:?}", TOTAL_RECORDS, total_duration);
|
||||
println!("Average insertion rate: {:.2} records/second",
|
||||
TOTAL_RECORDS as f64 / total_duration.as_secs_f64());
|
||||
println!(
|
||||
"Total time to insert {} records: {:?}",
|
||||
TOTAL_RECORDS, total_duration
|
||||
);
|
||||
println!(
|
||||
"Average insertion rate: {:.2} records/second",
|
||||
TOTAL_RECORDS as f64 / total_duration.as_secs_f64()
|
||||
);
|
||||
|
||||
// Show performance trend
|
||||
println!("\nPerformance Trend (records inserted vs. time per batch):");
|
||||
for (i, (record_count, duration)) in insertion_times.iter().enumerate() {
|
||||
if i % 10 == 0 || i == insertion_times.len() - 1 { // Only show every 10th point to avoid too much output
|
||||
println!(" After {} records: {:?} for {} records ({:.2} records/sec)",
|
||||
if i % 10 == 0 || i == insertion_times.len() - 1 {
|
||||
// Only show every 10th point to avoid too much output
|
||||
println!(
|
||||
" After {} records: {:?} for {} records ({:.2} records/sec)",
|
||||
record_count,
|
||||
duration,
|
||||
PROGRESS_INTERVAL,
|
||||
PROGRESS_INTERVAL as f64 / duration.as_secs_f64());
|
||||
PROGRESS_INTERVAL as f64 / duration.as_secs_f64()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,8 +133,10 @@ fn main() -> Result<(), tst::Error> {
|
||||
total_get_time += get_start.elapsed();
|
||||
}
|
||||
|
||||
println!("Average time to retrieve a record: {:?}",
|
||||
total_get_time / num_samples as u32);
|
||||
println!(
|
||||
"Average time to retrieve a record: {:?}",
|
||||
total_get_time / num_samples as u32
|
||||
);
|
||||
|
||||
// Test prefix search performance
|
||||
println!("\nTesting prefix search performance...");
|
||||
@ -134,8 +147,12 @@ fn main() -> Result<(), tst::Error> {
|
||||
let keys = tree.list(prefix)?;
|
||||
let list_duration = list_start.elapsed();
|
||||
|
||||
println!("Found {} keys with prefix '{}' in {:?}",
|
||||
keys.len(), prefix, list_duration);
|
||||
println!(
|
||||
"Found {} keys with prefix '{}' in {:?}",
|
||||
keys.len(),
|
||||
prefix,
|
||||
list_duration
|
||||
);
|
||||
}
|
||||
|
||||
// Clean up (optional)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use tst::TST;
|
||||
use std::time::Instant;
|
||||
use tst::TST;
|
||||
|
||||
fn main() -> Result<(), tst::Error> {
|
||||
// Create a temporary directory for the database
|
||||
@ -16,11 +16,31 @@ fn main() -> Result<(), tst::Error> {
|
||||
|
||||
// Names
|
||||
let names = [
|
||||
"Alice", "Alexander", "Amanda", "Andrew", "Amy",
|
||||
"Bob", "Barbara", "Benjamin", "Brenda", "Brian",
|
||||
"Charlie", "Catherine", "Christopher", "Cynthia", "Carl",
|
||||
"David", "Diana", "Daniel", "Deborah", "Donald",
|
||||
"Edward", "Elizabeth", "Eric", "Emily", "Ethan"
|
||||
"Alice",
|
||||
"Alexander",
|
||||
"Amanda",
|
||||
"Andrew",
|
||||
"Amy",
|
||||
"Bob",
|
||||
"Barbara",
|
||||
"Benjamin",
|
||||
"Brenda",
|
||||
"Brian",
|
||||
"Charlie",
|
||||
"Catherine",
|
||||
"Christopher",
|
||||
"Cynthia",
|
||||
"Carl",
|
||||
"David",
|
||||
"Diana",
|
||||
"Daniel",
|
||||
"Deborah",
|
||||
"Donald",
|
||||
"Edward",
|
||||
"Elizabeth",
|
||||
"Eric",
|
||||
"Emily",
|
||||
"Ethan",
|
||||
];
|
||||
|
||||
for (i, name) in names.iter().enumerate() {
|
||||
@ -30,10 +50,26 @@ fn main() -> Result<(), tst::Error> {
|
||||
|
||||
// Cities
|
||||
let cities = [
|
||||
"New York", "Los Angeles", "Chicago", "Houston", "Phoenix",
|
||||
"Philadelphia", "San Antonio", "San Diego", "Dallas", "San Jose",
|
||||
"Austin", "Jacksonville", "Fort Worth", "Columbus", "San Francisco",
|
||||
"Charlotte", "Indianapolis", "Seattle", "Denver", "Washington"
|
||||
"New York",
|
||||
"Los Angeles",
|
||||
"Chicago",
|
||||
"Houston",
|
||||
"Phoenix",
|
||||
"Philadelphia",
|
||||
"San Antonio",
|
||||
"San Diego",
|
||||
"Dallas",
|
||||
"San Jose",
|
||||
"Austin",
|
||||
"Jacksonville",
|
||||
"Fort Worth",
|
||||
"Columbus",
|
||||
"San Francisco",
|
||||
"Charlotte",
|
||||
"Indianapolis",
|
||||
"Seattle",
|
||||
"Denver",
|
||||
"Washington",
|
||||
];
|
||||
|
||||
for (i, city) in cities.iter().enumerate() {
|
||||
@ -43,9 +79,21 @@ fn main() -> Result<(), tst::Error> {
|
||||
|
||||
// Countries
|
||||
let countries = [
|
||||
"United States", "Canada", "Mexico", "Brazil", "Argentina",
|
||||
"United Kingdom", "France", "Germany", "Italy", "Spain",
|
||||
"China", "Japan", "India", "Australia", "Russia"
|
||||
"United States",
|
||||
"Canada",
|
||||
"Mexico",
|
||||
"Brazil",
|
||||
"Argentina",
|
||||
"United Kingdom",
|
||||
"France",
|
||||
"Germany",
|
||||
"Italy",
|
||||
"Spain",
|
||||
"China",
|
||||
"Japan",
|
||||
"India",
|
||||
"Australia",
|
||||
"Russia",
|
||||
];
|
||||
|
||||
for (i, country) in countries.iter().enumerate() {
|
||||
@ -53,7 +101,10 @@ fn main() -> Result<(), tst::Error> {
|
||||
tree.set(country, value)?;
|
||||
}
|
||||
|
||||
println!("Total items inserted: {}", names.len() + cities.len() + countries.len());
|
||||
println!(
|
||||
"Total items inserted: {}",
|
||||
names.len() + cities.len() + countries.len()
|
||||
);
|
||||
|
||||
// Test prefix operations
|
||||
test_prefix(&mut tree, "A")?;
|
||||
@ -71,7 +122,11 @@ fn main() -> Result<(), tst::Error> {
|
||||
let all_keys = tree.list("")?;
|
||||
let duration = start.elapsed();
|
||||
|
||||
println!("Found {} keys with empty prefix in {:?}", all_keys.len(), duration);
|
||||
println!(
|
||||
"Found {} keys with empty prefix in {:?}",
|
||||
all_keys.len(),
|
||||
duration
|
||||
);
|
||||
println!("First 5 keys (alphabetically):");
|
||||
for key in all_keys.iter().take(5) {
|
||||
println!(" {}", key);
|
||||
@ -96,7 +151,12 @@ fn test_prefix(tree: &mut TST, prefix: &str) -> Result<(), tst::Error> {
|
||||
let keys = tree.list(prefix)?;
|
||||
let list_duration = start.elapsed();
|
||||
|
||||
println!("Found {} keys with prefix '{}' in {:?}", keys.len(), prefix, list_duration);
|
||||
println!(
|
||||
"Found {} keys with prefix '{}' in {:?}",
|
||||
keys.len(),
|
||||
prefix,
|
||||
list_duration
|
||||
);
|
||||
|
||||
if !keys.is_empty() {
|
||||
println!("Keys:");
|
||||
@ -110,12 +170,14 @@ fn test_prefix(tree: &mut TST, prefix: &str) -> Result<(), tst::Error> {
|
||||
let getall_duration = start.elapsed();
|
||||
|
||||
println!("Retrieved {} values in {:?}", values.len(), getall_duration);
|
||||
println!("First value: {}",
|
||||
println!(
|
||||
"First value: {}",
|
||||
if !values.is_empty() {
|
||||
String::from_utf8_lossy(&values[0])
|
||||
} else {
|
||||
"None".into()
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Error types for the TST module.
|
||||
|
||||
use thiserror::Error;
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Error type for TST operations.
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -36,10 +36,7 @@ pub fn new_tst(path: &str, reset: bool) -> Result<TST, Error> {
|
||||
Some(1) // Root node always has ID 1
|
||||
};
|
||||
|
||||
Ok(TST {
|
||||
db,
|
||||
root_id,
|
||||
})
|
||||
Ok(TST { db, root_id })
|
||||
}
|
||||
|
||||
/// Sets a key-value pair in the tree.
|
||||
@ -60,7 +57,13 @@ pub fn set(tree: &mut TST, key: &str, value: Vec<u8>) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
/// Recursive helper function for setting a key-value pair.
|
||||
fn set_recursive(tree: &mut TST, node_id: u32, chars: &[char], pos: usize, value: Vec<u8>) -> Result<u32, Error> {
|
||||
fn set_recursive(
|
||||
tree: &mut TST,
|
||||
node_id: u32,
|
||||
chars: &[char],
|
||||
pos: usize,
|
||||
value: Vec<u8>,
|
||||
) -> Result<u32, Error> {
|
||||
let mut node = tree.get_node(node_id)?;
|
||||
|
||||
if pos >= chars.len() {
|
||||
@ -275,7 +278,7 @@ pub fn list(tree: &mut TST, prefix: &str) -> Result<Vec<String>, Error> {
|
||||
// For non-empty prefix, we start with the prefix minus the last character
|
||||
// (since the last character is in the node we found)
|
||||
let prefix_base = if chars.len() > 1 {
|
||||
chars[0..chars.len()-1].iter().collect()
|
||||
chars[0..chars.len() - 1].iter().collect()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
@ -287,7 +290,12 @@ pub fn list(tree: &mut TST, prefix: &str) -> Result<Vec<String>, Error> {
|
||||
}
|
||||
|
||||
/// Finds the node corresponding to a prefix.
|
||||
fn find_prefix_node(tree: &mut TST, node_id: u32, chars: &[char], pos: usize) -> Result<u32, Error> {
|
||||
fn find_prefix_node(
|
||||
tree: &mut TST,
|
||||
node_id: u32,
|
||||
chars: &[char],
|
||||
pos: usize,
|
||||
) -> Result<u32, Error> {
|
||||
if pos >= chars.len() {
|
||||
return Ok(node_id);
|
||||
}
|
||||
@ -409,7 +417,7 @@ pub fn getall(tree: &mut TST, prefix: &str) -> Result<Vec<Vec<u8>>, Error> {
|
||||
for key in keys {
|
||||
match get(tree, &key) {
|
||||
Ok(value) => values.push(value),
|
||||
Err(e) => errors.push(format!("Error getting value for key '{}': {:?}", key, e))
|
||||
Err(e) => errors.push(format!("Error getting value for key '{}': {:?}", key, e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,8 @@ impl TSTNode {
|
||||
|
||||
/// Deserializes bytes to a node.
|
||||
pub fn deserialize(data: &[u8]) -> Result<Self, Error> {
|
||||
if data.len() < 14 { // Minimum size: version + char + is_end + value_len + 3 child IDs
|
||||
if data.len() < 14 {
|
||||
// Minimum size: version + char + is_end + value_len + 3 child IDs
|
||||
return Err(Error::Deserialization("Data too short".to_string()));
|
||||
}
|
||||
|
||||
@ -57,11 +58,14 @@ impl TSTNode {
|
||||
pos += 1;
|
||||
|
||||
if version != VERSION {
|
||||
return Err(Error::Deserialization(format!("Unsupported version: {}", version)));
|
||||
return Err(Error::Deserialization(format!(
|
||||
"Unsupported version: {}",
|
||||
version
|
||||
)));
|
||||
}
|
||||
|
||||
// Character
|
||||
let char_bytes = [data[pos], data[pos+1], data[pos+2], data[pos+3]];
|
||||
let char_bytes = [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]];
|
||||
let char_code = u32::from_le_bytes(char_bytes);
|
||||
let character = char::from_u32(char_code)
|
||||
.ok_or_else(|| Error::Deserialization("Invalid character".to_string()))?;
|
||||
@ -72,16 +76,18 @@ impl TSTNode {
|
||||
pos += 1;
|
||||
|
||||
// Value length
|
||||
let value_len_bytes = [data[pos], data[pos+1], data[pos+2], data[pos+3]];
|
||||
let value_len_bytes = [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]];
|
||||
let value_len = u32::from_le_bytes(value_len_bytes) as usize;
|
||||
pos += 4;
|
||||
|
||||
// Value
|
||||
let value = if value_len > 0 {
|
||||
if pos + value_len > data.len() {
|
||||
return Err(Error::Deserialization("Value length exceeds data".to_string()));
|
||||
return Err(Error::Deserialization(
|
||||
"Value length exceeds data".to_string(),
|
||||
));
|
||||
}
|
||||
data[pos..pos+value_len].to_vec()
|
||||
data[pos..pos + value_len].to_vec()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
@ -89,18 +95,20 @@ impl TSTNode {
|
||||
|
||||
// Child pointers
|
||||
if pos + 12 > data.len() {
|
||||
return Err(Error::Deserialization("Data too short for child pointers".to_string()));
|
||||
return Err(Error::Deserialization(
|
||||
"Data too short for child pointers".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let left_id_bytes = [data[pos], data[pos+1], data[pos+2], data[pos+3]];
|
||||
let left_id_bytes = [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]];
|
||||
let left_id = u32::from_le_bytes(left_id_bytes);
|
||||
pos += 4;
|
||||
|
||||
let middle_id_bytes = [data[pos], data[pos+1], data[pos+2], data[pos+3]];
|
||||
let middle_id_bytes = [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]];
|
||||
let middle_id = u32::from_le_bytes(middle_id_bytes);
|
||||
pos += 4;
|
||||
|
||||
let right_id_bytes = [data[pos], data[pos+1], data[pos+2], data[pos+3]];
|
||||
let right_id_bytes = [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]];
|
||||
let right_id = u32::from_le_bytes(right_id_bytes);
|
||||
|
||||
Ok(TSTNode {
|
||||
@ -108,7 +116,11 @@ impl TSTNode {
|
||||
value,
|
||||
is_end_of_key,
|
||||
left_id: if left_id == 0 { None } else { Some(left_id) },
|
||||
middle_id: if middle_id == 0 { None } else { Some(middle_id) },
|
||||
middle_id: if middle_id == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(middle_id)
|
||||
},
|
||||
right_id: if right_id == 0 { None } else { Some(right_id) },
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use tst::TST;
|
||||
use std::env::temp_dir;
|
||||
use std::fs;
|
||||
use std::time::SystemTime;
|
||||
use tst::TST;
|
||||
|
||||
fn get_test_db_path() -> String {
|
||||
let timestamp = SystemTime::now()
|
||||
@ -166,10 +166,7 @@ fn test_list_prefix() {
|
||||
let mut tree = result.unwrap();
|
||||
|
||||
// Insert keys with common prefixes - use fewer keys to avoid filling the lookup table
|
||||
let keys = [
|
||||
"apple", "application", "append",
|
||||
"banana", "bandana"
|
||||
];
|
||||
let keys = ["apple", "application", "append", "banana", "bandana"];
|
||||
|
||||
for key in &keys {
|
||||
let set_result = tree.set(key, key.as_bytes().to_vec());
|
||||
@ -223,9 +220,7 @@ fn test_getall_prefix() {
|
||||
let mut tree = result.unwrap();
|
||||
|
||||
// Insert keys with common prefixes - use fewer keys to avoid filling the lookup table
|
||||
let keys = [
|
||||
"apple", "application", "append"
|
||||
];
|
||||
let keys = ["apple", "application", "append"];
|
||||
|
||||
for key in &keys {
|
||||
let set_result = tree.set(key, key.as_bytes().to_vec());
|
||||
|
@ -1,7 +1,7 @@
|
||||
use tst::TST;
|
||||
use std::env::temp_dir;
|
||||
use std::fs;
|
||||
use std::time::SystemTime;
|
||||
use tst::TST;
|
||||
|
||||
fn get_test_db_path() -> String {
|
||||
let timestamp = SystemTime::now()
|
||||
@ -232,9 +232,18 @@ fn test_prefix_with_long_keys() {
|
||||
|
||||
// Insert long keys
|
||||
let test_data = [
|
||||
("this_is_a_very_long_key_for_testing_purposes_1", b"value1".to_vec()),
|
||||
("this_is_a_very_long_key_for_testing_purposes_2", b"value2".to_vec()),
|
||||
("this_is_a_very_long_key_for_testing_purposes_3", b"value3".to_vec()),
|
||||
(
|
||||
"this_is_a_very_long_key_for_testing_purposes_1",
|
||||
b"value1".to_vec(),
|
||||
),
|
||||
(
|
||||
"this_is_a_very_long_key_for_testing_purposes_2",
|
||||
b"value2".to_vec(),
|
||||
),
|
||||
(
|
||||
"this_is_a_very_long_key_for_testing_purposes_3",
|
||||
b"value3".to_vec(),
|
||||
),
|
||||
("this_is_another_long_key_for_testing", b"value4".to_vec()),
|
||||
];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user