netabase_store 0.0.8

A type-safe, multi-backend key-value storage library for Rust with support for native (Sled, Redb) and WASM (IndexedDB) environments.
Documentation
//! Unified trait interface for tree operations across different backends
//!
//! This module provides a uniform API for CRUD operations on netabase store trees,
//! abstracting away the differences between SledStore, RedbStore, and IndexedDBStore.
use crate::error::NetabaseError;

/// Synchronous tree operations trait for native backends (SledStore, RedbStore).
///
/// `NetabaseTreeSync` provides a unified interface for CRUD operations on a specific model type,
/// allowing you to write backend-agnostic code that works with both SledStore and RedbStore.
///
/// All methods are synchronous and designed for native (non-WASM) environments.
///
/// # Type Parameters
///
/// * `'db` - Lifetime parameter that ties the tree to its parent database, ensuring trees cannot outlive their stores
/// * `D` - The definition type (generated by `#[netabase_definition_module]`)
/// * `M` - The model type (annotated with `#[derive(NetabaseModel)]`)
///
/// # Implementations
///
/// This trait is implemented by:
/// - `SledStoreTree<'db, D, M>` - High-performance embedded database
/// - `RedbStoreTree<'db, D, M>` - ACID-compliant embedded database
///
/// # Examples
///
/// ## Writing Backend-Agnostic Code
///
/// ```
/// use netabase_store::{netabase_definition_module, NetabaseModel, netabase};
/// use netabase_store::databases::sled_store::SledStore;
/// use netabase_store::traits::tree::NetabaseTreeSync;
/// use netabase_store::error::NetabaseError;
///
/// #[netabase_definition_module(MyDefinition, MyKeys)]
/// mod my_models {
///     use super::*;
///     use netabase_store::{NetabaseModel, netabase};
///
///     #[derive(NetabaseModel, Clone, Debug, PartialEq,
///              bincode::Encode, bincode::Decode,
///              serde::Serialize, serde::Deserialize)]
///     #[netabase(MyDefinition)]
///     pub struct User {
///         #[primary_key]
///         pub id: u64,
///         pub name: String,
///     }
/// }
/// use my_models::*;
///
/// // This function works with ANY backend
/// fn count_users<'a, T>(tree: &T) -> Result<usize, NetabaseError>
/// where
///     T: NetabaseTreeSync<'a, MyDefinition, User>
/// {
///     tree.len()
/// }
///
/// // Use with Sled
/// let sled_store = SledStore::<MyDefinition>::temp().unwrap();
/// let sled_tree = sled_store.open_tree::<User>();
/// let count1 = count_users(&sled_tree).unwrap();
/// assert_eq!(count1, 0);
/// ```
///
/// # See Also
///
/// - [`crate::databases::sled_store::SledStoreTree`] - Sled implementation
/// - [`crate::databases::redb_store::RedbStoreTree`] - Redb implementation
#[cfg_attr(
    feature = "wasm",
    doc = "- [`NetabaseTreeAsync`] - Async version for WASM backends"
)]
pub trait NetabaseTreeSync<'db, D, M> {
    /// Insert or update a model in the tree
    ///
    /// # Arguments
    /// * `model` - The model to insert or update
    ///
    /// # Returns
    /// * `Ok(())` if the operation succeeded
    /// * `Err(NetabaseError)` if the operation failed
    ///
    /// # Type Parameters
    /// * `PrimaryKey` - The primary key type from model M
    /// * `SecondaryKeys` - The secondary keys type from model M
    type PrimaryKey;
    type SecondaryKeys;

    fn put(&self, model: M) -> Result<(), NetabaseError>;

    /// Get a model by its primary key
    ///
    /// # Arguments
    /// * `key` - The primary key of the model to retrieve
    ///
    /// # Returns
    /// * `Ok(Some(model))` if the model was found
    /// * `Ok(None)` if the model was not found
    /// * `Err(NetabaseError)` if the operation failed
    fn get(&self, key: Self::PrimaryKey) -> Result<Option<M>, NetabaseError>;

    /// Delete a model by its primary key
    ///
    /// # Arguments
    /// * `key` - The primary key of the model to delete
    ///
    /// # Returns
    /// * `Ok(Some(model))` if the model was deleted (returns the deleted model)
    /// * `Ok(None)` if the model was not found
    /// * `Err(NetabaseError)` if the operation failed
    fn remove(&self, key: Self::PrimaryKey) -> Result<Option<M>, NetabaseError>;

    /// Find models by secondary key
    ///
    /// # Arguments
    /// * `secondary_key` - The secondary key to search for
    ///
    /// # Returns
    /// * `Ok(Vec<M>)` - Vector of all models matching the secondary key
    /// * `Err(NetabaseError)` if the operation failed
    fn get_by_secondary_key(
        &self,
        secondary_key: Self::SecondaryKeys,
    ) -> Result<Vec<M>, NetabaseError>;

    /// Check if the tree is empty
    ///
    /// # Returns
    /// * `Ok(true)` if the tree contains no models
    /// * `Ok(false)` if the tree contains at least one model
    /// * `Err(NetabaseError)` if the operation failed
    fn is_empty(&self) -> Result<bool, NetabaseError>;

    /// Get the number of models in the tree
    ///
    /// # Returns
    /// * `Ok(count)` - The number of models in the tree
    /// * `Err(NetabaseError)` if the operation failed
    fn len(&self) -> Result<usize, NetabaseError>;

    /// Clear all models from the tree
    ///
    /// # Returns
    /// * `Ok(())` if the operation succeeded
    /// * `Err(NetabaseError)` if the operation failed
    fn clear(&self) -> Result<(), NetabaseError>;
}

