use heromodels::db::hero::OurDB; use heromodels::models::governance::{Proposal, ProposalStatus, VoteEventStatus, VoteOption, Ballot}; use rhai::Engine; 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 // Create a proposal (returns a complex type, but takes simple parameters) fn create_proposal(id: i64, creator_id: String, title: String, description: String) -> Proposal { let start_date = Utc::now(); let end_date = start_date + Duration::days(14); Proposal::new(id as u32, creator_id, title, description, start_date, end_date) } // Getter functions for Proposal properties fn get_title(proposal: &Proposal) -> String { proposal.title.clone() } fn get_description(proposal: &Proposal) -> String { proposal.description.clone() } fn get_creator_id(proposal: &Proposal) -> String { proposal.creator_id.clone() } fn get_id(proposal: &Proposal) -> i64 { proposal.base_data.id as i64 } fn get_status(proposal: &Proposal) -> String { format!("{:?}", proposal.status) } fn get_vote_status(proposal: &Proposal) -> String { format!("{:?}", proposal.vote_status) } // Functions that operate on Proposal objects fn add_option_to_proposal(proposal: Proposal, option_id: i64, option_text: String) -> Proposal { proposal.add_option(option_id as u8, option_text) } fn cast_vote_on_proposal(proposal: Proposal, ballot_id: i64, user_id: i64, option_id: i64, shares: i64) -> Proposal { proposal.cast_vote(ballot_id as u32, user_id as u32, option_id as u8, shares) } fn change_proposal_status(proposal: Proposal, status_str: String) -> Proposal { let new_status = match status_str.as_str() { "Draft" => ProposalStatus::Draft, "Active" => ProposalStatus::Active, "Approved" => ProposalStatus::Approved, "Rejected" => ProposalStatus::Rejected, "Cancelled" => ProposalStatus::Cancelled, _ => ProposalStatus::Draft, }; proposal.change_proposal_status(new_status) } fn change_vote_event_status(proposal: Proposal, status_str: String) -> Proposal { let new_status = match status_str.as_str() { "Open" => VoteEventStatus::Open, "Closed" => VoteEventStatus::Closed, "Cancelled" => VoteEventStatus::Cancelled, _ => VoteEventStatus::Open, }; proposal.change_vote_event_status(new_status) } // Functions for accessing proposal options and ballots fn get_option_count(proposal: &Proposal) -> i64 { proposal.options.len() as i64 } fn get_option_at(proposal: &Proposal, index: i64) -> VoteOption { if index >= 0 && index < proposal.options.len() as i64 { proposal.options[index as usize].clone() } else { VoteOption::new(0, "Invalid Option") } } fn get_option_text(option: &VoteOption) -> String { option.text.clone() } fn get_option_votes(option: &VoteOption) -> i64 { option.count } fn get_ballot_count(proposal: &Proposal) -> i64 { proposal.ballots.len() as i64 } fn get_ballot_at(proposal: &Proposal, index: i64) -> Ballot { if index >= 0 && index < proposal.ballots.len() as i64 { proposal.ballots[index as usize].clone() } else { Ballot::new(0, 0, 0, 0) } } fn get_ballot_user_id(ballot: &Ballot) -> i64 { ballot.user_id as i64 } fn get_ballot_option_id(ballot: &Ballot) -> i64 { ballot.vote_option_id as i64 } fn get_ballot_shares(ballot: &Ballot) -> i64 { ballot.shares_count } // Simple functions that we can use with the #[rhai] attribute #[rhai] fn create_proposal_wrapper(id: i64, creator_id: String, title: String, description: String) -> String { let proposal = create_proposal(id, creator_id, title, description); format!("Created proposal with ID: {}", proposal.base_data.id) } #[rhai] fn add_option_wrapper(id: i64, option_id: i64, option_text: String) -> String { let proposal = create_proposal(id, "user".to_string(), "title".to_string(), "description".to_string()); let updated = add_option_to_proposal(proposal, option_id, option_text.clone()); format!("Added option '{}' to proposal {}", option_text, id) } // Database operations fn save_proposal(_db: Arc, proposal: Proposal) { println!("Proposal saved: {}", proposal.title); } fn get_all_proposals(_db: Arc) -> Vec { // In a real implementation, this would retrieve all proposals from the database let start_date = Utc::now(); let end_date = start_date + Duration::days(14); vec![ Proposal::new(1, "Creator 1", "Proposal 1", "Description 1", start_date, end_date), Proposal::new(2, "Creator 2", "Proposal 2", "Description 2", start_date, end_date) ] } fn delete_proposal_by_id(_db: Arc, id: i64) { // In a real implementation, this would delete the proposal from the database println!("Proposal deleted with ID: {}", id); } fn main() -> Result<(), Box> { // Initialize Rhai engine let mut engine = Engine::new(); // Initialize database let db = Arc::new(OurDB::new("temp_governance_db", true).expect("Failed to create database")); // Register the Proposal type with Rhai // This function is generated by the #[rhai_model_export] attribute Proposal::register_rhai_bindings_for_proposal(&mut engine, db.clone()); // Register the Ballot type with Rhai Ballot::register_rhai_bindings_for_ballot(&mut engine, db.clone()); // Create a clone of db for use in the get_db function let db_for_get_db = db.clone(); // Register a function to get the database instance engine.register_fn("get_db", move || db_for_get_db.clone()); // Register builder functions for Proposal and related types engine.register_fn("create_proposal", |id: i64, creator_id: String, title: String, description: String| { let start_date = Utc::now(); let end_date = start_date + Duration::days(14); Proposal::new(id as u32, creator_id, title, description, start_date, end_date) }); engine.register_fn("create_vote_option", |id: i64, text: String| { VoteOption::new(id as u8, text) }); engine.register_fn("create_ballot", |id: i64, user_id: i64, vote_option_id: i64, shares_count: i64| { Ballot::new(id as u32, user_id as u32, vote_option_id as u8, shares_count) }); // Register getter and setter methods for Proposal properties engine.register_fn("get_title", get_title); engine.register_fn("get_description", get_description); engine.register_fn("get_creator_id", get_creator_id); engine.register_fn("get_id", get_id); engine.register_fn("get_status", get_status); engine.register_fn("get_vote_status", get_vote_status); // Register methods for proposal operations engine.register_fn("add_option_to_proposal", add_option_to_proposal); engine.register_fn("cast_vote_on_proposal", cast_vote_on_proposal); engine.register_fn("change_proposal_status", change_proposal_status); engine.register_fn("change_vote_event_status", change_vote_event_status); // Register functions for database operations engine.register_fn("save_proposal", save_proposal); engine.register_fn("get_proposal_by_id", |_db: Arc, id: i64| -> Proposal { // In a real implementation, this would retrieve the proposal from the database let start_date = Utc::now(); let end_date = start_date + Duration::days(14); Proposal::new(id as u32, "Retrieved Creator", "Retrieved Proposal", "Retrieved Description", start_date, end_date) }); // Register a function to check if a proposal exists engine.register_fn("proposal_exists", |_db: Arc, id: i64| -> bool { // In a real implementation, this would check if the proposal exists in the database id == 1 || id == 2 }); // Register the function with the wrap_vec_return macro engine.register_fn("get_all_proposals", wrap_vec_return!(get_all_proposals, Arc => Proposal)); engine.register_fn("delete_proposal_by_id", delete_proposal_by_id); // Register helper functions for accessing proposal options and ballots engine.register_fn("get_option_count", get_option_count); engine.register_fn("get_option_at", get_option_at); engine.register_fn("get_option_text", get_option_text); engine.register_fn("get_option_votes", get_option_votes); engine.register_fn("get_ballot_count", get_ballot_count); engine.register_fn("get_ballot_at", get_ballot_at); engine.register_fn("get_ballot_user_id", get_ballot_user_id); engine.register_fn("get_ballot_option_id", get_ballot_option_id); engine.register_fn("get_ballot_shares", get_ballot_shares); // Register our wrapper functions that use the #[rhai] attribute engine.register_fn("create_proposal_wrapper", create_proposal_wrapper); engine.register_fn("add_option_wrapper", add_option_wrapper); // Now instead of loading and evaluating a Rhai script, we'll use direct function calls // to implement the same functionality // Use the database instance // Create a new proposal let proposal = create_proposal(1, "user_creator_123".to_string(), "Community Fund Allocation for Q3".to_string(), "Proposal to allocate funds for community projects in the third quarter.".to_string()); println!("Created Proposal: '{}' (ID: {})", get_title(&proposal), get_id(&proposal)); println!("Status: {}, Vote Status: {}", get_status(&proposal), get_vote_status(&proposal)); // Add vote options let mut proposal_with_options = add_option_to_proposal( proposal, 1, "Approve Allocation".to_string()); proposal_with_options = add_option_to_proposal( proposal_with_options, 2, "Reject Allocation".to_string()); proposal_with_options = add_option_to_proposal( proposal_with_options, 3, "Abstain".to_string()); println!("\nAdded Vote Options:"); let option_count = get_option_count(&proposal_with_options); for i in 0..option_count { let option = get_option_at(&proposal_with_options, i); println!("- Option ID: {}, Text: '{}', Votes: {}", i, get_option_text(&option), get_option_votes(&option)); } // Save the proposal to the database save_proposal(db.clone(), proposal_with_options.clone()); println!("\nProposal saved to database"); // Simulate casting votes println!("\nSimulating Votes..."); // User 1 votes for 'Approve Allocation' with 100 shares let mut proposal_with_votes = cast_vote_on_proposal( proposal_with_options, 101, 1, 1, 100); // User 2 votes for 'Reject Allocation' with 50 shares proposal_with_votes = cast_vote_on_proposal( proposal_with_votes, 102, 2, 2, 50); // User 3 votes for 'Approve Allocation' with 75 shares proposal_with_votes = cast_vote_on_proposal( proposal_with_votes, 103, 3, 1, 75); // User 4 abstains with 20 shares proposal_with_votes = cast_vote_on_proposal( proposal_with_votes, 104, 4, 3, 20); println!("\nVote Counts After Simulation:"); let option_count = get_option_count(&proposal_with_votes); for i in 0..option_count { let option = get_option_at(&proposal_with_votes, i); println!("- Option ID: {}, Text: '{}', Votes: {}", i, get_option_text(&option), get_option_votes(&option)); } println!("\nBallots Cast:"); let ballot_count = get_ballot_count(&proposal_with_votes); for i in 0..ballot_count { let ballot = get_ballot_at(&proposal_with_votes, i); println!("- Ballot ID: {}, User ID: {}, Option ID: {}, Shares: {}", i, get_ballot_user_id(&ballot), get_ballot_option_id(&ballot), get_ballot_shares(&ballot)); } // Change proposal status let active_proposal = change_proposal_status( proposal_with_votes, "Active".to_string()); println!("\nChanged Proposal Status to: {}", get_status(&active_proposal)); // Simulate closing the vote let closed_proposal = change_vote_event_status( active_proposal, "Closed".to_string()); println!("Changed Vote Event Status to: {}", get_vote_status(&closed_proposal)); // Final proposal state println!("\nFinal Proposal State:"); println!("Title: '{}'", get_title(&closed_proposal)); println!("Status: {}", get_status(&closed_proposal)); println!("Vote Status: {}", get_vote_status(&closed_proposal)); println!("Options:"); let option_count = get_option_count(&closed_proposal); for i in 0..option_count { let option = get_option_at(&closed_proposal, i); println!(" - {}: {} (Votes: {})", i, get_option_text(&option), get_option_votes(&option)); } println!("Total Ballots: {}", get_ballot_count(&closed_proposal)); // Get all proposals from the database let all_proposals = get_all_proposals(db.clone()); println!("\nTotal Proposals in Database: {}", all_proposals.len()); for proposal in all_proposals { println!("Proposal ID: {}, Title: '{}'", get_id(&proposal), get_title(&proposal)); } // Delete a proposal delete_proposal_by_id(db.clone(), 1); println!("\nDeleted proposal with ID 1"); // Demonstrate the use of Rhai client functions for simple types println!("\nUsing Rhai client functions for simple types:"); let create_result = create_proposal_wrapper_rhai_client(&engine, 2, "rhai_user".to_string(), "Rhai Proposal".to_string(), "This proposal was created using a Rhai client function".to_string()); println!("{}", create_result); let add_option_result = add_option_wrapper_rhai_client(&engine, 2, 4, "Rhai Option".to_string()); println!("{}", add_option_result); println!("\nGovernance Proposal Example Finished."); Ok(()) }