...
This commit is contained in:
		
							
								
								
									
										63
									
								
								heromodels/src/comment.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								heromodels/src/comment.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::model::{BaseModel, BaseModelData, IndexKey};
 | 
			
		||||
 | 
			
		||||
/// Represents a comment on a model
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct Comment {
 | 
			
		||||
    /// Base model data
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
    
 | 
			
		||||
    /// The ID of the user who created the comment
 | 
			
		||||
    pub user_id: u32,
 | 
			
		||||
    
 | 
			
		||||
    /// The ID of the model this comment is attached to
 | 
			
		||||
    pub model_id: u32,
 | 
			
		||||
    
 | 
			
		||||
    /// The type of model this comment is attached to
 | 
			
		||||
    pub model_type: String,
 | 
			
		||||
    
 | 
			
		||||
    /// The content of the comment
 | 
			
		||||
    pub content: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Comment {
 | 
			
		||||
    /// Create a new comment
 | 
			
		||||
    pub fn new(id: u32, user_id: u32, model_id: u32, model_type: String, content: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            base_data: BaseModelData::new(id),
 | 
			
		||||
            user_id,
 | 
			
		||||
            model_id,
 | 
			
		||||
            model_type,
 | 
			
		||||
            content,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Update the comment content
 | 
			
		||||
    pub fn update_content(&mut self, content: String) {
 | 
			
		||||
        self.content = content;
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BaseModel for Comment {
 | 
			
		||||
    fn db_prefix() -> &'static str {
 | 
			
		||||
        "comment"
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn get_id(&self) -> u32 {
 | 
			
		||||
        self.base_data.id
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn db_keys(&self) -> Vec<IndexKey> {
 | 
			
		||||
        vec![
 | 
			
		||||
            IndexKey {
 | 
			
		||||
                name: "user_id",
 | 
			
		||||
                value: self.user_id.to_string(),
 | 
			
		||||
            },
 | 
			
		||||
            IndexKey {
 | 
			
		||||
                name: "model_id",
 | 
			
		||||
                value: format!("{}:{}", self.model_type, self.model_id),
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								heromodels/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								heromodels/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
//! # Hero Models
 | 
			
		||||
//! 
 | 
			
		||||
//! A library for hero models with base model trait implementation.
 | 
			
		||||
//! 
 | 
			
		||||
//! This crate provides a base model trait and implementation that other models can inherit from.
 | 
			
		||||
//! It also provides a Comment model that can be used to add comments to any model.
 | 
			
		||||
 | 
			
		||||
pub mod model;
 | 
			
		||||
pub mod comment;
 | 
			
		||||
pub mod user;
 | 
			
		||||
 | 
			
		||||
// Re-export key types for convenience
 | 
			
		||||
pub use model::{BaseModel, BaseModelData, IndexKey, impl_base_model};
 | 
			
		||||
pub use comment::Comment;
 | 
			
		||||
pub use user::User;
 | 
			
		||||
 | 
			
		||||
/// Example of how to use the heromodels crate
 | 
			
		||||
/// 
 | 
			
		||||
/// ```rust
 | 
			
		||||
/// use heromodels::{BaseModel, User, Comment};
 | 
			
		||||
/// 
 | 
			
		||||
/// // Create a new user
 | 
			
		||||
/// let mut user = User::new(
 | 
			
		||||
///     1,
 | 
			
		||||
///     "johndoe".to_string(),
 | 
			
		||||
///     "john.doe@example.com".to_string(),
 | 
			
		||||
///     "John Doe".to_string()
 | 
			
		||||
/// );
 | 
			
		||||
/// 
 | 
			
		||||
/// // Create a comment for the user
 | 
			
		||||
/// let comment = Comment::new(
 | 
			
		||||
///     1,
 | 
			
		||||
///     2, // commenter's user ID
 | 
			
		||||
///     user.get_id(),
 | 
			
		||||
///     User::db_prefix().to_string(),
 | 
			
		||||
///     "This is a comment on the user".to_string()
 | 
			
		||||
/// );
 | 
			
		||||
/// 
 | 
			
		||||
/// // Add the comment to the user
 | 
			
		||||
/// user.base_data.add_comment(comment.get_id());
 | 
			
		||||
/// 
 | 
			
		||||
/// // Get the database prefix for the User model
 | 
			
		||||
/// assert_eq!(User::db_prefix(), "user");
 | 
			
		||||
/// 
 | 
			
		||||
/// // Get the database keys for the user
 | 
			
		||||
/// let keys = user.db_keys();
 | 
			
		||||
/// assert!(keys.iter().any(|k| k.name == "username" && k.value == "johndoe"));
 | 
			
		||||
							
								
								
									
										181
									
								
								heromodels/src/model.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								heromodels/src/model.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,181 @@
 | 
			
		||||
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<IndexKey> {
 | 
			
		||||
        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<u32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<i64>,
 | 
			
		||||
    modified_at: Option<i64>,
 | 
			
		||||
    comments: Vec<u32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<u32>) -> 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
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								heromodels/src/user.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								heromodels/src/user.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::model::{BaseModel, BaseModelData, IndexKey};
 | 
			
		||||
 | 
			
		||||
/// Represents a user in the system
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct User {
 | 
			
		||||
    /// Base model data
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
    
 | 
			
		||||
    /// User's username
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    
 | 
			
		||||
    /// User's email address
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    
 | 
			
		||||
    /// User's full name
 | 
			
		||||
    pub full_name: String,
 | 
			
		||||
    
 | 
			
		||||
    /// Whether the user is active
 | 
			
		||||
    pub is_active: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl User {
 | 
			
		||||
    /// Create a new user
 | 
			
		||||
    pub fn new(id: u32, username: String, email: String, full_name: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            base_data: BaseModelData::new(id),
 | 
			
		||||
            username,
 | 
			
		||||
            email,
 | 
			
		||||
            full_name,
 | 
			
		||||
            is_active: true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Deactivate the user
 | 
			
		||||
    pub fn deactivate(&mut self) {
 | 
			
		||||
        self.is_active = false;
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Activate the user
 | 
			
		||||
    pub fn activate(&mut self) {
 | 
			
		||||
        self.is_active = true;
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Update user's email
 | 
			
		||||
    pub fn update_email(&mut self, email: String) {
 | 
			
		||||
        self.email = email;
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Update user's full name
 | 
			
		||||
    pub fn update_full_name(&mut self, full_name: String) {
 | 
			
		||||
        self.full_name = full_name;
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implement BaseModel for User
 | 
			
		||||
impl BaseModel for User {
 | 
			
		||||
    fn db_prefix() -> &'static str {
 | 
			
		||||
        "user"
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn get_id(&self) -> u32 {
 | 
			
		||||
        self.base_data.id
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    fn db_keys(&self) -> Vec<IndexKey> {
 | 
			
		||||
        vec![
 | 
			
		||||
            IndexKey {
 | 
			
		||||
                name: "username",
 | 
			
		||||
                value: self.username.clone(),
 | 
			
		||||
            },
 | 
			
		||||
            IndexKey {
 | 
			
		||||
                name: "email",
 | 
			
		||||
                value: self.email.clone(),
 | 
			
		||||
            },
 | 
			
		||||
            IndexKey {
 | 
			
		||||
                name: "is_active",
 | 
			
		||||
                value: self.is_active.to_string(),
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user