merge branches and cleanup db
This commit is contained in:
277
heromodels/docs/model_trait_unification_plan.md
Normal file
277
heromodels/docs/model_trait_unification_plan.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# Model Trait Unification Plan
|
||||
|
||||
## Introduction
|
||||
|
||||
This document outlines the plan to unify the `BaseModel` and `ModelBuilder` traits in the heromodels crate into a single `Model` trait. The goal is to simplify the codebase, reduce boilerplate, and make the API more intuitive while maintaining all existing functionality.
|
||||
|
||||
## Current Structure
|
||||
|
||||
Currently, the codebase has two separate traits:
|
||||
|
||||
1. **BaseModel** - Provides database-related functionality:
|
||||
- `db_prefix()` - Returns a string prefix for database operations
|
||||
- `db_keys()` - Returns index keys for the model
|
||||
- `get_id()` - Returns the model's unique ID
|
||||
|
||||
2. **ModelBuilder** - Provides builder pattern functionality:
|
||||
- `base_data_mut()` - Gets a mutable reference to the base data
|
||||
- `id()` - Sets the ID for the model
|
||||
- `build()` - Finalizes the model by updating timestamps
|
||||
|
||||
Each model (like User and Comment) implements both traits separately, and there are two separate macros for implementing these traits:
|
||||
|
||||
```rust
|
||||
// For BaseModel
|
||||
impl_base_model!(User, "user");
|
||||
|
||||
// For ModelBuilder
|
||||
impl_model_builder!(User);
|
||||
```
|
||||
|
||||
This leads to duplication and cognitive overhead when working with models.
|
||||
|
||||
## Proposed Structure
|
||||
|
||||
We will create a unified `Model` trait that combines all the functionality from both existing traits:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Model {
|
||||
<<trait>>
|
||||
+db_prefix() static str
|
||||
+db_keys() Vec~IndexKey~
|
||||
+get_id() u32
|
||||
+base_data_mut() &mut BaseModelData
|
||||
+id(u32) Self
|
||||
+build() Self
|
||||
}
|
||||
|
||||
class User {
|
||||
+base_data BaseModelData
|
||||
+username String
|
||||
+email String
|
||||
+full_name String
|
||||
+is_active bool
|
||||
}
|
||||
|
||||
class Comment {
|
||||
+base_data BaseModelData
|
||||
+user_id u32
|
||||
+content String
|
||||
}
|
||||
|
||||
Model <|-- User
|
||||
Model <|-- Comment
|
||||
```
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### 1. Update model.rs
|
||||
|
||||
Create the new `Model` trait that combines all methods from both existing traits:
|
||||
|
||||
```rust
|
||||
/// 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
|
||||
fn db_keys(&self) -> Vec<IndexKey> {
|
||||
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;
|
||||
|
||||
/// Set the ID for this model
|
||||
fn id(mut self, id: u32) -> Self where Self: Sized {
|
||||
self.base_data_mut().id = id;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the model, updating the modified timestamp
|
||||
fn build(mut self) -> Self where Self: Sized {
|
||||
self.base_data_mut().update_modified();
|
||||
self
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Create a new implementation macro that implements all required methods:
|
||||
|
||||
```rust
|
||||
/// Macro to implement Model for a struct that contains a base_data field of type BaseModelData
|
||||
#[macro_export]
|
||||
macro_rules! impl_model {
|
||||
($type:ty, $prefix:expr) => {
|
||||
impl $crate::core::model::Model for $type {
|
||||
fn db_prefix() -> &'static str {
|
||||
$prefix
|
||||
}
|
||||
|
||||
fn get_id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
|
||||
fn base_data_mut(&mut self) -> &mut $crate::core::model::BaseModelData {
|
||||
&mut self.base_data
|
||||
}
|
||||
|
||||
// Other methods have default implementations
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Mark the old traits and macros as deprecated (or remove them entirely):
|
||||
|
||||
```rust
|
||||
#[deprecated(
|
||||
since = "0.2.0",
|
||||
note = "Use the unified Model trait instead"
|
||||
)]
|
||||
pub trait BaseModel { /* ... */ }
|
||||
|
||||
#[deprecated(
|
||||
since = "0.2.0",
|
||||
note = "Use the unified Model trait instead"
|
||||
)]
|
||||
pub trait ModelBuilder { /* ... */ }
|
||||
|
||||
#[deprecated(
|
||||
since = "0.2.0",
|
||||
note = "Use impl_model instead"
|
||||
)]
|
||||
#[macro_export]
|
||||
macro_rules! impl_base_model { /* ... */ }
|
||||
|
||||
#[deprecated(
|
||||
since = "0.2.0",
|
||||
note = "Use impl_model instead"
|
||||
)]
|
||||
#[macro_export]
|
||||
macro_rules! impl_model_builder { /* ... */ }
|
||||
```
|
||||
|
||||
### 2. Update User and Comment Implementations
|
||||
|
||||
For User:
|
||||
|
||||
```rust
|
||||
// Implement Model for User
|
||||
impl_model!(User, "user");
|
||||
|
||||
// Custom implementation of db_keys
|
||||
impl Model for User {
|
||||
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(),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For Comment:
|
||||
|
||||
```rust
|
||||
// Implement Model for Comment
|
||||
impl_model!(Comment, "comment");
|
||||
|
||||
// Custom implementation of db_keys
|
||||
impl Model for Comment {
|
||||
fn db_keys(&self) -> Vec<IndexKey> {
|
||||
vec![
|
||||
IndexKey {
|
||||
name: "user_id",
|
||||
value: self.user_id.to_string(),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Update Imports and References
|
||||
|
||||
Update the lib.rs file to export the new trait and macro:
|
||||
|
||||
```rust
|
||||
// Export core module
|
||||
pub mod core;
|
||||
|
||||
// Export userexample module
|
||||
pub mod userexample;
|
||||
|
||||
// Re-export key types for convenience
|
||||
pub use core::model::{Model, BaseModelData, IndexKey};
|
||||
pub use core::Comment;
|
||||
pub use userexample::User;
|
||||
|
||||
// Re-export macros
|
||||
pub use crate::impl_model;
|
||||
```
|
||||
|
||||
Update the example code to use the new trait:
|
||||
|
||||
```rust
|
||||
use heromodels::{Model, Comment, User};
|
||||
|
||||
fn main() {
|
||||
println!("Hero Models - Basic Usage Example");
|
||||
println!("================================");
|
||||
|
||||
// Create a new user using the fluent interface
|
||||
let user = User::new(1)
|
||||
.username("johndoe")
|
||||
.email("john.doe@example.com")
|
||||
.full_name("John Doe")
|
||||
.build();
|
||||
|
||||
println!("Created user: {:?}", user);
|
||||
println!("User ID: {}", user.get_id());
|
||||
println!("User DB Prefix: {}", User::db_prefix());
|
||||
|
||||
// Create a comment for the user
|
||||
let comment = Comment::new(1)
|
||||
.user_id(2) // commenter's user ID
|
||||
.content("This is a comment on the user")
|
||||
.build();
|
||||
|
||||
println!("\nCreated comment: {:?}", comment);
|
||||
println!("Comment ID: {}", comment.get_id());
|
||||
println!("Comment DB Prefix: {}", Comment::db_prefix());
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Testing
|
||||
|
||||
Run the example to ensure it works as expected:
|
||||
|
||||
```bash
|
||||
cargo run --example basic_user_example
|
||||
```
|
||||
|
||||
Verify that all functionality is preserved and that the output matches the expected output.
|
||||
|
||||
## Benefits of This Approach
|
||||
|
||||
1. **Simplified Code Structure**: One trait instead of two means less cognitive overhead
|
||||
2. **Reduced Boilerplate**: A single macro implementation reduces repetitive code
|
||||
3. **Improved Usability**: No need to import multiple traits or worry about which trait provides which method
|
||||
4. **Maintained Functionality**: All existing functionality is preserved
|
||||
5. **Better Encapsulation**: The model concept is more cohesive as a single trait
|
Reference in New Issue
Block a user