use serde::{Deserialize, Serialize}; use std::fmt::Debug; /// Represents an index key for a model #[derive(Debug, Clone, PartialEq, Eq)] pub struct IndexKey { /// The name of the index key pub name: &'static str, /// The value of the index key for a specific model instance pub value: String, } /// Builder for IndexKey pub struct IndexKeyBuilder { name: &'static str, value: String, } impl IndexKeyBuilder { /// Create a new IndexKeyBuilder pub fn new(name: &'static str) -> Self { Self { name, value: String::new(), } } /// Set the value for this index key pub fn value(mut self, value: impl ToString) -> Self { self.value = value.to_string(); self } /// Build the IndexKey pub fn build(self) -> IndexKey { IndexKey { name: self.name, value: self.value, } } } /// Unified trait for all models pub trait Model: Debug + Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static { /// Get the database prefix for this model type fn db_prefix() -> &'static str where Self: Sized; /// Returns a list of index keys for this model instance /// These keys will be used to create additional indexes in the TST /// The default implementation returns an empty vector /// Override this method to provide custom indexes fn db_keys(&self) -> Vec { Vec::new() } /// Get the unique ID for this model fn get_id(&self) -> u32; /// Get a mutable reference to the base_data field fn base_data_mut(&mut self) -> &mut BaseModelData; /// Build the model, updating the modified timestamp fn build(mut self) -> Self where Self: Sized, { self.base_data_mut().update_modified(); self } } /// An identifier for an index in the DB pub trait Index { /// The model for which this is an index in the database type Model: Model; type Key: ToString + ?Sized; /// The key of this index fn key() -> &'static str; } /// Base struct that all models should include /// /// # ID Management /// /// The `id` field is managed automatically by OurDB when using incremental mode: /// /// 1. When creating a new model, the `id` field is initialized to 0. /// 2. When saving the model to the database with `set()`, OurDB will: /// - If the ID is 0, auto-generate a new ID and update the model. /// - If the ID is not 0, use the provided ID for updates. /// /// # Important Notes /// /// - Never manually set the `id` field to a specific value. Always use 0 for new models. /// - The ID 0 is reserved for new models and should not be used for existing models. /// - After saving a model, use the ID returned by `set()` to retrieve the model from the database. /// - The original model passed to `set()` is not modified; you need to retrieve the updated model. /// /// # Example /// /// ```rust /// // Create a new model with ID 0 /// let user = User::new() /// .username("johndoe") /// .email("john.doe@example.com") /// .build(); /// /// // Save the model and get the assigned ID /// let user_id = db.collection().set(&user).expect("Failed to save user"); /// /// // Retrieve the model with the assigned ID /// let db_user = db.collection().get_by_id(user_id).expect("Failed to get user"); /// ``` #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BaseModelData { /// Unique incremental ID - will be auto-generated by OurDB /// /// This field is automatically managed by OurDB: /// - For new models, set this to 0 and OurDB will auto-generate an ID. /// - For existing models, this will be the ID assigned by OurDB. /// /// Do not manually set this field to a specific value. pub id: u32, /// Unix epoch timestamp for creation time pub created_at: i64, /// Unix epoch timestamp for last modification time pub modified_at: i64, /// List of comment IDs referencing Comment objects pub comments: Vec, } impl BaseModelData { /// Create a new BaseModelData instance with ID set to 0 /// The ID will be auto-generated by OurDB when the model is saved pub fn new() -> Self { let now = chrono::Utc::now().timestamp(); Self { id: 0, // This will be replaced by OurDB with an auto-generated ID created_at: now, modified_at: now, comments: Vec::new(), } } /// Create a new BaseModelDataBuilder pub fn builder() -> BaseModelDataBuilder { BaseModelDataBuilder::new() } /// Add a comment to this model pub fn add_comment(&mut self, comment_id: u32) { self.comments.push(comment_id); self.modified_at = chrono::Utc::now().timestamp(); } /// Remove a comment from this model pub fn remove_comment(&mut self, comment_id: u32) { self.comments.retain(|&id| id != comment_id); self.update_modified(); } /// Update the modified timestamp pub fn update_modified(&mut self) { 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 pub struct BaseModelDataBuilder { created_at: Option, modified_at: Option, comments: Vec, } impl BaseModelDataBuilder { /// Create a new BaseModelDataBuilder pub fn new() -> Self { Self { created_at: None, modified_at: None, comments: Vec::new(), } } /// Set the created_at timestamp pub fn created_at(mut self, timestamp: i64) -> Self { self.created_at = Some(timestamp); self } /// Set the modified_at timestamp pub fn modified_at(mut self, timestamp: i64) -> Self { self.modified_at = Some(timestamp); self } /// Add a comment ID pub fn add_comment(mut self, comment_id: u32) -> Self { self.comments.push(comment_id); self } /// Add multiple comment IDs pub fn add_comments(mut self, comment_ids: Vec) -> Self { self.comments.extend(comment_ids); self } /// Build the BaseModelData pub fn build(self) -> BaseModelData { let now = chrono::Utc::now().timestamp(); BaseModelData { id: 0, // This will be replaced by OurDB with an auto-generated ID created_at: self.created_at.unwrap_or(now), modified_at: self.modified_at.unwrap_or(now), comments: self.comments, } } }