lunar-lib 0.9.0

Common utilities for lunar applications
Documentation
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use sled::Tree;

use crate::database::{
    CompareAndSwapTransaction, Database, EntryId, TransactionError, deserialize_from_ivec,
    sled_get_all_raw, sled_get_batch_raw, sled_get_raw,
};

/// Defines an item that can be stored in the database
///
/// # Notes
///
/// For structs that should not be able to be written to the database manually, you can define [`read_only()`] function to return [`true`]
/// For structs where you want to do special logic to them before they are upserted, you can define [`pre_upsert()`]
pub trait DatabaseEntry:
    Serialize + DeserializeOwned + for<'de> Deserialize<'de> + Clone + std::fmt::Debug + 'static
{
    type Db: Database;
    type Id: EntryId<Entry = Self, IdDb = Self::Db>;

    /// The current version number of the struct, used for updating
    const VERSION_NUMBER: u32;

    /// A unique name for the tree of this entry
    const TREE_NAME: &str;

    /// Checks if an item exists in the database as lightly as possible
    ///
    /// # Errors
    ///
    /// Errors if [`sled`] fails to read the entry at the `id`
    fn db_check(id: Self::Id, db: &Self::Db) -> Result<bool, TransactionError> {
        Ok(Self::__tree(db).contains_key(*id)?)
    }

    /// Gets the entry of [`Self`] with the given `id` from the input `db`
    ///
    /// # Errors
    ///
    /// Errors if [`sled`] fails to retrieve the entry
    fn db_get(id: Self::Id, db: &Self::Db) -> Result<Option<Self>, TransactionError> {
        sled_get_raw(&Self::__tree(db), id.as_bytes())?
            .map(deserialize_from_ivec)
            .transpose()
    }

    /// Gets all entries of [`Self`] in from the input `db`
    ///
    /// # Errors
    ///
    /// Errors if [`sled`] fails to retrieve any entry
    fn db_get_all(db: &Self::Db) -> Result<Vec<Self>, TransactionError> {
        sled_get_all_raw(&Self::__tree(db))?
            .into_iter()
            .map(deserialize_from_ivec)
            .collect()
    }

    /// Gets all entries of [`Self`] in the database with the matching `ids` from the input `db`
    ///
    /// # Errors
    ///
    /// Errors if [`sled`] fails to retrieve any entry
    fn db_get_batch<I>(ids: I, db: &Self::Db) -> Result<Vec<Self>, TransactionError>
    where
        I: IntoIterator<Item = Self::Id>,
    {
        let items = sled_get_batch_raw(&Self::__tree(db), ids.into_iter().map(|a| *a.as_bytes()))?;

        items.into_iter().map(deserialize_from_ivec).collect()
    }

    /// This function is called before a value is upserted, allowing you to make last moment changes before its upserted into the database, like field sorting
    fn pre_upsert(
        &mut self,
        _cas_tx: &CompareAndSwapTransaction<Self::Db>,
    ) -> Result<(), TransactionError> {
        Ok(())
    }

    /// This function is called before a value is upserted, if it returns true, an error will be returned from the transaction.
    ///
    /// If you plan on making hardcoded variants of structs that are always in the database, you can set them to read only, and ensure they exist using [`Database::pre_open()`]
    fn read_only(&self) -> bool {
        false
    }

    /// Returns the ID that represents `self`
    fn id(&self) -> Self::Id;

    /// Returns the tree that `self` belongs to
    fn __tree(db: &Self::Db) -> Tree {
        db.db().open_tree(Self::TREE_NAME).unwrap()
    }
}