db/heromodels_core/src/lib.rs
2025-05-17 13:47:17 +03:00

235 lines
6.5 KiB
Rust

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<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;
/// 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
///
/// # ID Management
///
/// The `id` field is managed automatically by OurDB when using incremental mode:
///
/// 1. When creating a new model, the `id` field is initialized to 0.
/// 2. When saving the model to the database with `set()`, OurDB will:
/// - If the ID is 0, auto-generate a new ID and update the model.
/// - If the ID is not 0, use the provided ID for updates.
///
/// # Important Notes
///
/// - Never manually set the `id` field to a specific value. Always use 0 for new models.
/// - The ID 0 is reserved for new models and should not be used for existing models.
/// - After saving a model, use the ID returned by `set()` to retrieve the model from the database.
/// - The original model passed to `set()` is not modified; you need to retrieve the updated model.
///
/// # Example
///
/// ```rust
/// // Create a new model with ID 0
/// let user = User::new()
/// .username("johndoe")
/// .email("john.doe@example.com")
/// .build();
///
/// // Save the model and get the assigned ID
/// let user_id = db.collection().set(&user).expect("Failed to save user");
///
/// // Retrieve the model with the assigned ID
/// let db_user = db.collection().get_by_id(user_id).expect("Failed to get user");
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaseModelData {
/// Unique incremental ID - will be auto-generated by OurDB
///
/// This field is automatically managed by OurDB:
/// - For new models, set this to 0 and OurDB will auto-generate an ID.
/// - For existing models, this will be the ID assigned by OurDB.
///
/// Do not manually set this field to a specific value.
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 with ID set to 0
/// The ID will be auto-generated by OurDB when the model is saved
pub fn new() -> Self {
let now = chrono::Utc::now().timestamp();
Self {
id: 0, // This will be replaced by OurDB with an auto-generated ID
created_at: now,
modified_at: now,
comments: Vec::new(),
}
}
/// Create a new BaseModelDataBuilder
pub fn builder() -> BaseModelDataBuilder {
BaseModelDataBuilder::new()
}
/// 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();
}
/// Update the ID of this model
pub fn update_id(&mut self, id: u32) {
self.id = id;
}
}
/// Builder for BaseModelData
pub struct BaseModelDataBuilder {
created_at: Option<i64>,
modified_at: Option<i64>,
comments: Vec<u32>,
}
impl BaseModelDataBuilder {
/// Create a new BaseModelDataBuilder
pub fn new() -> Self {
Self {
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: 0, // This will be replaced by OurDB with an auto-generated ID
created_at: self.created_at.unwrap_or(now),
modified_at: self.modified_at.unwrap_or(now),
comments: self.comments,
}
}
}