ps-hkey 0.1.0-34

This crate defines the hashkey format and provides methods for resolving them
Documentation
use std::ops::{Deref, DerefMut};

use ps_datachunk::{BorrowedDataChunk, DataChunk, DataChunkError, OwnedDataChunk};
use ps_hash::Hash;

use crate::{HkeyError, Store};

pub trait DynStore: Send + Sync {
    type Error: From<DataChunkError> + From<HkeyError> + Send + 'static;

    fn get(&self, hash: &Hash) -> Result<OwnedDataChunk, Self::Error>;
    fn put_encrypted(&self, chunk: BorrowedDataChunk<'_>) -> Result<(), Self::Error>;
}

impl<T> DynStore for T
where
    T: Store + Send + Sync + 'static,
{
    type Error = T::Error;

    fn get(&self, hash: &Hash) -> Result<OwnedDataChunk, Self::Error> {
        Ok(Store::get(self, hash)?.into_owned())
    }

    fn put_encrypted(&self, chunk: BorrowedDataChunk<'_>) -> Result<(), Self::Error> {
        Store::put_encrypted(self, chunk)
    }
}

#[derive(Default)]
pub struct CombinedStore<E: CombinedStoreError, const WRITE_TO_ALL: bool> {
    stores: Vec<Box<dyn DynStore<Error = E>>>,
}

impl<E: CombinedStoreError, const WRITE_TO_ALL: bool> CombinedStore<E, WRITE_TO_ALL> {
    /// Creates a `CombinedStore` from a list of Stores.
    #[must_use]
    pub fn new<S, I>(stores: I) -> Self
    where
        S: Store<Error = E> + Send + Sync + 'static,
        I: IntoIterator<Item = S>,
    {
        Self {
            stores: stores.into_iter().map(|s| Box::new(s) as _).collect(),
        }
    }

    pub fn push<S>(&mut self, store: S)
    where
        S: Store<Error = E> + Send + Sync + 'static,
    {
        self.stores.push(Box::new(store));
    }

    pub fn extend<S, I>(&mut self, iter: I)
    where
        S: Store<Error = E> + Send + Sync + 'static,
        I: IntoIterator<Item = S>,
    {
        self.stores
            .extend(iter.into_iter().map(|s| Box::new(s) as _));
    }

    #[must_use]
    pub fn write_to_all(self) -> CombinedStore<E, true> {
        CombinedStore {
            stores: self.stores,
        }
    }

    #[must_use]
    pub fn write_to_one(self) -> CombinedStore<E, false> {
        CombinedStore {
            stores: self.stores,
        }
    }

    fn get(&self, hash: &Hash) -> Result<OwnedDataChunk, E> {
        let mut last_err = None;

        for s in self.iter() {
            match s.get(hash) {
                Ok(chunk) => return Ok(chunk),
                Err(err) => last_err = Some(err),
            }
        }

        Err(last_err.unwrap_or_else(E::no_stores))
    }
}

impl<E: CombinedStoreError, const WRITE_TO_ALL: bool> Deref for CombinedStore<E, WRITE_TO_ALL> {
    type Target = Vec<Box<dyn DynStore<Error = E>>>;

    fn deref(&self) -> &Self::Target {
        &self.stores
    }
}

impl<E: CombinedStoreError, const WRITE_TO_ALL: bool> DerefMut for CombinedStore<E, WRITE_TO_ALL> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.stores
    }
}

impl<E: CombinedStoreError> Store for CombinedStore<E, true> {
    type Chunk<'c> = OwnedDataChunk;
    type Error = E;

    fn get<'a>(&'a self, hash: &Hash) -> Result<Self::Chunk<'a>, Self::Error> {
        self.get(hash)
    }

    fn put_encrypted<C: DataChunk>(&self, chunk: C) -> Result<(), Self::Error> {
        if self.is_empty() {
            return Err(E::no_stores());
        }

        let mut result = Ok(());

        for s in self.iter() {
            if let Err(err) = s.put_encrypted(chunk.borrow()) {
                if result.is_ok() {
                    result = Err(err);
                }
            }
        }

        result
    }
}

impl<E: CombinedStoreError> Store for CombinedStore<E, false> {
    type Chunk<'c> = OwnedDataChunk;
    type Error = E;

    fn get<'a>(&'a self, hash: &Hash) -> Result<Self::Chunk<'a>, Self::Error> {
        self.get(hash)
    }

    fn put_encrypted<C: DataChunk>(&self, chunk: C) -> Result<(), Self::Error> {
        let mut last_err = None;

        for store in self.iter() {
            match store.put_encrypted(chunk.borrow()) {
                Ok(()) => return Ok(()),
                Err(err) => last_err = Some(err),
            }
        }

        Err(last_err.unwrap_or_else(E::no_stores))
    }
}

pub trait CombinedStoreError: From<DataChunkError> + From<HkeyError> + Send + 'static {
    fn no_stores() -> Self;
}