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, } } } /// Base trait for all models pub trait BaseModel: 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; } /// Base struct that all models should include #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BaseModelData { /// Unique incremental ID per circle 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 pub fn new(id: u32) -> Self { let now = chrono::Utc::now().timestamp(); Self { id, created_at: now, modified_at: now, comments: Vec::new(), } } /// Create a new BaseModelDataBuilder pub fn builder(id: u32) -> BaseModelDataBuilder { BaseModelDataBuilder::new(id) } /// 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.modified_at = chrono::Utc::now().timestamp(); } /// Update the modified timestamp pub fn update_modified(&mut self) { self.modified_at = chrono::Utc::now().timestamp(); } } /// Builder for BaseModelData pub struct BaseModelDataBuilder { id: u32, created_at: Option, modified_at: Option, comments: Vec, } impl BaseModelDataBuilder { /// Create a new BaseModelDataBuilder pub fn new(id: u32) -> Self { Self { id, 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: self.id, created_at: self.created_at.unwrap_or(now), modified_at: self.modified_at.unwrap_or(now), comments: self.comments, } } } /// Macro to implement BaseModel for a struct that contains a base_data field of type BaseModelData #[macro_export] macro_rules! impl_base_model { ($type:ty, $prefix:expr) => { impl BaseModel for $type { fn db_prefix() -> &'static str { $prefix } fn get_id(&self) -> u32 { self.base_data.id } } }; }