This commit is contained in:
2025-04-22 08:24:17 +04:00
parent d75de1e73c
commit cad285fd59
13 changed files with 659 additions and 129 deletions

View File

@@ -1,63 +0,0 @@
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),
},
]
}
}

View File

@@ -0,0 +1,56 @@
use serde::{Deserialize, Serialize};
use crate::core::model::{BaseModel, BaseModelData, IndexKey, ModelBuilder};
use crate::impl_model_builder;
/// Represents a comment on a model
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Comment {
pub base_data: BaseModelData,
pub user_id: u32,
pub content: String,
}
impl Comment {
/// Create a new comment
pub fn new(id: u32) -> Self {
Self {
base_data: BaseModelData::new(id),
user_id: 0,
content: String::new(),
}
}
/// Set the user ID
pub fn user_id(mut self, id: u32) -> Self {
self.user_id = id;
self
}
/// Set the content
pub fn content(mut self, content: impl ToString) -> Self {
self.content = content.to_string();
self
}
}
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(),
},
]
}
}
// Implement ModelBuilder for Comment
impl_model_builder!(Comment);

View File

@@ -0,0 +1,10 @@
pub mod model;
pub mod comment;
// Re-export key types for convenience
pub use model::{BaseModel, BaseModelData, IndexKey, IndexKeyBuilder, ModelBuilder};
pub use comment::Comment;
pub use crate::impl_model_builder;

View File

@@ -0,0 +1,7 @@
// Export submodules
pub mod model;
pub mod comment;
// Re-export key types for convenience
pub use model::{BaseModel, BaseModelData, IndexKey, IndexKeyBuilder, ModelBuilder};
pub use comment::Comment;

View File

@@ -100,7 +100,7 @@ impl BaseModelData {
/// 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();
self.update_modified();
}
/// Update the modified timestamp
@@ -164,11 +164,29 @@ impl BaseModelDataBuilder {
}
}
/// Trait for model builders that have a base_data field
pub trait ModelBuilder: Sized {
/// Get a mutable reference to the base_data field
fn base_data_mut(&mut self) -> &mut BaseModelData;
/// Set the ID for this model
fn id(mut self, id: u32) -> Self {
self.base_data_mut().id = id;
self
}
/// Build the model, updating the modified timestamp
fn build(mut self) -> Self {
self.base_data_mut().update_modified();
self
}
}
/// 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 {
impl $crate::core::model::BaseModel for $type {
fn db_prefix() -> &'static str {
$prefix
}
@@ -178,4 +196,16 @@ macro_rules! impl_base_model {
}
}
};
}
/// Macro to implement ModelBuilder for a struct that contains a base_data field of type BaseModelData
#[macro_export]
macro_rules! impl_model_builder {
($type:ty) => {
impl $crate::core::model::ModelBuilder for $type {
fn base_data_mut(&mut self) -> &mut $crate::core::model::BaseModelData {
&mut self.base_data
}
}
};
}

View File

@@ -1,47 +1,12 @@
//! # 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.
// Export core module
pub mod core;
pub mod model;
pub mod comment;
pub mod user;
// Export userexample module
pub mod userexample;
// Re-export key types for convenience
pub use model::{BaseModel, BaseModelData, IndexKey, impl_base_model};
pub use comment::Comment;
pub use user::User;
pub use core::{BaseModel, BaseModelData, IndexKey, ModelBuilder};
pub use core::Comment;
pub use userexample::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"));
// No need to re-export macros as they are already exported at the crate root

View File

@@ -0,0 +1,6 @@
// Export user module
pub mod user;
// Re-export User for convenience
pub use user::User;

View File

@@ -0,0 +1,5 @@
// Export user module
pub mod user;
// Re-export User for convenience
pub use user::User;

View File

@@ -1,5 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::model::{BaseModel, BaseModelData, IndexKey};
use crate::core::model::{BaseModel, BaseModelData, IndexKey, ModelBuilder};
use crate::impl_model_builder;
/// Represents a user in the system
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -22,38 +23,58 @@ pub struct User {
impl User {
/// Create a new user
pub fn new(id: u32, username: String, email: String, full_name: String) -> Self {
pub fn new(id: u32) -> Self {
Self {
base_data: BaseModelData::new(id),
username,
email,
full_name,
username: String::new(),
email: String::new(),
full_name: String::new(),
is_active: true,
}
}
/// Set the username
pub fn username(mut self, username: impl ToString) -> Self {
self.username = username.to_string();
self
}
/// Set the email
pub fn email(mut self, email: impl ToString) -> Self {
self.email = email.to_string();
self
}
/// Set the full name
pub fn full_name(mut self, full_name: impl ToString) -> Self {
self.full_name = full_name.to_string();
self
}
/// Set whether the user is active
pub fn is_active(mut self, is_active: bool) -> Self {
self.is_active = is_active;
self
}
/// Add a comment ID
pub fn add_comment(mut self, comment_id: u32) -> Self {
self.base_data.add_comment(comment_id);
self
}
/// 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();
}
}
@@ -83,4 +104,7 @@ impl BaseModel for User {
},
]
}
}
}
// Implement ModelBuilder for User
impl_model_builder!(User);