fix: Use incremental ID

This commit is contained in:
Mahmoud Emad
2025-05-17 13:00:05 +03:00
parent bde5db0e52
commit a676854251
13 changed files with 149 additions and 116 deletions

View File

@@ -32,8 +32,9 @@ where
/// Get an object from its ID. This does not use an index lookup
fn get_by_id(&self, id: u32) -> Result<Option<V>, Error<Self::Error>>;
/// Store an item in the DB.
fn set(&self, value: &V) -> Result<(), Error<Self::Error>>;
/// Store an item in the DB and return the assigned ID.
/// This method does not modify the original model.
fn set(&self, value: &V) -> Result<u32, Error<Self::Error>>;
/// Delete all items from the db with a given index.
fn delete<I, Q>(&self, key: &Q) -> Result<(), Error<Self::Error>>

View File

@@ -26,7 +26,7 @@ impl OurDB {
data_path.push("data");
let data_db = ourdb::OurDB::new(ourdb::OurDBConfig {
incremental_mode: false,
incremental_mode: true,
path: data_path,
file_size: None,
keysize: None,
@@ -87,7 +87,7 @@ where
Self::get_ourdb_value(&mut data_db, id)
}
fn set(&self, value: &M) -> Result<(), super::Error<Self::Error>> {
fn set(&self, value: &M) -> Result<u32, super::Error<Self::Error>> {
// Before inserting the new object, check if an object with this ID already exists. If it does, we potentially need to update indices.
let mut data_db = self.data.lock().expect("can lock data DB");
let old_obj: Option<M> = Self::get_ourdb_value(&mut data_db, value.get_id())?;
@@ -134,13 +134,52 @@ where
}
}
// set or update the object
let v = bincode::serde::encode_to_vec(value, BINCODE_CONFIG)?;
// Get the current ID
let id = value.get_id();
data_db.set(OurDBSetArgs {
id: Some(id),
data: &v,
})?;
// If id is 0, it's a new object, so let OurDB auto-generate an ID
// Otherwise, it's an update to an existing object
let id_param = if id == 0 { None } else { Some(id) };
// For new objects (id == 0), we need to get the assigned ID from OurDB
// and update the model before serializing it
let assigned_id = if id == 0 {
// First, get the next ID that OurDB will assign
let next_id = data_db.get_next_id()?;
// Create a mutable clone of the value and update its ID
// This is a bit of a hack, but we need to update the ID before serializing
let mut value_clone = value.clone();
let base_data = value_clone.base_data_mut();
base_data.update_id(next_id);
// Now serialize the updated model
let v = bincode::serde::encode_to_vec(&value_clone, BINCODE_CONFIG)?;
// Save to OurDB with the ID parameter set to None to let OurDB auto-generate the ID
let assigned_id = data_db.set(OurDBSetArgs {
id: id_param,
data: &v,
})?;
// The assigned ID should match the next_id we got earlier
assert_eq!(assigned_id, next_id, "OurDB assigned a different ID than expected");
// Return the assigned ID
assigned_id
} else {
// For existing objects, just serialize and save
let v = bincode::serde::encode_to_vec(value, BINCODE_CONFIG)?;
// Save to OurDB with the existing ID
let assigned_id = data_db.set(OurDBSetArgs {
id: id_param,
data: &v,
})?;
// Return the existing ID
assigned_id
};
// Now add the new indices
for index_key in indices_to_add {
@@ -148,12 +187,14 @@ where
// Load the existing id set for the index or create a new set
let mut existing_ids =
Self::get_tst_value::<HashSet<u32>>(&mut index_db, &key)?.unwrap_or_default();
existing_ids.insert(id);
// Use the assigned ID for new objects
existing_ids.insert(assigned_id);
let encoded_ids = bincode::serde::encode_to_vec(existing_ids, BINCODE_CONFIG)?;
index_db.set(&key, encoded_ids)?;
}
Ok(())
// Return the assigned ID
Ok(assigned_id)
}
fn delete<I, Q>(&self, key: &Q) -> Result<(), super::Error<Self::Error>>

View File

@@ -139,14 +139,13 @@ pub struct Calendar {
}
impl Calendar {
/// Creates a new calendar
/// Creates a new calendar with auto-generated ID
///
/// # Arguments
/// * `id` - Optional ID for the calendar. If None, the ID will be auto-generated.
/// * `name` - Name of the calendar
pub fn new(id: Option<u32>, name: impl ToString) -> Self {
pub fn new(name: impl ToString) -> Self {
Self {
base_data: BaseModelData::new(id),
base_data: BaseModelData::new(),
name: name.to_string(),
description: None,
events: Vec::new(),

View File

@@ -13,13 +13,10 @@ pub struct Comment {
}
impl Comment {
/// Create a new comment
///
/// # Arguments
/// * `id` - Optional ID for the comment. If None, the ID will be auto-generated.
pub fn new(id: Option<u32>) -> Self {
/// Create a new comment with auto-generated ID
pub fn new() -> Self {
Self {
base_data: BaseModelData::new(id),
base_data: BaseModelData::new(),
user_id: 0,
content: String::new(),
}

View File

@@ -21,10 +21,9 @@ pub struct Account {
}
impl Account {
/// Create a new account
/// Create a new account with auto-generated ID
///
/// # Arguments
/// * `id` - Optional ID for the account. If None, the ID will be auto-generated.
/// * `name` - Name of the account
/// * `user_id` - ID of the user who owns the account
/// * `description` - Description of the account
@@ -32,7 +31,6 @@ impl Account {
/// * `address` - Address of the account on the blockchain
/// * `pubkey` - Public key
pub fn new(
id: Option<u32>,
name: impl ToString,
user_id: u32,
description: impl ToString,
@@ -41,7 +39,7 @@ impl Account {
pubkey: impl ToString
) -> Self {
Self {
base_data: BaseModelData::new(id),
base_data: BaseModelData::new(),
name: name.to_string(),
user_id,
description: description.to_string(),

View File

@@ -33,9 +33,8 @@ pub struct Asset {
}
impl Asset {
/// Create a new asset
/// Create a new asset with auto-generated ID
pub fn new(
id: Option<u32>,
name: impl ToString,
description: impl ToString,
amount: f64,
@@ -44,7 +43,7 @@ impl Asset {
decimals: u8,
) -> Self {
Self {
base_data: BaseModelData::new(id),
base_data: BaseModelData::new(),
name: name.to_string(),
description: description.to_string(),
amount,
@@ -72,14 +71,14 @@ impl Asset {
if amount <= 0.0 {
return Err("Transfer amount must be positive");
}
if self.amount < amount {
return Err("Insufficient balance for transfer");
}
self.amount -= amount;
target.amount += amount;
Ok(())
}
}

View File

@@ -111,9 +111,8 @@ pub struct Listing {
}
impl Listing {
/// Create a new listing
/// Create a new listing with auto-generated ID
pub fn new(
id: Option<u32>,
title: impl ToString,
description: impl ToString,
asset_id: impl ToString,
@@ -127,7 +126,7 @@ impl Listing {
image_url: Option<impl ToString>,
) -> Self {
Self {
base_data: BaseModelData::new(id),
base_data: BaseModelData::new(),
title: title.to_string(),
description: description.to_string(),
asset_id: asset_id.to_string(),
@@ -153,32 +152,32 @@ impl Listing {
if self.listing_type != ListingType::Auction {
return Err("Bids can only be placed on auction listings");
}
// Check if listing is active
if self.status != ListingStatus::Active {
return Err("Cannot place bid on inactive listing");
}
// Check if bid amount is higher than current price
if bid.amount <= self.price {
return Err("Bid amount must be higher than current price");
}
// Check if there are existing bids and if the new bid is higher
if let Some(highest_bid) = self.highest_bid() {
if bid.amount <= highest_bid.amount {
return Err("Bid amount must be higher than current highest bid");
}
}
// Add the bid
self.bids.push(bid);
// Update the current price to the new highest bid
if let Some(highest_bid) = self.highest_bid() {
self.price = highest_bid.amount;
}
Ok(self)
}
@@ -195,12 +194,12 @@ impl Listing {
if self.status != ListingStatus::Active {
return Err("Cannot complete sale for inactive listing");
}
self.status = ListingStatus::Sold;
self.buyer_id = Some(buyer_id.to_string());
self.sale_price = Some(sale_price);
self.sold_at = Some(Utc::now());
// If this was an auction, accept the winning bid and reject others
if self.listing_type == ListingType::Auction {
for bid in &mut self.bids {
@@ -211,7 +210,7 @@ impl Listing {
}
}
}
Ok(self)
}
@@ -220,16 +219,16 @@ impl Listing {
if self.status != ListingStatus::Active {
return Err("Cannot cancel inactive listing");
}
self.status = ListingStatus::Cancelled;
// Cancel all active bids
for bid in &mut self.bids {
if bid.status == BidStatus::Active {
bid.status = BidStatus::Cancelled;
}
}
Ok(self)
}
@@ -239,7 +238,7 @@ impl Listing {
if let Some(expires_at) = self.expires_at {
if Utc::now() > expires_at {
self.status = ListingStatus::Expired;
// Cancel all active bids
for bid in &mut self.bids {
if bid.status == BidStatus::Active {
@@ -249,7 +248,7 @@ impl Listing {
}
}
}
self
}

View File

@@ -75,16 +75,15 @@ pub struct Ballot {
}
impl Ballot {
/// Create a new ballot
/// Create a new ballot with auto-generated ID
///
/// # Arguments
/// * `id` - Optional ID for the ballot. If None, the ID will be auto-generated.
/// * `user_id` - ID of the user who cast this ballot
/// * `vote_option_id` - ID of the vote option chosen
/// * `shares_count` - Number of shares/tokens/voting power
pub fn new(id: Option<u32>, user_id: u32, vote_option_id: u8, shares_count: i64) -> Self {
pub fn new(user_id: u32, vote_option_id: u8, shares_count: i64) -> Self {
Self {
base_data: BaseModelData::new(id),
base_data: BaseModelData::new(),
user_id,
vote_option_id,
shares_count,
@@ -114,18 +113,17 @@ pub struct Proposal {
}
impl Proposal {
/// Create a new proposal
/// Create a new proposal with auto-generated ID
///
/// # Arguments
/// * `id` - Optional ID for the proposal. If None, the ID will be auto-generated.
/// * `creator_id` - ID of the user who created the proposal
/// * `title` - Title of the 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(creator_id: impl ToString, title: impl ToString, description: impl ToString, vote_start_date: DateTime<Utc>, vote_end_date: DateTime<Utc>) -> Self {
Self {
base_data: BaseModelData::new(id),
base_data: BaseModelData::new(),
creator_id: creator_id.to_string(),
title: title.to_string(),
description: description.to_string(),
@@ -145,7 +143,7 @@ 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, 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;
@@ -161,7 +159,7 @@ impl Proposal {
}
}
let new_ballot = Ballot::new(ballot_id, user_id, chosen_option_id, shares);
let new_ballot = Ballot::new(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) {

View File

@@ -26,13 +26,10 @@ pub struct User {
}
impl User {
/// Create a new user
///
/// # Arguments
/// * `id` - Optional ID for the user. If None, the ID will be auto-generated.
pub fn new(id: Option<u32>) -> Self {
/// Create a new user with auto-generated ID
pub fn new() -> Self {
Self {
base_data: BaseModelData::new(id),
base_data: BaseModelData::new(),
username: String::new(),
email: String::new(),
full_name: String::new(),