unistore 0.4.0

simple storage abstraction that works on wasm and native Targets
Documentation
use crate::{AsKey, Key, UniTable, Value};

pub struct UniIndex<'a, I: Key, K: Key, V: Value> {
    pub table: &'a UniTable<'a, K, V>,
    pub index: UniTable<'a, String, ()>,
    pub index_rev: UniTable<'a, String, String>,
    phantom: std::marker::PhantomData<I>,
}

impl<I: Key, K: Key, V: Value> std::fmt::Debug for UniIndex<'_, I, K, V> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("UniIndex")
            .field("table", &self.table.name)
            .field("index", &self.index.name)
            .finish()
    }
}

impl<I: Key, K: Key + Clone, V: Value> UniIndex<'_, I, K, V> {
    pub async fn get(&self, key: impl AsKey<I>) -> Result<Vec<(K, V)>, crate::Error> {
        let index_entries = self.index.get_prefix(key.as_key().to_key_string()).await?;
        let mut results = Vec::new();
        for (index_key, ()) in index_entries {
            let (_, key) = index_key
                .split_once('\0')
                .expect("Index key should contain a separator");
            let key = K::from_key_string(key)?;
            if let Some(value) = self.table.get(key.clone()).await? {
                results.push((key, value));
            }
        }
        Ok(results)
    }

    pub async fn get_first(&self, key: impl AsKey<I>) -> Result<Option<(K, V)>, crate::Error> {
        let index_entries = self.index.get_prefix(key.as_key().to_key_string()).await?;
        if index_entries.is_empty() {
            return Ok(None);
        }
        let (index_key, ()) = index_entries.into_iter().next().unwrap();
        let (_, key) = index_key
            .split_once('\0')
            .expect("Index key should contain a separator");
        let key = K::from_key_string(key)?;
        if let Some(value) = self.table.get(key.clone()).await? {
            return Ok(Some((key, value)));
        }
        Ok(None)
    }

    pub async fn insert(
        &self,
        value: impl AsKey<I>,
        key: impl AsKey<K>,
    ) -> Result<(), crate::Error> {
        let key_str = key.as_key().to_key_string();
        let value_str = value.as_key().to_key_string();
        if let Some(existing) = self.index_rev.get(key_str.as_str()).await? {
            self.index.remove(existing).await?;
        }
        let index_key = format!("{value_str}\0{key_str}");
        self.index.insert(index_key.clone(), ()).await?;
        self.index_rev.insert(key_str, index_key).await?;
        Ok(())
    }
}

impl<K: Key, V: Value> UniTable<'_, K, V> {
    pub async fn create_index<I: Key>(
        &self,
        index: &'static str,
    ) -> Result<UniIndex<'_, I, K, V>, crate::Error> {
        let index_table = self
            .store
            .create_table(&format!("{}_index_{index}", self.name), false)
            .await?;
        let rev_index_table = self
            .store
            .create_table(&format!("{}_index_{index}_rev", self.name), false)
            .await?;
        Ok(UniIndex {
            table: self,
            index: index_table,
            index_rev: rev_index_table,
            phantom: std::marker::PhantomData,
        })
    }
}