use chrono::{Duration, Utc}; use heromodels::db::hero::OurDB; use heromodels::models::governance::{ Ballot, Proposal, ProposalStatus, VoteEventStatus, VoteOption, }; use rhai::Engine; use rhai_wrapper::wrap_vec_return; use std::sync::Arc; use std::{fs, path::Path}; fn main() -> Result<(), Box> { // Initialize Rhai engine let mut engine = Engine::new(); // Initialize database let db = Arc::new(OurDB::new("temp_governance_db", true).expect("Failed to create database")); // Register the Proposal type with Rhai // This function is generated by the #[rhai_model_export] attribute Proposal::register_rhai_bindings_for_proposal(&mut engine, db.clone()); // Register the Ballot type with Rhai Ballot::register_rhai_bindings_for_ballot(&mut engine, db.clone()); // Register a function to get the database instance engine.register_fn("get_db", move || db.clone()); // Register builder functions for Proposal and related types engine.register_fn( "create_proposal", |id: i64, creator_id: String, creator_name: String, title: String, description: String| { let start_date = Utc::now(); let end_date = start_date + Duration::days(14); let id_option = if id <= 0 { None } else { Some(id as u32) }; Proposal::new( id_option, creator_id, creator_name, title, description, ProposalStatus::Draft, Utc::now(), Utc::now(), start_date, end_date, ) }, ); engine.register_fn("create_vote_option", |id: i64, text: String| { VoteOption::new(id as u8, text, Some("This is an optional comment")) }); engine.register_fn( "create_ballot", |id: i64, user_id: i64, vote_option_id: i64, shares_count: i64| { let id_option = if id <= 0 { None } else { Some(id as u32) }; Ballot::new( id_option, user_id as u32, vote_option_id as u8, shares_count, ) }, ); // Register getter and setter methods for Proposal properties engine.register_fn("get_title", |proposal: Proposal| -> String { proposal.title.clone() }); engine.register_fn("get_description", |proposal: Proposal| -> String { proposal.description.clone() }); engine.register_fn("get_creator_id", |proposal: Proposal| -> String { proposal.creator_id.clone() }); engine.register_fn("get_id", |proposal: Proposal| -> i64 { proposal.base_data.id as i64 }); engine.register_fn("get_status", |proposal: Proposal| -> String { format!("{:?}", proposal.status) }); engine.register_fn("get_vote_status", |proposal: Proposal| -> String { format!("{:?}", proposal.vote_status) }); // Register methods for proposal operations engine.register_fn( "add_option_to_proposal", |mut proposal: Proposal, option_id: i64, option_text: String| -> Proposal { proposal.add_option( option_id as u8, option_text, Some("This is an optional comment".to_string()), ) }, ); engine.register_fn( "cast_vote_on_proposal", |mut proposal: Proposal, ballot_id: i64, user_id: i64, option_id: i64, shares: i64| -> Proposal { let ballot_id_option = if ballot_id <= 0 { None } else { Some(ballot_id as u32) }; proposal.cast_vote(ballot_id_option, user_id as u32, option_id as u8, shares) }, ); engine.register_fn( "change_proposal_status", |mut proposal: Proposal, status_str: String| -> Proposal { let new_status = match status_str.as_str() { "Draft" => ProposalStatus::Draft, "Active" => ProposalStatus::Active, "Approved" => ProposalStatus::Approved, "Rejected" => ProposalStatus::Rejected, "Cancelled" => ProposalStatus::Cancelled, _ => ProposalStatus::Draft, }; proposal.change_proposal_status(new_status) }, ); engine.register_fn( "change_vote_event_status", |mut proposal: Proposal, status_str: String| -> Proposal { let new_status = match status_str.as_str() { "Open" => VoteEventStatus::Open, "Closed" => VoteEventStatus::Closed, "Cancelled" => VoteEventStatus::Cancelled, _ => VoteEventStatus::Open, }; proposal.change_vote_event_status(new_status) }, ); // Register functions for database operations engine.register_fn("save_proposal", |_db: Arc, proposal: Proposal| { println!("Proposal saved: {}", proposal.title); }); engine.register_fn( "get_proposal_by_id", |_db: Arc, id: i64| -> Proposal { // In a real implementation, this would retrieve the proposal from the database let start_date = Utc::now(); let end_date = start_date + Duration::days(14); Proposal::new( Some(id as u32), "Retrieved Creator", "Retrieved Creator Name", "Retrieved Proposal", "Retrieved Description", ProposalStatus::Draft, Utc::now(), Utc::now(), start_date, end_date, ) }, ); // Register a function to check if a proposal exists engine.register_fn("proposal_exists", |_db: Arc, id: i64| -> bool { // In a real implementation, this would check if the proposal exists in the database id == 1 || id == 2 }); // Define the function for get_all_proposals fn get_all_proposals(_db: Arc) -> Vec { // In a real implementation, this would retrieve all proposals from the database let start_date = Utc::now(); let end_date = start_date + Duration::days(14); vec![ Proposal::new( Some(1), "Creator 1", "Creator Name 1", "Proposal 1", "Description 1", ProposalStatus::Draft, Utc::now(), Utc::now(), start_date, end_date, ), Proposal::new( Some(2), "Creator 2", "Creator Name 2", "Proposal 2", "Description 2", ProposalStatus::Draft, Utc::now(), Utc::now(), start_date, end_date, ), ] } // Register the function with the wrap_vec_return macro engine.register_fn( "get_all_proposals", wrap_vec_return!(get_all_proposals, Arc => Proposal), ); engine.register_fn("delete_proposal_by_id", |_db: Arc, _id: i64| { // In a real implementation, this would delete the proposal from the database println!("Proposal deleted with ID: {}", _id); }); // Register helper functions for accessing proposal options and ballots engine.register_fn("get_option_count", |proposal: Proposal| -> i64 { proposal.options.len() as i64 }); engine.register_fn( "get_option_at", |proposal: Proposal, index: i64| -> VoteOption { if index >= 0 && index < proposal.options.len() as i64 { proposal.options[index as usize].clone() } else { VoteOption::new( 0, "Invalid Option", Some("This is an invalid option".to_string()), ) } }, ); engine.register_fn("get_option_text", |option: VoteOption| -> String { option.text.clone() }); engine.register_fn("get_option_votes", |option: VoteOption| -> i64 { option.count }); engine.register_fn("get_ballot_count", |proposal: Proposal| -> i64 { proposal.ballots.len() as i64 }); engine.register_fn( "get_ballot_at", |proposal: Proposal, index: i64| -> Ballot { if index >= 0 && index < proposal.ballots.len() as i64 { proposal.ballots[index as usize].clone() } else { Ballot::new(None, 0, 0, 0) } }, ); engine.register_fn("get_ballot_user_id", |ballot: Ballot| -> i64 { ballot.user_id as i64 }); engine.register_fn("get_ballot_option_id", |ballot: Ballot| -> i64 { ballot.vote_option_id as i64 }); engine.register_fn("get_ballot_shares", |ballot: Ballot| -> i64 { ballot.shares_count }); // Load and evaluate the Rhai script let script_path = Path::new("examples/governance_rhai/governance.rhai"); let script = fs::read_to_string(script_path)?; match engine.eval::<()>(&script) { Ok(_) => println!("Script executed successfully!"), Err(e) => eprintln!("Script execution failed: {}", e), } Ok(()) }