/// Asynchronous tree operations trait for WASM backends (IndexedDBStore)
///
/// This trait provides a uniform interface for CRUD operations on a specific model type.
/// All methods are asynchronous and suitable for WASM environments.
///
/// The generic parameters D and M are not bounded here to avoid duplicating complex trait bounds.
/// Implementors should ensure proper bounds are specified in their impl blocks.
#[cfg(feature = "wasm")]
pub trait NetabaseTreeAsync<D, M> {
    /// Insert or update a model in the tree
    ///
    /// # Arguments
    /// * `model` - The model to insert or update
    ///
    /// # Returns
    /// * `Ok(())` if the operation succeeded
    /// * `Err(NetabaseError)` if the operation failed
    ///
    /// # Type Parameters
    /// * `PrimaryKey` - The primary key type from model M
    /// * `SecondaryKeys` - The secondary keys type from model M
    type PrimaryKey;
    type SecondaryKeys;

    fn put(&self, model: M) -> impl std::future::Future<Output = Result<(), NetabaseError>>;

    /// Get a model by its primary key
    ///
    /// # Arguments
    /// * `key` - The primary key of the model to retrieve
    ///
    /// # Returns
    /// * `Ok(Some(model))` if the model was found
    /// * `Ok(None)` if the model was not found
    /// * `Err(NetabaseError)` if the operation failed
    fn get(
        &self,
        key: Self::PrimaryKey,
    ) -> impl std::future::Future<Output = Result<Option<M>, NetabaseError>>;

    /// Delete a model by its primary key
    ///
    /// # Arguments
    /// * `key` - The primary key of the model to delete
    ///
    /// # Returns
    /// * `Ok(Some(model))` if the model was deleted (returns the deleted model)
    /// * `Ok(None)` if the model was not found
    /// * `Err(NetabaseError)` if the operation failed
    fn remove(
        &self,
        key: Self::PrimaryKey,
    ) -> impl std::future::Future<Output = Result<Option<M>, NetabaseError>>;

    /// Find models by secondary key
    ///
    /// # Arguments
    /// * `secondary_key` - The secondary key to search for
    ///
    /// # Returns
    /// * `Ok(Vec<M>)` - Vector of all models matching the secondary key
    /// * `Err(NetabaseError)` if the operation failed
    fn get_by_secondary_key(
        &self,
        secondary_key: Self::SecondaryKeys,
    ) -> impl std::future::Future<Output = Result<Vec<M>, NetabaseError>>;

    /// Check if the tree is empty
    ///
    /// # Returns
    /// * `Ok(true)` if the tree contains no models
    /// * `Ok(false)` if the tree contains at least one model
    /// * `Err(NetabaseError)` if the operation failed
    fn is_empty(&self) -> impl std::future::Future<Output = Result<bool, NetabaseError>>;

    /// Get the number of models in the tree
    ///
    /// # Returns
    /// * `Ok(count)` - The number of models in the tree
    /// * `Err(NetabaseError)` if the operation failed
    fn len(&self) -> impl std::future::Future<Output = Result<usize, NetabaseError>>;

    /// Clear all models from the tree
    ///
    /// # Returns
    /// * `Ok(())` if the operation succeeded
    /// * `Err(NetabaseError)` if the operation failed
    fn clear(&self) -> impl std::future::Future<Output = Result<(), NetabaseError>>;
}