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

@ -130,7 +130,7 @@ pub fn model(_attr: TokenStream, item: TokenStream) -> TokenStream {
} }
fn get_id(&self) -> u32 { fn get_id(&self) -> u32 {
self.base_data.id.unwrap_or(0) self.base_data.id
} }
fn base_data_mut(&mut self) -> &mut heromodels_core::BaseModelData { fn base_data_mut(&mut self) -> &mut heromodels_core::BaseModelData {

View File

@ -33,68 +33,73 @@ fn main() {
println!("Hero Models - Basic Usage Example"); println!("Hero Models - Basic Usage Example");
println!("================================"); println!("================================");
// Create users with different ID configurations // Create users with auto-generated IDs
// User 1: With explicit ID // User 1
let user1 = User::new(Some(1)) let user1 = User::new()
.username("johndoe") .username("johndoe")
.email("john.doe@example.com") .email("john.doe@example.com")
.full_name("John Doe") .full_name("John Doe")
.is_active(false) .is_active(false)
.build(); .build();
// User 2: With auto-generated ID // User 2
let user2 = User::new(None) let user2 = User::new()
.username("janesmith") .username("janesmith")
.email("jane.smith@example.com") .email("jane.smith@example.com")
.full_name("Jane Smith") .full_name("Jane Smith")
.is_active(true) .is_active(true)
.build(); .build();
// User 3: With explicit ID // User 3
let user3 = User::new(Some(3)) let user3 = User::new()
.username("willism") .username("willism")
.email("willis.masters@example.com") .email("willis.masters@example.com")
.full_name("Willis Masters") .full_name("Willis Masters")
.is_active(true) .is_active(true)
.build(); .build();
// User 4: With explicit ID // User 4
let user4 = User::new(Some(4)) let user4 = User::new()
.username("carrols") .username("carrols")
.email("carrol.smith@example.com") .email("carrol.smith@example.com")
.full_name("Carrol Smith") .full_name("Carrol Smith")
.is_active(false) .is_active(false)
.build(); .build();
// Save all users to database // Save all users to database and get their assigned IDs
db.collection().expect("can open user collection").set(&user1).expect("can set user"); let user1_id = db.collection().expect("can open user collection").set(&user1).expect("can set user");
db.collection().expect("can open user collection").set(&user2).expect("can set user"); let user2_id = db.collection().expect("can open user collection").set(&user2).expect("can set user");
db.collection().expect("can open user collection").set(&user3).expect("can set user"); let user3_id = db.collection().expect("can open user collection").set(&user3).expect("can set user");
db.collection().expect("can open user collection").set(&user4).expect("can set user"); let user4_id = db.collection().expect("can open user collection").set(&user4).expect("can set user");
// Retrieve all users from database println!("User 1 assigned ID: {}", user1_id);
println!("User 2 assigned ID: {}", user2_id);
println!("User 3 assigned ID: {}", user3_id);
println!("User 4 assigned ID: {}", user4_id);
// Retrieve all users from database using the assigned IDs
let db_user1 = db.collection::<User>().expect("can open user collection") let db_user1 = db.collection::<User>().expect("can open user collection")
.get_by_id(user1.get_id()).expect("can load user").expect("user should exist"); .get_by_id(user1_id).expect("can load user").expect("user should exist");
let db_user2 = db.collection::<User>().expect("can open user collection") let db_user2 = db.collection::<User>().expect("can open user collection")
.get_by_id(user2.get_id()).expect("can load user").expect("user should exist"); .get_by_id(user2_id).expect("can load user").expect("user should exist");
let db_user3 = db.collection::<User>().expect("can open user collection") let db_user3 = db.collection::<User>().expect("can open user collection")
.get_by_id(user3.get_id()).expect("can load user").expect("user should exist"); .get_by_id(user3_id).expect("can load user").expect("user should exist");
let db_user4 = db.collection::<User>().expect("can open user collection") let db_user4 = db.collection::<User>().expect("can open user collection")
.get_by_id(user4.get_id()).expect("can load user").expect("user should exist"); .get_by_id(user4_id).expect("can load user").expect("user should exist");
// Print all users retrieved from database // Print all users retrieved from database
println!("\n--- Users Retrieved from Database ---"); println!("\n--- Users Retrieved from Database ---");
println!("\n1. User with explicit ID (1):"); println!("\n1. First user:");
print_user_details(&db_user1); print_user_details(&db_user1);
println!("\n2. User with auto-generated ID:"); println!("\n2. Second user:");
print_user_details(&db_user2); print_user_details(&db_user2);
println!("\n3. User with explicit ID (3):"); println!("\n3. Third user:");
print_user_details(&db_user3); print_user_details(&db_user3);
println!("\n4. User with explicit ID (4):"); println!("\n4. Fourth user:");
print_user_details(&db_user4); print_user_details(&db_user4);
// Demonstrate different ways to retrieve users from the database // Demonstrate different ways to retrieve users from the database
@ -126,9 +131,11 @@ fn main() {
// 3. Delete a user and show the updated results // 3. Delete a user and show the updated results
println!("\n3. After Deleting a User:"); println!("\n3. After Deleting a User:");
let user_to_delete_id = active_users[0].get_id();
println!("Deleting user with ID: {}", user_to_delete_id);
db.collection::<User>() db.collection::<User>()
.expect("can open user collection") .expect("can open user collection")
.delete_by_id(active_users[0].get_id()) .delete_by_id(user_to_delete_id)
.expect("can delete existing user"); .expect("can delete existing user");
// Show remaining active users // Show remaining active users
@ -165,21 +172,24 @@ fn main() {
// 1. Create and save a comment // 1. Create and save a comment
println!("\n1. Creating a Comment:"); println!("\n1. Creating a Comment:");
let comment = Comment::new(None) let comment = Comment::new()
.user_id(db_user1.get_id()) // commenter's user ID .user_id(db_user1.get_id()) // commenter's user ID
.content("This is a comment on the user") .content("This is a comment on the user")
.build(); .build();
db.collection() // Save the comment and get its assigned ID
let comment_id = db.collection()
.expect("can open comment collection") .expect("can open comment collection")
.set(&comment) .set(&comment)
.expect("can set comment"); .expect("can set comment");
// 2. Retrieve the comment from database println!("Comment assigned ID: {}", comment_id);
// 2. Retrieve the comment from database using the assigned ID
let db_comment = db let db_comment = db
.collection::<Comment>() .collection::<Comment>()
.expect("can open comment collection") .expect("can open comment collection")
.get_by_id(comment.get_id()) .get_by_id(comment_id)
.expect("can load comment") .expect("can load comment")
.expect("comment should exist"); .expect("comment should exist");

View File

@ -19,7 +19,7 @@ fn main() {
println!("SimpleUser DB Prefix: {}", SimpleUser::db_prefix()); println!("SimpleUser DB Prefix: {}", SimpleUser::db_prefix());
let user = SimpleUser { let user = SimpleUser {
base_data: BaseModelData::new(1), base_data: BaseModelData::new(),
login: "johndoe".to_string(), login: "johndoe".to_string(),
full_name: "John Doe".to_string(), full_name: "John Doe".to_string(),
}; };

View File

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

View File

@ -26,7 +26,7 @@ impl OurDB {
data_path.push("data"); data_path.push("data");
let data_db = ourdb::OurDB::new(ourdb::OurDBConfig { let data_db = ourdb::OurDB::new(ourdb::OurDBConfig {
incremental_mode: false, incremental_mode: true,
path: data_path, path: data_path,
file_size: None, file_size: None,
keysize: None, keysize: None,
@ -87,7 +87,7 @@ where
Self::get_ourdb_value(&mut data_db, id) 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. // 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 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())?; 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 // Get the current ID
let v = bincode::serde::encode_to_vec(value, BINCODE_CONFIG)?;
let id = value.get_id(); let id = value.get_id();
data_db.set(OurDBSetArgs {
id: Some(id), // If id is 0, it's a new object, so let OurDB auto-generate an ID
data: &v, // 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 // Now add the new indices
for index_key in indices_to_add { 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 // Load the existing id set for the index or create a new set
let mut existing_ids = let mut existing_ids =
Self::get_tst_value::<HashSet<u32>>(&mut index_db, &key)?.unwrap_or_default(); 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)?; let encoded_ids = bincode::serde::encode_to_vec(existing_ids, BINCODE_CONFIG)?;
index_db.set(&key, encoded_ids)?; 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>> fn delete<I, Q>(&self, key: &Q) -> Result<(), super::Error<Self::Error>>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -75,16 +75,15 @@ pub struct Ballot {
} }
impl Ballot { impl Ballot {
/// Create a new ballot /// Create a new ballot with auto-generated ID
/// ///
/// # Arguments /// # 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 /// * `user_id` - ID of the user who cast this ballot
/// * `vote_option_id` - ID of the vote option chosen /// * `vote_option_id` - ID of the vote option chosen
/// * `shares_count` - Number of shares/tokens/voting power /// * `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 { Self {
base_data: BaseModelData::new(id), base_data: BaseModelData::new(),
user_id, user_id,
vote_option_id, vote_option_id,
shares_count, shares_count,
@ -114,18 +113,17 @@ pub struct Proposal {
} }
impl Proposal { impl Proposal {
/// Create a new proposal /// Create a new proposal with auto-generated ID
/// ///
/// # Arguments /// # 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 /// * `creator_id` - ID of the user who created the proposal
/// * `title` - Title of the proposal /// * `title` - Title of the proposal
/// * `description` - Description of the proposal /// * `description` - Description of the proposal
/// * `vote_start_date` - Date when voting starts /// * `vote_start_date` - Date when voting starts
/// * `vote_end_date` - Date when voting ends /// * `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 { Self {
base_data: BaseModelData::new(id), base_data: BaseModelData::new(),
creator_id: creator_id.to_string(), creator_id: creator_id.to_string(),
title: title.to_string(), title: title.to_string(),
description: description.to_string(), description: description.to_string(),
@ -145,7 +143,7 @@ impl Proposal {
self 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 { if self.vote_status != VoteEventStatus::Open {
eprintln!("Voting is not open for proposal '{}'", self.title); eprintln!("Voting is not open for proposal '{}'", self.title);
return self; 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); 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) {

View File

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

View File

@ -59,21 +59,11 @@ pub trait Model:
} }
/// Get the unique ID for this model /// Get the unique ID for this model
/// Returns 0 if the ID is None
fn get_id(&self) -> u32; fn get_id(&self) -> u32;
/// Get a mutable reference to the base_data field /// Get a mutable reference to the base_data field
fn base_data_mut(&mut self) -> &mut BaseModelData; fn base_data_mut(&mut self) -> &mut BaseModelData;
/// Set the ID for this model
fn id(mut self, id: Option<u32>) -> Self
where
Self: Sized,
{
self.base_data_mut().id = id;
self
}
/// Build the model, updating the modified timestamp /// Build the model, updating the modified timestamp
fn build(mut self) -> Self fn build(mut self) -> Self
where where
@ -98,8 +88,8 @@ pub trait Index {
/// Base struct that all models should include /// Base struct that all models should include
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaseModelData { pub struct BaseModelData {
/// Unique incremental ID per circle /// Unique incremental ID - will be auto-generated by OurDB
pub id: Option<u32>, pub id: u32,
/// Unix epoch timestamp for creation time /// Unix epoch timestamp for creation time
pub created_at: i64, pub created_at: i64,
@ -112,11 +102,12 @@ pub struct BaseModelData {
} }
impl BaseModelData { impl BaseModelData {
/// Create a new BaseModelData instance /// Create a new BaseModelData instance with ID set to 0
pub fn new(id: Option<u32>) -> Self { /// The ID will be auto-generated by OurDB when the model is saved
pub fn new() -> Self {
let now = chrono::Utc::now().timestamp(); let now = chrono::Utc::now().timestamp();
Self { Self {
id, id: 0, // This will be replaced by OurDB with an auto-generated ID
created_at: now, created_at: now,
modified_at: now, modified_at: now,
comments: Vec::new(), comments: Vec::new(),
@ -124,8 +115,8 @@ impl BaseModelData {
} }
/// Create a new BaseModelDataBuilder /// Create a new BaseModelDataBuilder
pub fn builder(id: Option<u32>) -> BaseModelDataBuilder { pub fn builder() -> BaseModelDataBuilder {
BaseModelDataBuilder::new(id) BaseModelDataBuilder::new()
} }
/// Add a comment to this model /// Add a comment to this model
@ -144,11 +135,15 @@ impl BaseModelData {
pub fn update_modified(&mut self) { pub fn update_modified(&mut self) {
self.modified_at = chrono::Utc::now().timestamp(); self.modified_at = chrono::Utc::now().timestamp();
} }
/// Update the ID of this model
pub fn update_id(&mut self, id: u32) {
self.id = id;
}
} }
/// Builder for BaseModelData /// Builder for BaseModelData
pub struct BaseModelDataBuilder { pub struct BaseModelDataBuilder {
id: Option<u32>,
created_at: Option<i64>, created_at: Option<i64>,
modified_at: Option<i64>, modified_at: Option<i64>,
comments: Vec<u32>, comments: Vec<u32>,
@ -156,9 +151,8 @@ pub struct BaseModelDataBuilder {
impl BaseModelDataBuilder { impl BaseModelDataBuilder {
/// Create a new BaseModelDataBuilder /// Create a new BaseModelDataBuilder
pub fn new(id: Option<u32>) -> Self { pub fn new() -> Self {
Self { Self {
id,
created_at: None, created_at: None,
modified_at: None, modified_at: None,
comments: Vec::new(), comments: Vec::new(),
@ -193,7 +187,7 @@ impl BaseModelDataBuilder {
pub fn build(self) -> BaseModelData { pub fn build(self) -> BaseModelData {
let now = chrono::Utc::now().timestamp(); let now = chrono::Utc::now().timestamp();
BaseModelData { BaseModelData {
id: self.id, id: 0, // This will be replaced by OurDB with an auto-generated ID
created_at: self.created_at.unwrap_or(now), created_at: self.created_at.unwrap_or(now),
modified_at: self.modified_at.unwrap_or(now), modified_at: self.modified_at.unwrap_or(now),
comments: self.comments, comments: self.comments,