From b8e1449ddb40da126ca8fad439bfa72d6610a2de Mon Sep 17 00:00:00 2001 From: Lee Smet Date: Fri, 25 Apr 2025 13:58:17 +0200 Subject: [PATCH] Restructure crates for correct proc macro usage Signed-off-by: Lee Smet --- .../Cargo.lock | 0 .../Cargo.toml | 0 .../src/lib.rs | 15 +- .../tests/test_model_macro.rs | 0 heromodels/Cargo.lock | 9 + heromodels/Cargo.toml | 3 +- heromodels/examples/basic_user_example.rs | 5 +- heromodels/examples/custom_model_example.rs | 4 +- heromodels/examples/model_macro_example.rs | 30 +- heromodels/examples/simple_model_example.rs | 15 +- heromodels/src/db.rs | 2 +- heromodels/src/db/hero.rs | 5 +- heromodels/src/models/core/comment.rs | 31 +- heromodels/src/models/core/mod.rs | 6 +- heromodels/src/models/core/model.rs | 223 ------------ heromodels/src/models/mod.rs | 4 +- heromodels/src/models/userexample/user.rs | 71 +--- heromodels_core/Cargo.lock | 318 ++++++++++++++++++ heromodels_core/Cargo.toml | 8 + heromodels_core/src/lib.rs | 201 +++++++++++ 20 files changed, 588 insertions(+), 362 deletions(-) rename {heromodels/heromodels-derive => heromodels-derive}/Cargo.lock (100%) rename {heromodels/heromodels-derive => heromodels-derive}/Cargo.toml (100%) rename {heromodels/heromodels-derive => heromodels-derive}/src/lib.rs (92%) rename {heromodels/heromodels-derive => heromodels-derive}/tests/test_model_macro.rs (100%) create mode 100644 heromodels_core/Cargo.lock create mode 100644 heromodels_core/Cargo.toml create mode 100644 heromodels_core/src/lib.rs diff --git a/heromodels/heromodels-derive/Cargo.lock b/heromodels-derive/Cargo.lock similarity index 100% rename from heromodels/heromodels-derive/Cargo.lock rename to heromodels-derive/Cargo.lock diff --git a/heromodels/heromodels-derive/Cargo.toml b/heromodels-derive/Cargo.toml similarity index 100% rename from heromodels/heromodels-derive/Cargo.toml rename to heromodels-derive/Cargo.toml diff --git a/heromodels/heromodels-derive/src/lib.rs b/heromodels-derive/src/lib.rs similarity index 92% rename from heromodels/heromodels-derive/src/lib.rs rename to heromodels-derive/src/lib.rs index 72f8b94..94c62a5 100644 --- a/heromodels/heromodels-derive/src/lib.rs +++ b/heromodels-derive/src/lib.rs @@ -96,7 +96,7 @@ pub fn model(_attr: TokenStream, item: TokenStream) -> TokenStream { // Generate Model trait implementation let db_keys_impl = if indexed_fields.is_empty() { quote! { - fn db_keys(&self) -> Vec { + fn db_keys(&self) -> Vec { Vec::new() } } @@ -107,7 +107,7 @@ pub fn model(_attr: TokenStream, item: TokenStream) -> TokenStream { .cloned() .unwrap_or(field_name.to_string()); quote! { - crate::models::IndexKey { + heromodels_core::IndexKey { name: #name_str, value: self.#field_name.to_string(), } @@ -115,7 +115,7 @@ pub fn model(_attr: TokenStream, item: TokenStream) -> TokenStream { }); quote! { - fn db_keys(&self) -> Vec { + fn db_keys(&self) -> Vec { vec![ #(#field_keys),* ] @@ -124,7 +124,7 @@ pub fn model(_attr: TokenStream, item: TokenStream) -> TokenStream { }; let model_impl = quote! { - impl crate::models::Model for #struct_name { + impl heromodels_core::Model for #struct_name { fn db_prefix() -> &'static str { #db_prefix } @@ -133,7 +133,7 @@ pub fn model(_attr: TokenStream, item: TokenStream) -> TokenStream { self.base_data.id } - fn base_data_mut(&mut self) -> &mut crate::models::BaseModelData { + fn base_data_mut(&mut self) -> &mut heromodels_core::BaseModelData { &mut self.base_data } @@ -162,7 +162,7 @@ pub fn model(_attr: TokenStream, item: TokenStream) -> TokenStream { let index_impl = quote! { pub struct #index_struct_name; - impl crate::models::Index for #index_struct_name { + impl heromodels_core::Index for #index_struct_name { type Model = super::#struct_name; type Key = #field_type; @@ -176,8 +176,9 @@ pub fn model(_attr: TokenStream, item: TokenStream) -> TokenStream { } if !index_impls.is_empty() { + let index_mod_name = format_ident!("{}_index", db_prefix); index_impls = quote! { - pub mod index { + pub mod #index_mod_name { #index_impls } } diff --git a/heromodels/heromodels-derive/tests/test_model_macro.rs b/heromodels-derive/tests/test_model_macro.rs similarity index 100% rename from heromodels/heromodels-derive/tests/test_model_macro.rs rename to heromodels-derive/tests/test_model_macro.rs diff --git a/heromodels/Cargo.lock b/heromodels/Cargo.lock index 726bf29..21e0555 100644 --- a/heromodels/Cargo.lock +++ b/heromodels/Cargo.lock @@ -263,6 +263,7 @@ dependencies = [ "chrono", "fjall", "heromodels-derive", + "heromodels_core", "ourdb", "serde", "tst", @@ -277,6 +278,14 @@ dependencies = [ "syn", ] +[[package]] +name = "heromodels_core" +version = "0.1.0" +dependencies = [ + "chrono", + "serde", +] + [[package]] name = "iana-time-zone" version = "0.1.63" diff --git a/heromodels/Cargo.toml b/heromodels/Cargo.toml index 948edbb..bf1b27b 100644 --- a/heromodels/Cargo.toml +++ b/heromodels/Cargo.toml @@ -12,4 +12,5 @@ chrono = { version = "0.4", features = ["serde"] } fjall = "2.9.0" ourdb = { path = "../ourdb" } tst = { path = "../tst" } -heromodels-derive = { path = "./heromodels-derive" } +heromodels-derive = { path = "../heromodels-derive" } +heromodels_core = { path = "../heromodels_core" } diff --git a/heromodels/examples/basic_user_example.rs b/heromodels/examples/basic_user_example.rs index 7a64391..3ad2f1d 100644 --- a/heromodels/examples/basic_user_example.rs +++ b/heromodels/examples/basic_user_example.rs @@ -1,6 +1,7 @@ use heromodels::db::{Collection, Db}; -use heromodels::models::userexample::user::index::{is_active, username}; -use heromodels::models::{Comment, Model, User}; +use heromodels::models::userexample::user::user_index::{is_active, username}; +use heromodels::models::{Comment, User}; +use heromodels_core::Model; fn main() { let index_db = tst::TST::new("/tmp/ourdb/tst", true).expect("can create index DB"); diff --git a/heromodels/examples/custom_model_example.rs b/heromodels/examples/custom_model_example.rs index 408b871..8558036 100644 --- a/heromodels/examples/custom_model_example.rs +++ b/heromodels/examples/custom_model_example.rs @@ -1,5 +1,5 @@ -use heromodels::model; -use heromodels::models::core::model::{BaseModelData, Model}; +use heromodels_core::{BaseModelData, Model}; +use heromodels_derive::model; use serde::{Deserialize, Serialize}; // Define a custom attribute for indexing diff --git a/heromodels/examples/model_macro_example.rs b/heromodels/examples/model_macro_example.rs index c1b6f85..04602fd 100644 --- a/heromodels/examples/model_macro_example.rs +++ b/heromodels/examples/model_macro_example.rs @@ -1,58 +1,58 @@ -use heromodels::model; -use heromodels::models::core::model::{BaseModelData, Model, Index, IndexKey}; -use serde::{Serialize, Deserialize}; +use heromodels_core::{BaseModelData, Model}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; // Basic usage #[derive(Debug, Clone, Serialize, Deserialize)] #[model] pub struct SimpleUser { pub base_data: BaseModelData, - + #[index] pub login: String, - + pub full_name: String, } // With customization options #[derive(Debug, Clone, Serialize, Deserialize)] -#[model(prefix = "custom_user")] +#[model] pub struct CustomUser { pub base_data: BaseModelData, - + #[index(name = "user_name")] pub login_name: String, - + #[index] pub is_active: bool, - + pub full_name: String, } fn main() { println!("Hero Models - Model Macro Example"); println!("================================="); - + // Example usage of the generated implementations println!("SimpleUser DB Prefix: {}", SimpleUser::db_prefix()); println!("CustomUser DB Prefix: {}", CustomUser::db_prefix()); - + let user = SimpleUser { base_data: BaseModelData::new(1), login: "johndoe".to_string(), full_name: "John Doe".to_string(), }; - + let custom_user = CustomUser { base_data: BaseModelData::new(2), login_name: "janesmith".to_string(), is_active: true, full_name: "Jane Smith".to_string(), }; - + println!("\nSimpleUser ID: {}", user.get_id()); println!("SimpleUser DB Keys: {:?}", user.db_keys()); - + println!("\nCustomUser ID: {}", custom_user.get_id()); println!("CustomUser DB Keys: {:?}", custom_user.db_keys()); -} \ No newline at end of file +} diff --git a/heromodels/examples/simple_model_example.rs b/heromodels/examples/simple_model_example.rs index c8a0355..c24f9b2 100644 --- a/heromodels/examples/simple_model_example.rs +++ b/heromodels/examples/simple_model_example.rs @@ -1,6 +1,6 @@ -use heromodels::model; -use heromodels::models::core::model::{BaseModelData, Model, IndexKey}; -use serde::{Serialize, Deserialize}; +use heromodels_core::{BaseModelData, Model}; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; // Basic usage #[derive(Debug, Clone, Serialize, Deserialize)] @@ -14,16 +14,17 @@ pub struct SimpleUser { fn main() { println!("Hero Models - Simple Model Example"); println!("=================================="); - + // Example usage of the generated implementation println!("SimpleUser DB Prefix: {}", SimpleUser::db_prefix()); - + let user = SimpleUser { base_data: BaseModelData::new(1), login: "johndoe".to_string(), full_name: "John Doe".to_string(), }; - + println!("\nSimpleUser ID: {}", user.get_id()); println!("SimpleUser DB Keys: {:?}", user.db_keys()); -} \ No newline at end of file +} + diff --git a/heromodels/src/db.rs b/heromodels/src/db.rs index 130a280..63ee811 100644 --- a/heromodels/src/db.rs +++ b/heromodels/src/db.rs @@ -1,6 +1,6 @@ use std::borrow::Borrow; -use crate::models::{Index, Model}; +use heromodels_core::{Index, Model}; use serde::{Deserialize, Serialize}; pub mod fjall; diff --git a/heromodels/src/db/hero.rs b/heromodels/src/db/hero.rs index e064bf0..108389d 100644 --- a/heromodels/src/db/hero.rs +++ b/heromodels/src/db/hero.rs @@ -1,8 +1,7 @@ +use heromodels_core::{Index, Model}; use ourdb::OurDBSetArgs; use serde::Deserialize; -use crate::models::{Index, Model}; - use std::{ borrow::Borrow, collections::HashSet, @@ -30,7 +29,7 @@ impl OurDB { impl super::Db for OurDB { type Error = tst::Error; - fn collection( + fn collection( &self, ) -> Result, super::Error> { Ok(self.clone()) diff --git a/heromodels/src/models/core/comment.rs b/heromodels/src/models/core/comment.rs index ca362aa..110383a 100644 --- a/heromodels/src/models/core/comment.rs +++ b/heromodels/src/models/core/comment.rs @@ -1,10 +1,13 @@ +use heromodels_core::BaseModelData; +use heromodels_derive::model; use serde::{Deserialize, Serialize}; -use crate::models::core::model::{Model, BaseModelData, IndexKey}; /// Represents a comment on a model #[derive(Debug, Clone, Serialize, Deserialize)] +#[model] pub struct Comment { pub base_data: BaseModelData, + #[index] pub user_id: u32, pub content: String, } @@ -24,34 +27,10 @@ impl Comment { self.user_id = id; self } - + /// Set the content pub fn content(mut self, content: impl ToString) -> Self { self.content = content.to_string(); self } } - -// Implement the Model trait for Comment -impl Model for Comment { - fn db_prefix() -> &'static str { - "comment" - } - - fn get_id(&self) -> u32 { - self.base_data.id - } - - fn base_data_mut(&mut self) -> &mut BaseModelData { - &mut self.base_data - } - - fn db_keys(&self) -> Vec { - vec![ - IndexKey { - name: "user_id", - value: self.user_id.to_string(), - }, - ] - } -} diff --git a/heromodels/src/models/core/mod.rs b/heromodels/src/models/core/mod.rs index 2524f23..f7fde31 100644 --- a/heromodels/src/models/core/mod.rs +++ b/heromodels/src/models/core/mod.rs @@ -1,7 +1,7 @@ // Export submodules -pub mod model; pub mod comment; +pub mod model; // Re-export key types for convenience -pub use model::{Model, BaseModelData, IndexKey, IndexKeyBuilder, Index}; -pub use comment::Comment; \ No newline at end of file +pub use comment::Comment; + diff --git a/heromodels/src/models/core/model.rs b/heromodels/src/models/core/model.rs index 4d69907..e69de29 100644 --- a/heromodels/src/models/core/model.rs +++ b/heromodels/src/models/core/model.rs @@ -1,223 +0,0 @@ -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; - - /// 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 - } -} - -/// 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 -#[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.update_modified(); - } - - /// 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 Model for a struct that contains a base_data field of type BaseModelData -#[macro_export] -macro_rules! impl_model { - // Basic implementation with default db_keys - ($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 - } - } - }; -} - diff --git a/heromodels/src/models/mod.rs b/heromodels/src/models/mod.rs index a12ce27..c2fc43d 100644 --- a/heromodels/src/models/mod.rs +++ b/heromodels/src/models/mod.rs @@ -3,6 +3,6 @@ pub mod core; pub mod userexample; // Re-export key types for convenience -pub use core::model::{Model, BaseModelData, IndexKey, Index}; pub use core::Comment; -pub use userexample::User; \ No newline at end of file +pub use userexample::User; + diff --git a/heromodels/src/models/userexample/user.rs b/heromodels/src/models/userexample/user.rs index d906567..dc41746 100644 --- a/heromodels/src/models/userexample/user.rs +++ b/heromodels/src/models/userexample/user.rs @@ -1,4 +1,4 @@ -use crate::models::core::model::BaseModelData; +use heromodels_core::BaseModelData; use heromodels_derive::model; use serde::{Deserialize, Serialize}; @@ -81,72 +81,3 @@ impl User { self.is_active = true; } } - -// Implement the Model trait for User -// impl Model for User { -// fn db_prefix() -> &'static str { -// "user" -// } -// -// fn get_id(&self) -> u32 { -// self.base_data.id -// } -// -// //WHY? -// fn base_data_mut(&mut self) -> &mut BaseModelData { -// &mut self.base_data -// } -// -// fn db_keys(&self) -> Vec { -// 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(), -// }, -// ] -// } -// } -// -// // Marker structs for indexed fields -// -// pub struct UserName; -// pub struct Email; -// pub struct IsActive; -// -// impl Index for UserName { -// type Model = User; -// -// type Key = str; -// -// fn key() -> &'static str { -// "username" -// } -// } -// -// impl Index for Email { -// type Model = User; -// -// type Key = str; -// -// fn key() -> &'static str { -// "email" -// } -// } -// -// impl Index for IsActive { -// type Model = User; -// -// type Key = bool; -// -// fn key() -> &'static str { -// "is_active" -// } -// } diff --git a/heromodels_core/Cargo.lock b/heromodels_core/Cargo.lock new file mode 100644 index 0000000..767687b --- /dev/null +++ b/heromodels_core/Cargo.lock @@ -0,0 +1,318 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "cc" +version = "1.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "heromodels_core" +version = "0.1.0" +dependencies = [ + "chrono", + "serde", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] diff --git a/heromodels_core/Cargo.toml b/heromodels_core/Cargo.toml new file mode 100644 index 0000000..5a21470 --- /dev/null +++ b/heromodels_core/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "heromodels_core" +version = "0.1.0" +edition = "2024" + +[dependencies] +chrono = "0.4.40" +serde = { version = "1.0.219", features = ["derive"] } diff --git a/heromodels_core/src/lib.rs b/heromodels_core/src/lib.rs new file mode 100644 index 0000000..9bab2c7 --- /dev/null +++ b/heromodels_core/src/lib.rs @@ -0,0 +1,201 @@ +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; + + /// 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 + } +} + +/// 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 +#[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.update_modified(); + } + + /// 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, + } + } +}