use std::borrow::Borrow; use heromodels_core::{Index, Model}; use serde::{Deserialize, Serialize}; pub mod hero; pub mod postgres; pub trait Db { /// Error type returned by database operations. type Error: std::fmt::Debug; /// Open the collection for a specific model. This method must create the collection if it does not exist. fn collection(&self) -> Result, Error>; } /// A collection stores a specific model under a specific key pub trait Collection where K: Serialize, V: Serialize + for<'a> Deserialize<'a>, { /// Error type for database operations type Error: std::fmt::Debug; /// Get all items where the given index field is equal to key. fn get(&self, key: &Q) -> Result, Error> where I: Index, I::Key: Borrow, Q: ToString + Serialize + core::fmt::Debug + Sync + ?Sized; /// Get an object from its ID. This does not use an index lookup fn get_by_id(&self, id: u32) -> Result, Error>; /// Store an item in the DB and return the assigned ID and the updated model. /// /// # Important Notes /// - This method does not modify the original model passed as an argument. /// - For new models (with ID 0), an ID will be auto-generated by OurDB. /// - The returned model will have the correct ID and should be used instead of the original model. /// - The original model should not be used after calling this method, as it may have /// an inconsistent state compared to what's in the database. /// - ID 0 is reserved for new models and should not be used for existing models. fn set(&self, value: &V) -> Result<(u32, V), Error>; /// Delete all items from the db with a given index. fn delete(&self, key: &Q) -> Result<(), Error> where I: Index, I::Key: Borrow, Q: ToString + Serialize + core::fmt::Debug + Sync + ?Sized; /// Delete an object with a given ID fn delete_by_id(&self, id: u32) -> Result<(), Error>; /// Get all objects from the collection fn get_all(&self) -> Result, Error>; /// Begin a transaction for this collection fn begin_transaction( &self, ) -> Result>, Error>; } /// Errors returned by the DB implementation #[derive(Debug)] pub enum Error { /// Error in the underlying database DB(E), /// Error decoding a stored model Decode(bincode::error::DecodeError), /// Error encoding a model for storage Encode(bincode::error::EncodeError), /// Invalid ID used (e.g., using ID 0 for an existing model) InvalidId(String), /// ID mismatch (e.g., expected ID 5 but got ID 6) IdMismatch(String), /// ID collision (e.g., trying to create a model with an ID that already exists) IdCollision(String), /// Type error (e.g., trying to get a model of the wrong type) TypeError, } impl From for Error { fn from(value: bincode::error::DecodeError) -> Self { Error::Decode(value) } } impl From for Error { fn from(value: bincode::error::EncodeError) -> Self { Error::Encode(value) } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Error::DB(e) => write!(f, "Database error: {}", e), Error::Decode(e) => write!(f, "Failed to decode model: {}", e), Error::Encode(e) => write!(f, "Failed to encode model: {}", e), Error::InvalidId(s) => write!(f, "Invalid ID: {}", s), Error::IdMismatch(s) => write!(f, "ID Mismatch: {}", s), Error::IdCollision(s) => write!(f, "ID Collision: {}", s), Error::TypeError => write!(f, "Type error"), } } } /// A transaction that can be committed or rolled back pub trait Transaction { /// Error type for transaction operations type Error: std::fmt::Debug; /// Begin a transaction fn begin(&self) -> Result<(), Error>; /// Commit a transaction fn commit(&self) -> Result<(), Error>; /// Roll back a transaction fn rollback(&self) -> Result<(), Error>; /// Check if a transaction is active fn is_active(&self) -> bool; }