// heromodels/examples/governance_proposal_example/main.rs use chrono::{Duration, Utc}; use heromodels::db::{Collection, Db}; use heromodels::models::governance::{Ballot, Proposal, ProposalStatus, VoteEventStatus}; fn main() { println!("Governance Proposal Model Example\n"); // Create a new DB instance, reset before every run let db_path = "/tmp/ourdb_governance_proposal_example"; let db = heromodels::db::hero::OurDB::new(db_path, true).expect("Can create DB"); // Create a new proposal with auto-generated ID let mut proposal = Proposal::new( None, // id (auto-generated) "user_creator_123", "Ahmed fared", // creator_id "Community Fund Allocation for Q3", // title "Proposal to allocate funds for community projects in the third quarter.", // description ProposalStatus::Draft, Utc::now(), // created_at Utc::now(), // updated_at Utc::now(), // vote_start_date Utc::now() + Duration::days(14), // vote_end_date (14 days from now) ); println!( "Before saving - Created Proposal: '{}' (ID: {})", proposal.title, proposal.base_data.id ); println!( "Before saving - Status: {:?}, Vote Status: {:?}", proposal.status, proposal.vote_status ); println!( "Before saving - Vote Period: {} to {}\n", proposal.vote_start_date, proposal.vote_end_date ); // Add vote options proposal = proposal.add_option(1, "Approve Allocation", Some("This is the approval option")); proposal = proposal.add_option(2, "Reject Allocation", Some("This is the rejection option")); proposal = proposal.add_option(3, "Abstain", Some("This is the abstain option")); println!("Added Vote Options:"); for option in &proposal.options { println!( "- Option ID: {}, Text: '{}', Votes: {}", option.id, option.text, option.count ); } println!(""); // Save the proposal to the database let collection = db .collection::() .expect("can open proposal collection"); let (proposal_id, saved_proposal) = collection.set(&proposal).expect("can save proposal"); println!( "After saving - Proposal ID: {}", saved_proposal.base_data.id ); println!("After saving - Returned ID: {}", proposal_id); // Use the saved proposal for further operations proposal = saved_proposal; // Simulate casting votes println!("Simulating Votes..."); // User 1 votes for 'Approve Allocation' with 100 shares (with explicit ballot ID) proposal = proposal.cast_vote(Some(101), 1, 1, 100); // User 2 votes for 'Reject Allocation' with 50 shares (with explicit ballot ID) proposal = proposal.cast_vote(Some(102), 2, 2, 50); // User 3 votes for 'Approve Allocation' with 75 shares (with auto-generated ballot ID) proposal = proposal.cast_vote(None, 3, 1, 75); // User 4 abstains with 20 shares (with auto-generated ballot ID) proposal = proposal.cast_vote(None, 4, 3, 20); // User 5 attempts to vote for a non-existent option (should be handled gracefully) proposal = proposal.cast_vote(Some(105), 5, 99, 10); // User 1 tries to vote again (not explicitly prevented by current model, but could be a future enhancement) // proposal = proposal.cast_vote(Some(106), 1, 1, 10); println!("\nVote Counts After Simulation:"); for option in &proposal.options { println!( "- Option ID: {}, Text: '{}', Votes: {}", option.id, option.text, option.count ); } println!("\nBallots Cast:"); for ballot in &proposal.ballots { println!( "- Ballot ID: {}, User ID: {}, Option ID: {}, Shares: {}", ballot.base_data.id, ballot.user_id, ballot.vote_option_id, ballot.shares_count ); } println!(""); // Example of voting with comments using the cast_vote_with_comment method println!("Adding votes with comments..."); // User 7 votes for 'Approve Allocation' with a comment proposal = proposal.cast_vote_with_comment( Some(110), // ballot_id 7, // user_id 1, // chosen_option_id (Approve Allocation) 80, // shares "I strongly support this proposal because it aligns with our community values." ); // User 8 votes for 'Reject Allocation' with a comment proposal = proposal.cast_vote_with_comment( Some(111), // ballot_id 8, // user_id 2, // chosen_option_id (Reject Allocation) 60, // shares "I have concerns about the allocation priorities." ); println!("\nBallots with Comments:"); for ballot in &proposal.ballots { if let Some(comment) = &ballot.comment { println!( "- Ballot ID: {}, User ID: {}, Option ID: {}, Shares: {}", ballot.base_data.id, ballot.user_id, ballot.vote_option_id, ballot.shares_count ); println!(" Comment: \"{}\"", comment); } } println!("\nUpdated Vote Counts After Comments:"); for option in &proposal.options { println!( "- Option ID: {}, Text: '{}', Votes: {}", option.id, option.text, option.count ); } // Change proposal status proposal = proposal.change_proposal_status(ProposalStatus::Active); println!("Changed Proposal Status to: {:?}", proposal.status); // Simulate closing the vote proposal = proposal.change_vote_event_status(VoteEventStatus::Closed); println!("Changed Vote Event Status to: {:?}", proposal.vote_status); // Attempt to cast a vote after closing (should be handled) println!("\nAttempting to cast vote after voting is closed..."); proposal = proposal.cast_vote(None, 6, 1, 25); // Final proposal state println!("\nFinal Proposal State:"); println!("Title: '{}'", proposal.title); println!("Status: {:?}", proposal.status); println!("Vote Status: {:?}", proposal.vote_status); println!("Options:"); for option in &proposal.options { println!( " - {}: {} (Votes: {})", option.id, option.text, option.count ); } println!("Total Ballots: {}", proposal.ballots.len()); // Example of a private proposal (not fully implemented in cast_vote eligibility yet) let mut private_proposal = Proposal::new( None, // auto-generated ID "user_admin_001", "Wael Ghonem", "Internal Team Restructure Vote", "Vote on proposed internal team changes.", ProposalStatus::Draft, Utc::now(), Utc::now(), Utc::now(), Utc::now() + Duration::days(7), ); private_proposal.private_group = Some(vec![10, 20, 30]); // Only users 10, 20, 30 can vote private_proposal = private_proposal.add_option( 1, "Accept Restructure", Some("This is the accept restructure option".to_string()), ); private_proposal = private_proposal.add_option( 2, "Reject Restructure", Some("This is the reject restructure option".to_string()), ); println!( "\nBefore saving - Created Private Proposal: '{}'", private_proposal.title ); println!( "Before saving - Eligible Voters (Group): {:?}", private_proposal.private_group ); // Save the private proposal to the database let (private_proposal_id, saved_private_proposal) = collection .set(&private_proposal) .expect("can save private proposal"); private_proposal = saved_private_proposal; println!( "After saving - Private Proposal ID: {}", private_proposal.base_data.id ); println!("After saving - Returned ID: {}", private_proposal_id); // User 10 (eligible) votes with explicit ballot ID private_proposal = private_proposal.cast_vote(Some(201), 10, 1, 100); // User 40 (ineligible) tries to vote with auto-generated ballot ID private_proposal = private_proposal.cast_vote(None, 40, 1, 50); // Example of voting with comments on a private proposal println!("\nAdding votes with comments to private proposal..."); // User 20 (eligible) votes with a comment private_proposal = private_proposal.cast_vote_with_comment( Some(202), // ballot_id 20, // user_id (eligible) 1, // chosen_option_id 75, // shares "I support this restructuring plan with some reservations." ); // User 30 (eligible) votes with a comment private_proposal = private_proposal.cast_vote_with_comment( Some(203), // ballot_id 30, // user_id (eligible) 2, // chosen_option_id 90, // shares "I believe we should reconsider the timing of these changes." ); // User 40 (ineligible) tries to vote with a comment private_proposal = private_proposal.cast_vote_with_comment( Some(204), // ballot_id 40, // user_id (ineligible) 1, // chosen_option_id 50, // shares "This restructuring seems unnecessary." ); println!("Eligible users 20 and 30 added votes with comments."); println!("Ineligible user 40 attempted to vote with a comment (should be rejected)."); println!("\nPrivate Proposal Ballots with Comments:"); for ballot in &private_proposal.ballots { if let Some(comment) = &ballot.comment { println!( "- Ballot ID: {}, User ID: {}, Option ID: {}, Shares: {}", ballot.base_data.id, ballot.user_id, ballot.vote_option_id, ballot.shares_count ); println!(" Comment: \"{}\"", comment); } } println!("Private Proposal Vote Counts:"); for option in &private_proposal.options { println!( " - {}: {} (Votes: {})", option.id, option.text, option.count ); } println!("\nExample finished. DB stored at {}", db_path); println!( "To clean up, you can manually delete the directory: {}", db_path ); // --- Additional Example: Listing and Filtering Proposals --- println!("\n--- Listing All Proposals ---"); // List all proposals from the DB let all_proposals = collection.get_all().expect("can list all proposals"); for proposal in &all_proposals { println!( "- Proposal ID: {}, Title: '{}', Status: {:?}", proposal.base_data.id, proposal.title, proposal.status ); } println!("Total proposals in DB: {}", all_proposals.len()); // Filter proposals by status (e.g., only Active proposals) let active_proposals: Vec<_> = all_proposals .iter() .filter(|p| p.status == ProposalStatus::Active) .collect(); println!("\n--- Filtering Proposals by Status: Active ---"); for proposal in &active_proposals { println!( "- Proposal ID: {}, Title: '{}', Status: {:?}", proposal.base_data.id, proposal.title, proposal.status ); } println!("Total ACTIVE proposals: {}", active_proposals.len()); }