feat: Add get_all method to list all db objects based on the object model
This commit is contained in:
parent
bd36d6bda0
commit
4c0c7be574
@ -1,6 +1,6 @@
|
||||
// heromodels/examples/governance_proposal_example/main.rs
|
||||
|
||||
use chrono::{Utc, Duration};
|
||||
use chrono::{Duration, Utc};
|
||||
use heromodels::db::{Collection, Db};
|
||||
use heromodels::models::governance::{Proposal, ProposalStatus, VoteEventStatus};
|
||||
|
||||
@ -13,17 +13,26 @@ fn main() {
|
||||
|
||||
// Create a new proposal with auto-generated ID
|
||||
let mut proposal = Proposal::new(
|
||||
None, // id (auto-generated)
|
||||
"user_creator_123", // creator_id
|
||||
"Community Fund Allocation for Q3", // title
|
||||
None, // id (auto-generated)
|
||||
"user_creator_123", // creator_id
|
||||
"Community Fund Allocation for Q3", // title
|
||||
"Proposal to allocate funds for community projects in the third quarter.", // description
|
||||
Utc::now(), // vote_start_date
|
||||
Utc::now() + Duration::days(14) // vote_end_date (14 days from now)
|
||||
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);
|
||||
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");
|
||||
@ -32,15 +41,23 @@ fn main() {
|
||||
|
||||
println!("Added Vote Options:");
|
||||
for option in &proposal.options {
|
||||
println!("- Option ID: {}, Text: '{}', Votes: {}", option.id, option.text, option.count);
|
||||
println!(
|
||||
"- Option ID: {}, Text: '{}', Votes: {}",
|
||||
option.id, option.text, option.count
|
||||
);
|
||||
}
|
||||
println!("");
|
||||
|
||||
// Save the proposal to the database
|
||||
let collection = db.collection::<Proposal>().expect("can open proposal collection");
|
||||
let collection = db
|
||||
.collection::<Proposal>()
|
||||
.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 - Proposal ID: {}",
|
||||
saved_proposal.base_data.id
|
||||
);
|
||||
println!("After saving - Returned ID: {}", proposal_id);
|
||||
|
||||
// Use the saved proposal for further operations
|
||||
@ -63,13 +80,18 @@ fn main() {
|
||||
|
||||
println!("\nVote Counts After Simulation:");
|
||||
for option in &proposal.options {
|
||||
println!("- Option ID: {}, Text: '{}', Votes: {}", option.id, option.text, option.count);
|
||||
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!(
|
||||
"- Ballot ID: {}, User ID: {}, Option ID: {}, Shares: {}",
|
||||
ballot.base_data.id, ballot.user_id, ballot.vote_option_id, ballot.shares_count
|
||||
);
|
||||
}
|
||||
println!("");
|
||||
|
||||
@ -92,7 +114,10 @@ fn main() {
|
||||
println!("Vote Status: {:?}", proposal.vote_status);
|
||||
println!("Options:");
|
||||
for option in &proposal.options {
|
||||
println!(" - {}: {} (Votes: {})", option.id, option.text, option.count);
|
||||
println!(
|
||||
" - {}: {} (Votes: {})",
|
||||
option.id, option.text, option.count
|
||||
);
|
||||
}
|
||||
println!("Total Ballots: {}", proposal.ballots.len());
|
||||
|
||||
@ -103,20 +128,31 @@ fn main() {
|
||||
"Internal Team Restructure Vote",
|
||||
"Vote on proposed internal team changes.",
|
||||
Utc::now(),
|
||||
Utc::now() + Duration::days(7)
|
||||
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");
|
||||
private_proposal = private_proposal.add_option(2, "Reject Restructure");
|
||||
|
||||
println!("\nBefore saving - Created Private Proposal: '{}'", private_proposal.title);
|
||||
println!("Before saving - Eligible Voters (Group): {:?}", private_proposal.private_group);
|
||||
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");
|
||||
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 - Private Proposal ID: {}",
|
||||
private_proposal.base_data.id
|
||||
);
|
||||
println!("After saving - Returned ID: {}", private_proposal_id);
|
||||
|
||||
// User 10 (eligible) votes with explicit ballot ID
|
||||
@ -125,10 +161,42 @@ fn main() {
|
||||
private_proposal = private_proposal.cast_vote(None, 40, 1, 50);
|
||||
|
||||
println!("Private Proposal Vote Counts:");
|
||||
for option in &private_proposal.options {
|
||||
println!(" - {}: {} (Votes: {})", option.id, option.text, option.count);
|
||||
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);
|
||||
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());
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
use crate::db::Transaction;
|
||||
use heromodels_core::{Index, Model};
|
||||
use ourdb::OurDBSetArgs;
|
||||
use serde::Deserialize;
|
||||
use crate::db::Transaction;
|
||||
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::HashSet,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex, atomic::{AtomicU32, Ordering}},
|
||||
sync::{
|
||||
Arc, Mutex,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
/// Configuration for custom ID sequences
|
||||
@ -85,7 +88,11 @@ impl OurDB {
|
||||
}
|
||||
|
||||
/// Create a new instance of ourdb with a custom ID sequence
|
||||
pub fn with_id_sequence(path: impl Into<PathBuf>, reset: bool, id_sequence: IdSequence) -> Result<Self, tst::Error> {
|
||||
pub fn with_id_sequence(
|
||||
path: impl Into<PathBuf>,
|
||||
reset: bool,
|
||||
id_sequence: IdSequence,
|
||||
) -> Result<Self, tst::Error> {
|
||||
let mut base_path = path.into();
|
||||
let mut data_path = base_path.clone();
|
||||
base_path.push("index");
|
||||
@ -138,7 +145,9 @@ where
|
||||
type Error = tst::Error;
|
||||
|
||||
/// Begin a transaction for this collection
|
||||
fn begin_transaction(&self) -> Result<Box<dyn super::Transaction<Error = Self::Error>>, super::Error<Self::Error>> {
|
||||
fn begin_transaction(
|
||||
&self,
|
||||
) -> Result<Box<dyn super::Transaction<Error = Self::Error>>, super::Error<Self::Error>> {
|
||||
// Create a new transaction
|
||||
let transaction = OurDBTransaction::new();
|
||||
|
||||
@ -287,7 +296,7 @@ where
|
||||
// and save it again to ensure the serialized data contains the correct ID
|
||||
let mut value_clone = value.clone();
|
||||
let base_data = value_clone.base_data_mut();
|
||||
base_data.update_id(assigned_id);
|
||||
base_data.id = assigned_id;
|
||||
|
||||
// Serialize the updated model
|
||||
let v = bincode::serde::encode_to_vec(&value_clone, BINCODE_CONFIG)?;
|
||||
@ -327,10 +336,13 @@ where
|
||||
}
|
||||
|
||||
// Get the updated model from the database
|
||||
let updated_model = Self::get_ourdb_value::<M>(&mut data_db, assigned_id)?
|
||||
.ok_or_else(|| super::Error::InvalidId(format!(
|
||||
"Failed to retrieve model with ID {} after saving", assigned_id
|
||||
)))?;
|
||||
let updated_model =
|
||||
Self::get_ourdb_value::<M>(&mut data_db, assigned_id)?.ok_or_else(|| {
|
||||
super::Error::InvalidId(format!(
|
||||
"Failed to retrieve model with ID {} after saving",
|
||||
assigned_id
|
||||
))
|
||||
})?;
|
||||
|
||||
// Return the assigned ID and the updated model
|
||||
Ok((assigned_id, updated_model))
|
||||
@ -413,7 +425,18 @@ where
|
||||
}
|
||||
|
||||
fn get_all(&self) -> Result<Vec<M>, super::Error<Self::Error>> {
|
||||
todo!("OurDB doesn't have a list all method yet")
|
||||
let mut data_db = self.data.lock().expect("can lock data DB");
|
||||
let mut all_objs = Vec::new();
|
||||
// Get the next available ID (exclusive upper bound)
|
||||
let next_id = data_db.get_next_id().map_err(super::Error::from)?;
|
||||
for id in 1..next_id {
|
||||
match Self::get_ourdb_value::<M>(&mut data_db, id) {
|
||||
Ok(Some(obj)) => all_objs.push(obj),
|
||||
Ok(None) => continue, // skip missing IDs
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(all_objs)
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,7 +533,9 @@ struct OurDBTransaction {
|
||||
impl OurDBTransaction {
|
||||
/// Create a new transaction
|
||||
fn new() -> Self {
|
||||
Self { active: std::sync::atomic::AtomicBool::new(false) }
|
||||
Self {
|
||||
active: std::sync::atomic::AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -519,8 +544,11 @@ impl Drop for OurDBTransaction {
|
||||
// If the transaction is still active when dropped, roll it back
|
||||
if self.active.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
// We can't return an error from drop, so we just log it
|
||||
eprintln!("Warning: Transaction was dropped without being committed or rolled back. Rolling back automatically.");
|
||||
self.active.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
eprintln!(
|
||||
"Warning: Transaction was dropped without being committed or rolled back. Rolling back automatically."
|
||||
);
|
||||
self.active
|
||||
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -541,10 +569,13 @@ impl super::Transaction for OurDBTransaction {
|
||||
// In a real implementation, you would commit the transaction in the underlying database
|
||||
// For now, we just check if the transaction is active
|
||||
if !self.active.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
return Err(super::Error::InvalidId("Cannot commit an inactive transaction".to_string()));
|
||||
return Err(super::Error::InvalidId(
|
||||
"Cannot commit an inactive transaction".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.active.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
self.active
|
||||
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -553,10 +584,13 @@ impl super::Transaction for OurDBTransaction {
|
||||
// In a real implementation, you would roll back the transaction in the underlying database
|
||||
// For now, we just check if the transaction is active
|
||||
if !self.active.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
return Err(super::Error::InvalidId("Cannot roll back an inactive transaction".to_string()));
|
||||
return Err(super::Error::InvalidId(
|
||||
"Cannot roll back an inactive transaction".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.active.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
self.active
|
||||
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai_autobind_macros::rhai_model_export;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use rhai_autobind_macros::rhai_model_export;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents the status of an attendee for an event
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
@ -60,7 +60,12 @@ pub struct Event {
|
||||
|
||||
impl Event {
|
||||
/// Creates a new event
|
||||
pub fn new(id: String, title: impl ToString, start_time: DateTime<Utc>, end_time: DateTime<Utc>) -> Self {
|
||||
pub fn new(
|
||||
id: String,
|
||||
title: impl ToString,
|
||||
start_time: DateTime<Utc>,
|
||||
end_time: DateTime<Utc>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
title: title.to_string(),
|
||||
@ -108,7 +113,11 @@ impl Event {
|
||||
}
|
||||
|
||||
/// Reschedules the event to new start and end times
|
||||
pub fn reschedule(mut self, new_start_time: DateTime<Utc>, new_end_time: DateTime<Utc>) -> Self {
|
||||
pub fn reschedule(
|
||||
mut self,
|
||||
new_start_time: DateTime<Utc>,
|
||||
new_end_time: DateTime<Utc>,
|
||||
) -> Self {
|
||||
// Basic validation: end_time should be after start_time
|
||||
if new_end_time > new_start_time {
|
||||
self.start_time = new_start_time;
|
||||
|
@ -1,8 +1,8 @@
|
||||
// heromodels/src/models/finance/account.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_derive::model;
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::asset::Asset;
|
||||
|
||||
@ -38,7 +38,7 @@ impl Account {
|
||||
description: impl ToString,
|
||||
ledger: impl ToString,
|
||||
address: impl ToString,
|
||||
pubkey: impl ToString
|
||||
pubkey: impl ToString,
|
||||
) -> Self {
|
||||
let mut base_data = BaseModelData::new();
|
||||
if let Some(id) = id {
|
||||
|
@ -1,8 +1,8 @@
|
||||
// heromodels/src/models/finance/asset.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_derive::model;
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// AssetType defines the type of blockchain asset
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
@ -24,12 +24,12 @@ impl Default for AssetType {
|
||||
#[model] // Has base.Base in V spec
|
||||
pub struct Asset {
|
||||
pub base_data: BaseModelData,
|
||||
pub name: String, // Name of the asset
|
||||
pub description: String, // Description of the asset
|
||||
pub amount: f64, // Amount of the asset
|
||||
pub address: String, // Address of the asset on the blockchain or bank
|
||||
pub asset_type: AssetType, // Type of the asset
|
||||
pub decimals: u8, // Number of decimals of the asset
|
||||
pub name: String, // Name of the asset
|
||||
pub description: String, // Description of the asset
|
||||
pub amount: f64, // Amount of the asset
|
||||
pub address: String, // Address of the asset on the blockchain or bank
|
||||
pub asset_type: AssetType, // Type of the asset
|
||||
pub decimals: u8, // Number of decimals of the asset
|
||||
}
|
||||
|
||||
impl Asset {
|
||||
|
@ -1,9 +1,9 @@
|
||||
// heromodels/src/models/finance/marketplace.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_derive::model;
|
||||
use heromodels_core::BaseModelData;
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::asset::AssetType;
|
||||
|
||||
@ -54,11 +54,11 @@ impl Default for BidStatus {
|
||||
/// Bid represents a bid on an auction listing
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
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
|
||||
pub amount: f64, // Bid amount
|
||||
pub currency: String, // Currency of the bid
|
||||
pub status: BidStatus, // Status of the 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
|
||||
pub amount: f64, // Bid amount
|
||||
pub currency: String, // Currency of the bid
|
||||
pub status: BidStatus, // Status of the bid
|
||||
pub created_at: DateTime<Utc>, // When the bid was created
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ pub struct Listing {
|
||||
pub asset_id: String,
|
||||
pub asset_type: AssetType,
|
||||
pub seller_id: String,
|
||||
pub price: f64, // Initial price for fixed price, or starting price for auction
|
||||
pub price: f64, // Initial price for fixed price, or starting price for auction
|
||||
pub currency: String,
|
||||
pub listing_type: ListingType,
|
||||
pub status: ListingStatus,
|
||||
@ -210,7 +210,11 @@ impl Listing {
|
||||
}
|
||||
|
||||
/// Complete a sale (fixed price or auction)
|
||||
pub fn complete_sale(mut self, buyer_id: impl ToString, sale_price: f64) -> Result<Self, &'static str> {
|
||||
pub fn complete_sale(
|
||||
mut self,
|
||||
buyer_id: impl ToString,
|
||||
sale_price: f64,
|
||||
) -> Result<Self, &'static str> {
|
||||
if self.status != ListingStatus::Active {
|
||||
return Err("Cannot complete sale for inactive listing");
|
||||
}
|
||||
@ -223,7 +227,9 @@ impl Listing {
|
||||
// If this was an auction, accept the winning bid and reject others
|
||||
if self.listing_type == ListingType::Auction {
|
||||
for bid in &mut self.bids {
|
||||
if bid.bidder_id.to_string() == self.buyer_id.as_ref().unwrap().to_string() && bid.amount == sale_price {
|
||||
if bid.bidder_id.to_string() == self.buyer_id.as_ref().unwrap().to_string()
|
||||
&& bid.amount == sale_price
|
||||
{
|
||||
bid.status = BidStatus::Accepted;
|
||||
} else {
|
||||
bid.status = BidStatus::Rejected;
|
||||
|
@ -1,10 +1,10 @@
|
||||
// heromodels/src/models/governance/proposal.rs
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use heromodels_derive::model; // For #[model]
|
||||
use rhai_autobind_macros::rhai_model_export;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use rhai_autobind_macros::rhai_model_export;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use heromodels_core::BaseModelData;
|
||||
|
||||
@ -13,11 +13,11 @@ use heromodels_core::BaseModelData;
|
||||
/// ProposalStatus defines the lifecycle status of a governance proposal itself
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum ProposalStatus {
|
||||
Draft, // Proposal is being prepared
|
||||
Active, // Proposal is active
|
||||
Approved, // Proposal has been formally approved
|
||||
Rejected, // Proposal has been formally rejected
|
||||
Cancelled,// Proposal was cancelled
|
||||
Draft, // Proposal is being prepared
|
||||
Active, // Proposal is active
|
||||
Approved, // Proposal has been formally approved
|
||||
Rejected, // Proposal has been formally rejected
|
||||
Cancelled, // Proposal was cancelled
|
||||
}
|
||||
|
||||
impl Default for ProposalStatus {
|
||||
@ -26,7 +26,6 @@ impl Default for ProposalStatus {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// VoteEventStatus represents the status of the voting process for a proposal
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum VoteEventStatus {
|
||||
@ -46,9 +45,9 @@ impl Default for VoteEventStatus {
|
||||
/// VoteOption represents a specific choice that can be voted on
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
pub struct VoteOption {
|
||||
pub id: u8, // Simple identifier for this option
|
||||
pub text: String, // Descriptive text of the option
|
||||
pub count: i64, // How many votes this option has received
|
||||
pub id: u8, // Simple identifier for this option
|
||||
pub text: String, // Descriptive text of the option
|
||||
pub count: i64, // How many votes this option has received
|
||||
pub min_valid: Option<i64>, // Optional: minimum votes needed
|
||||
}
|
||||
|
||||
@ -69,9 +68,9 @@ impl VoteOption {
|
||||
#[model] // Has base.Base in V spec
|
||||
pub struct Ballot {
|
||||
pub base_data: BaseModelData,
|
||||
pub user_id: u32, // The ID of the user who cast this ballot
|
||||
pub vote_option_id: u8, // The 'id' of the VoteOption chosen
|
||||
pub shares_count: i64, // Number of shares/tokens/voting power
|
||||
pub user_id: u32, // The ID of the user who cast this ballot
|
||||
pub vote_option_id: u8, // The 'id' of the VoteOption chosen
|
||||
pub shares_count: i64, // Number of shares/tokens/voting power
|
||||
}
|
||||
|
||||
impl Ballot {
|
||||
@ -97,7 +96,6 @@ impl Ballot {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Proposal represents a governance proposal that can be voted upon.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
#[rhai_model_export(db_type = "std::sync::Arc<crate::db::hero::OurDB>")]
|
||||
@ -128,7 +126,14 @@ impl Proposal {
|
||||
/// * `description` - Description of the proposal
|
||||
/// * `vote_start_date` - Date when voting starts
|
||||
/// * `vote_end_date` - Date when voting ends
|
||||
pub fn new(id: Option<u32>, creator_id: impl ToString, title: impl ToString, description: impl ToString, vote_start_date: DateTime<Utc>, vote_end_date: DateTime<Utc>) -> Self {
|
||||
pub fn new(
|
||||
id: Option<u32>,
|
||||
creator_id: impl ToString,
|
||||
title: impl ToString,
|
||||
description: impl ToString,
|
||||
vote_start_date: DateTime<Utc>,
|
||||
vote_end_date: DateTime<Utc>,
|
||||
) -> Self {
|
||||
let mut base_data = BaseModelData::new();
|
||||
if let Some(id) = id {
|
||||
base_data.update_id(id);
|
||||
@ -155,18 +160,30 @@ impl Proposal {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cast_vote(mut self, ballot_id: Option<u32>, user_id: u32, chosen_option_id: u8, shares: i64) -> Self {
|
||||
pub fn cast_vote(
|
||||
mut self,
|
||||
ballot_id: Option<u32>,
|
||||
user_id: u32,
|
||||
chosen_option_id: u8,
|
||||
shares: i64,
|
||||
) -> Self {
|
||||
if self.vote_status != VoteEventStatus::Open {
|
||||
eprintln!("Voting is not open for proposal '{}'", self.title);
|
||||
return self;
|
||||
}
|
||||
if !self.options.iter().any(|opt| opt.id == chosen_option_id) {
|
||||
eprintln!("Chosen option ID {} does not exist for proposal '{}'", chosen_option_id, self.title);
|
||||
eprintln!(
|
||||
"Chosen option ID {} does not exist for proposal '{}'",
|
||||
chosen_option_id, self.title
|
||||
);
|
||||
return self;
|
||||
}
|
||||
if let Some(group) = &self.private_group {
|
||||
if !group.contains(&user_id) {
|
||||
eprintln!("User {} is not eligible to vote on proposal '{}'", user_id, self.title);
|
||||
eprintln!(
|
||||
"User {} is not eligible to vote on proposal '{}'",
|
||||
user_id, self.title
|
||||
);
|
||||
return self;
|
||||
}
|
||||
}
|
||||
@ -174,7 +191,11 @@ impl Proposal {
|
||||
let new_ballot = Ballot::new(ballot_id, user_id, chosen_option_id, shares);
|
||||
self.ballots.push(new_ballot);
|
||||
|
||||
if let Some(option) = self.options.iter_mut().find(|opt| opt.id == chosen_option_id) {
|
||||
if let Some(option) = self
|
||||
.options
|
||||
.iter_mut()
|
||||
.find(|opt| opt.id == chosen_option_id)
|
||||
{
|
||||
option.count += shares;
|
||||
}
|
||||
self
|
||||
|
Loading…
Reference in New Issue
Block a user