From 1343e61e0f6c967bc6a4ddd02d12bb88d0de1e43 Mon Sep 17 00:00:00 2001 From: Lee Smet Date: Wed, 23 Apr 2025 15:09:54 +0200 Subject: [PATCH] Expand index trait to also include key type Signed-off-by: Lee Smet --- heromodels/examples/basic_user_example.rs | 8 +-- heromodels/src/db.rs | 5 +- heromodels/src/db/hero.rs | 8 +-- heromodels/src/models/core/model.rs | 67 ++++++++++++++--------- heromodels/src/models/userexample/user.rs | 45 ++++++++------- 5 files changed, 75 insertions(+), 58 deletions(-) diff --git a/heromodels/examples/basic_user_example.rs b/heromodels/examples/basic_user_example.rs index 475502f..f12747d 100644 --- a/heromodels/examples/basic_user_example.rs +++ b/heromodels/examples/basic_user_example.rs @@ -77,11 +77,10 @@ fn main() { // Load all active users using the IsActive field index // TODO: expand Index type so it defines the type of the key - let key = true.to_string(); let active_users = db .collection::() .expect("can open user collection") - .get::(&key) + .get::(&true) .expect("can load stored users"); // We should have 2 active users assert_eq!(active_users.len(), 2); @@ -96,15 +95,14 @@ fn main() { let active_users = db .collection::() .expect("can open user collection") - .get::(&key) + .get::(&true) .expect("can load stored users"); assert_eq!(active_users.len(), 1); // And verify we still have 2 inactive users - let key = false.to_string(); let inactive_users = db .collection::() .expect("can open user collection") - .get::(&key) + .get::(&false) .expect("can load stored users"); assert_eq!(inactive_users.len(), 2); diff --git a/heromodels/src/db.rs b/heromodels/src/db.rs index 5f74393..7c1f0e1 100644 --- a/heromodels/src/db.rs +++ b/heromodels/src/db.rs @@ -22,7 +22,7 @@ where type Error: std::fmt::Debug; /// Get all items where the given index field is equal to key. - fn get(&self, key: K) -> Result, Error> + fn get(&self, key: &I::Key) -> Result, Error> where I: Index; @@ -33,7 +33,7 @@ where fn set(&self, value: &V) -> Result<(), Error>; /// Delete all items from the db with a given index. - fn delete(&self, key: K) -> Result<(), Error> + fn delete(&self, key: &I::Key) -> Result<(), Error> where I: Index; @@ -66,4 +66,3 @@ impl From for Error { Error::Encode(value) } } - diff --git a/heromodels/src/db/hero.rs b/heromodels/src/db/hero.rs index e918176..f66b92e 100644 --- a/heromodels/src/db/hero.rs +++ b/heromodels/src/db/hero.rs @@ -42,12 +42,12 @@ where { type Error = tst::Error; - fn get(&self, key: &str) -> Result, super::Error> + fn get(&self, key: &I::Key) -> Result, super::Error> where I: Index, { let mut index_db = self.index.lock().expect("can lock index DB"); - let index_key = Self::index_key(M::db_prefix(), I::key(), key); + let index_key = Self::index_key(M::db_prefix(), I::key(), &key.to_string()); let Some(object_ids) = Self::get_tst_value::>(&mut index_db, &index_key)? else { @@ -140,12 +140,12 @@ where Ok(()) } - fn delete(&self, key: &str) -> Result<(), super::Error> + fn delete(&self, key: &I::Key) -> Result<(), super::Error> where I: Index, { let mut index_db = self.index.lock().expect("can lock index db"); - let key = Self::index_key(M::db_prefix(), I::key(), key); + let key = Self::index_key(M::db_prefix(), I::key(), &key.to_string()); let raw_obj_ids = index_db.get(&key)?; let (obj_ids, _): (HashSet, _) = bincode::serde::decode_from_slice(&raw_obj_ids, BINCODE_CONFIG)?; diff --git a/heromodels/src/models/core/model.rs b/heromodels/src/models/core/model.rs index 02e5583..4d69907 100644 --- a/heromodels/src/models/core/model.rs +++ b/heromodels/src/models/core/model.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; 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, } @@ -25,13 +25,13 @@ impl IndexKeyBuilder { 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 { @@ -42,10 +42,14 @@ impl IndexKeyBuilder { } /// Unified trait for all models -pub trait Model: Debug + Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static { +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; - + 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 @@ -53,21 +57,27 @@ pub trait Model: Debug + Clone + Serialize + for<'de> Deserialize<'de> + Send + 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 { + 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 { + fn build(mut self) -> Self + where + Self: Sized, + { self.base_data_mut().update_modified(); self } @@ -78,6 +88,8 @@ 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; } @@ -87,13 +99,13 @@ pub trait Index { 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, } @@ -109,24 +121,24 @@ impl BaseModelData { 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(); @@ -151,31 +163,31 @@ impl BaseModelDataBuilder { 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(); @@ -197,14 +209,15 @@ macro_rules! impl_model { 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 } } }; -} \ No newline at end of file +} + diff --git a/heromodels/src/models/userexample/user.rs b/heromodels/src/models/userexample/user.rs index 6ee421a..c2a2924 100644 --- a/heromodels/src/models/userexample/user.rs +++ b/heromodels/src/models/userexample/user.rs @@ -1,21 +1,21 @@ +use crate::models::core::model::{BaseModelData, Index, IndexKey, Model}; use serde::{Deserialize, Serialize}; -use crate::models::core::model::{Model, BaseModelData, IndexKey, Index}; /// 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, } @@ -31,35 +31,35 @@ impl User { 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); @@ -70,7 +70,7 @@ impl User { pub fn deactivate(&mut self) { self.is_active = false; } - + /// Activate the user pub fn activate(&mut self) { self.is_active = true; @@ -82,16 +82,16 @@ 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 { @@ -119,6 +119,8 @@ pub struct IsActive; impl Index for UserName { type Model = User; + type Key = str; + fn key() -> &'static str { "username" } @@ -127,6 +129,8 @@ impl Index for UserName { impl Index for Email { type Model = User; + type Key = str; + fn key() -> &'static str { "email" } @@ -135,7 +139,10 @@ impl Index for Email { impl Index for IsActive { type Model = User; + type Key = bool; + fn key() -> &'static str { "is_active" } -} \ No newline at end of file +} +