ps_hkey/store/
combined.rs

1use std::ops::{Deref, DerefMut};
2
3use ps_datachunk::{BorrowedDataChunk, DataChunk, OwnedDataChunk, PsDataChunkError};
4use ps_hash::Hash;
5
6use crate::{PsHkeyError, Store};
7
8pub trait DynStore: Send + Sync {
9    type Error: From<PsDataChunkError> + From<PsHkeyError> + Send + 'static;
10
11    fn get(&self, hash: &Hash) -> Result<OwnedDataChunk, Self::Error>;
12    fn put_encrypted(&self, chunk: BorrowedDataChunk<'_>) -> Result<(), Self::Error>;
13}
14
15impl<T> DynStore for T
16where
17    T: Store + Send + Sync + 'static,
18{
19    type Error = T::Error;
20
21    fn get(&self, hash: &Hash) -> Result<OwnedDataChunk, Self::Error> {
22        Ok(Store::get(self, hash)?.into_owned())
23    }
24
25    fn put_encrypted(&self, chunk: BorrowedDataChunk<'_>) -> Result<(), Self::Error> {
26        Store::put_encrypted(self, chunk)
27    }
28}
29
30#[derive(Default)]
31pub struct CombinedStore<E: CombinedStoreError, const WRITE_TO_ALL: bool> {
32    stores: Vec<Box<dyn DynStore<Error = E>>>,
33}
34
35impl<E: CombinedStoreError, const WRITE_TO_ALL: bool> CombinedStore<E, WRITE_TO_ALL> {
36    /// Creates a `CombinedStore` from a list of Stores.
37    #[must_use]
38    pub fn new<S, I>(stores: I) -> Self
39    where
40        S: Store<Error = E> + Send + Sync + 'static,
41        I: IntoIterator<Item = S>,
42    {
43        Self {
44            stores: stores.into_iter().map(|s| Box::new(s) as _).collect(),
45        }
46    }
47
48    pub fn push<S>(&mut self, store: S)
49    where
50        S: Store<Error = E> + Send + Sync + 'static,
51    {
52        self.stores.push(Box::new(store));
53    }
54
55    pub fn extend<S, I>(&mut self, iter: I)
56    where
57        S: Store<Error = E> + Send + Sync + 'static,
58        I: IntoIterator<Item = S>,
59    {
60        self.stores
61            .extend(iter.into_iter().map(|s| Box::new(s) as _));
62    }
63
64    #[must_use]
65    pub fn write_to_all(self) -> CombinedStore<E, true> {
66        CombinedStore {
67            stores: self.stores,
68        }
69    }
70
71    #[must_use]
72    pub fn write_to_one(self) -> CombinedStore<E, false> {
73        CombinedStore {
74            stores: self.stores,
75        }
76    }
77
78    fn get(&self, hash: &Hash) -> Result<OwnedDataChunk, E> {
79        let mut last_err = None;
80
81        for s in self.iter() {
82            match s.get(hash) {
83                Ok(chunk) => return Ok(chunk),
84                Err(err) => last_err = Some(err),
85            }
86        }
87
88        Err(last_err.unwrap_or_else(E::no_stores))
89    }
90}
91
92impl<E: CombinedStoreError, const WRITE_TO_ALL: bool> Deref for CombinedStore<E, WRITE_TO_ALL> {
93    type Target = Vec<Box<dyn DynStore<Error = E>>>;
94
95    fn deref(&self) -> &Self::Target {
96        &self.stores
97    }
98}
99
100impl<E: CombinedStoreError, const WRITE_TO_ALL: bool> DerefMut for CombinedStore<E, WRITE_TO_ALL> {
101    fn deref_mut(&mut self) -> &mut Self::Target {
102        &mut self.stores
103    }
104}
105
106impl<E: CombinedStoreError> Store for CombinedStore<E, true> {
107    type Chunk<'c> = OwnedDataChunk;
108    type Error = E;
109
110    fn get<'a>(&'a self, hash: &Hash) -> Result<Self::Chunk<'a>, Self::Error> {
111        self.get(hash)
112    }
113
114    fn put_encrypted<C: DataChunk>(&self, chunk: C) -> Result<(), Self::Error> {
115        if self.is_empty() {
116            return Err(E::no_stores());
117        }
118
119        let mut result = Ok(());
120
121        for s in self.iter() {
122            result = result.and(s.put_encrypted(chunk.borrow()));
123        }
124
125        result
126    }
127}
128
129impl<E: CombinedStoreError> Store for CombinedStore<E, false> {
130    type Chunk<'c> = OwnedDataChunk;
131    type Error = E;
132
133    fn get<'a>(&'a self, hash: &Hash) -> Result<Self::Chunk<'a>, Self::Error> {
134        self.get(hash)
135    }
136
137    fn put_encrypted<C: DataChunk>(&self, chunk: C) -> Result<(), Self::Error> {
138        let mut last_err = None;
139
140        for store in self.iter() {
141            match store.put_encrypted(chunk.borrow()) {
142                Ok(()) => return Ok(()),
143                Err(err) => last_err = Some(err),
144            }
145        }
146
147        Err(last_err.unwrap_or_else(E::no_stores))
148    }
149}
150
151pub trait CombinedStoreError: From<PsDataChunkError> + From<PsHkeyError> + Send + 'static {
152    fn no_stores() -> Self;
153}