selene-core 0.3.1

selene-core is the backend for Selene, a local-first music player
Documentation
use std::borrow::Borrow;

use serde::{Deserialize, Serialize, de::DeserializeOwned};
use sled::{Db, Tree};

use crate::database::{
    self, CompareAndSwapTransaction, Createable, DatabaseError, EntryId, Mergeable, Patchable,
    deserialize_from_ivec, library_db, sled_get_all_raw, sled_get_batch_raw, sled_get_raw,
    sled_version_from_raw,
    transaction_args::{apply_cas_tx, db_transaction},
};

pub trait DatabaseEntry:
    Serialize + DeserializeOwned + for<'de> Deserialize<'de> + Clone + std::fmt::Debug + 'static
{
    type Id: EntryId<Entry = Self>;
    /// The current version number of the struct, used for updating
    const VERSION_NUMBER: u32;

    /// 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) -> database::Result<bool> {
        let db = library_db();
        Ok(Self::tree(&db).contains_key(id.as_bytes())?)
    }

    fn db_check_version(id: Self::Id) -> Result<Option<u32>, DatabaseError> {
        let db = library_db();
        Ok(sled_get_raw(&Self::tree(&db), id.as_bytes())?.map(sled_version_from_raw))
    }

    /// Creates a new [`Self`] in the database using [`Self::CreateArgs`][`Createable::CreateArgs`]
    fn db_create(args: <Self as Createable>::CreateArgs) -> database::Result<Self>
    where
        Self: Createable,
    {
        let mut cas_tx = CompareAndSwapTransaction::new();
        let created_id = Self::tx_create(&mut cas_tx, args)?;
        apply_cas_tx(cas_tx, false)?;

        Ok(Self::db_get(created_id)?.expect("Item was just created"))
    }

    /// Gets the entry of [`Self`] with the given `id`
    ///
    /// # Errors
    ///
    /// Errors if [`sled`] fails to retrieve the entry
    fn db_get(id: Self::Id) -> database::Result<Option<Self>> {
        let db = library_db();
        Ok(sled_get_raw(&Self::tree(&db), id.as_bytes())?.map(deserialize_from_ivec))
    }

    /// Gets all entries of [`Self`] in the database
    ///
    /// # Errors
    ///
    /// Errors if [`sled`] fails to retrieve any entry
    fn db_get_all() -> database::Result<Vec<Self>> {
        let db = library_db();
        Ok(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`
    ///
    /// # Errors
    ///
    /// Errors if [`sled`] fails to retrieve any entry
    fn db_get_batch<I, A>(ids: I) -> database::Result<Vec<Self>>
    where
        I: IntoIterator<Item = A>,
        A: Borrow<Self::Id>,
    {
        let db = library_db();
        let items = sled_get_batch_raw(
            &Self::tree(&db),
            ids.into_iter().map(|a| *a.borrow().as_bytes()),
        )?;

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

    /// Merges `self` into `into` in the database.
    ///
    /// All items that point to `self` will be relinked to `into` and `self` will be deleted from the database
    fn db_merge(&self, into: Self::Id) -> database::Result<()>
    where
        Self: Mergeable,
    {
        db_transaction(|cas_tx| self.tx_merge(into, cas_tx), None, false)?;
        Ok(())
    }

    /// Patches or inserts [`Self`] into the database
    ///
    /// Patching rules depend on how [`Self`] implements [`Patchable<Self>`]
    fn db_patch(&mut self) -> database::Result<()>
    where
        Self: Patchable<Self>,
    {
        let db = library_db();
        let old_raw = Self::tree(&db).get(self.id().as_bytes())?;
        let old: Option<Self> = old_raw.clone().map(deserialize_from_ivec);

        if let Some(mut old) = old {
            old.patch(self.clone());
            old.db_upsert()
        } else {
            self.db_upsert()
        }
    }

    fn pre_upsert(&mut self, _cas_tx: &CompareAndSwapTransaction) -> Result<(), DatabaseError> {
        Ok(())
    }

    fn db_upsert(&self) -> database::Result<()> {
        db_transaction(
            |cas_tx| cas_tx.tx_upsert(self.id(), Some(self.clone())),
            None,
            false,
        )
    }

    fn id(&self) -> Self::Id;

    fn tree(db: &Db) -> Tree;
}