scmstore: Introduce FetchKey and FetchValue traits

Summary: Introduce `FetchKey` and `FetchValue` traits to simplify repeated trait bounds in many `ReadStore` implementations. We also newly require `Clone` for both keys and values, which was already required by the fallback combinator.

Reviewed By: DurhamG

Differential Revision: D27652499

fbshipit-source-id: 6a3d5eb18a904b982fdb9946b80fcc9025d391ea
This commit is contained in:
Meyer Jacobs 2021-04-13 22:12:04 -07:00 committed by Facebook GitHub Bot
parent fb44958218
commit 0ec6d1fcc8
4 changed files with 37 additions and 37 deletions

View File

@ -6,7 +6,6 @@
*/
use std::convert::{From, Into, TryFrom, TryInto};
use std::fmt;
use std::sync::Arc;
use anyhow::Error;
@ -16,7 +15,8 @@ use tracing::error;
use streams::select_drop;
use crate::scmstore::{
BoxedReadStore, BoxedWriteStore, FetchError, FetchStream, KeyStream, ReadStore,
BoxedReadStore, BoxedWriteStore, FetchError, FetchKey, FetchStream, FetchValue, KeyStream,
ReadStore,
};
/// A combinator which queries a preferred store, then falls back to a fallback store
@ -39,15 +39,15 @@ const CHANNEL_BUFFER: usize = 200;
impl<K, VP, VF, VW, VO> ReadStore<K, VO> for FallbackCache<K, VP, VF, VW>
where
K: fmt::Display + fmt::Debug + Send + Sync + Clone + Unpin + 'static,
K: FetchKey + Unpin,
// Preferred Value Type
VP: Send + Sync + Clone + 'static,
VP: FetchValue,
// Fallback Value Type
VF: Send + Sync + Clone + 'static,
VF: FetchValue,
// Write Value Type (must support conversion from fallback)
VW: Send + Sync + Clone + From<VF> + 'static,
VW: FetchValue + From<VF>,
// Output Value Type (must support conversion from preferred & fallback)
VO: Send + Sync + Clone + TryFrom<VF> + TryFrom<VP> + 'static,
VO: FetchValue + TryFrom<VF> + TryFrom<VP>,
// TODO(meyer): For now, we just require the conversion errors to convertible to anyhow::Error
// We can probably loosen this later. In particular, we want to associate the key, at least.
<VO as TryFrom<VF>>::Error: Into<Error>,
@ -136,13 +136,13 @@ pub struct Fallback<K, VP, VF> {
impl<K, VP, VF, VO> ReadStore<K, VO> for Fallback<K, VP, VF>
where
K: fmt::Display + fmt::Debug + Send + Sync + Clone + Unpin + 'static,
K: FetchKey + Unpin,
// Preferred Value Type
VP: Send + Sync + Clone + 'static,
VP: FetchValue,
// Fallback Value Type
VF: Send + Sync + Clone + 'static,
VF: FetchValue,
// Output Value Type (must support conversion from preferred & fallback)
VO: Send + Sync + Clone + From<VF> + From<VP> + 'static,
VO: FetchValue + From<VF> + From<VP>,
{
fn fetch_stream(self: Arc<Self>, keys: KeyStream<K>) -> FetchStream<K, VO> {
// TODO(meyer): Write a custom Stream implementation to try to avoid use of channels

View File

@ -5,12 +5,13 @@
* GNU General Public License version 2.
*/
use std::fmt;
use std::sync::Arc;
use futures::StreamExt;
use crate::scmstore::{BoxedWriteStore, WriteResults, WriteStore, WriteStream};
use crate::scmstore::{
BoxedWriteStore, FetchKey, FetchValue, WriteResults, WriteStore, WriteStream,
};
/// A minimal "filter_map" store, which filters writes to the associated `write_store`,
pub struct FilterMapStore<K, V, F> {
@ -23,8 +24,8 @@ pub struct FilterMapStore<K, V, F> {
impl<K, V, F> WriteStore<K, V> for FilterMapStore<K, V, F>
where
K: fmt::Display + fmt::Debug + Send + Sync + 'static,
V: Send + Sync + 'static,
K: FetchKey,
V: FetchValue,
F: Fn(V) -> Option<V> + Send + Sync + 'static,
{
fn write_stream(self: Arc<Self>, values: WriteStream<V>) -> WriteResults<K> {

View File

@ -5,12 +5,13 @@
* GNU General Public License version 2.
*/
use std::{collections::HashMap, fmt, hash::Hash, sync::Arc};
use std::{collections::HashMap, hash::Hash, sync::Arc};
use futures::{lock::Mutex, StreamExt};
use crate::scmstore::{
FetchError, FetchStream, KeyStream, ReadStore, WriteResults, WriteStore, WriteStream,
FetchError, FetchKey, FetchStream, FetchValue, KeyStream, ReadStore, WriteResults, WriteStore,
WriteStream,
};
pub struct HashMapStore<K, V> {
@ -38,8 +39,8 @@ pub trait KeyedValue {
impl<K, V> ReadStore<K, V> for HashMapStore<K, V>
where
K: fmt::Display + fmt::Debug + std::cmp::Eq + Hash + Send + Sync + 'static,
V: Clone + Send + Sync + 'static,
K: FetchKey + std::cmp::Eq + Hash,
V: FetchValue,
{
fn fetch_stream(self: Arc<Self>, keys: KeyStream<K>) -> FetchStream<K, V> {
Box::pin(keys.then(move |key| {
@ -59,8 +60,8 @@ where
impl<K, V> WriteStore<K, V> for HashMapStore<K, V>
where
K: Clone + fmt::Display + fmt::Debug + std::cmp::Eq + Hash + Send + Sync + 'static,
V: KeyedValue<Key = K> + Send + Sync + 'static,
K: FetchKey + std::cmp::Eq + Hash,
V: FetchValue + KeyedValue<Key = K>,
{
fn write_stream(self: Arc<Self>, values: WriteStream<V>) -> WriteResults<K> {
Box::pin(values.then(move |value| {

View File

@ -55,6 +55,13 @@ pub type BoxedWriteStore<K, V> = Arc<dyn WriteStore<K, V>>;
/// A trait object for stores which support both reading and writing.
pub type BoxedRWStore<K, V> = Arc<dyn ReadWriteStore<K, V>>;
// Automatic blanket impls for FetchKey and FetchValue. For now they're just used to simplify trait bounds.
pub trait FetchKey: fmt::Display + fmt::Debug + Clone + Send + Sync + 'static {}
impl<K> FetchKey for K where K: fmt::Display + fmt::Debug + Clone + Send + Sync + 'static {}
pub trait FetchValue: Send + Sync + Clone + 'static {}
impl<V> FetchValue for V where V: Send + Sync + Clone + 'static {}
#[derive(Debug, Error)]
pub enum FetchError<K: fmt::Debug + fmt::Display> {
#[error("failed to fetch key '{0}': key not found")]
@ -108,8 +115,8 @@ where
pub fn fetch_error<K, V, E>(e: E) -> FetchStream<K, V>
where
E: Into<Error> + Send + Sync + 'static,
K: fmt::Display + fmt::Debug + Send + Sync + 'static,
V: Send + Sync + 'static,
K: FetchKey,
V: FetchValue,
{
Box::pin(stream::once(future::ready(Err(FetchError::from(e)))))
}
@ -140,34 +147,25 @@ where
}
/// A typed, key-value store which supports both reading and writing.
pub trait ReadWriteStore<
K: fmt::Display + fmt::Debug + Send + Sync + 'static,
V: Send + Sync + 'static,
>: ReadStore<K, V> + WriteStore<K, V>
{
}
pub trait ReadWriteStore<K: FetchKey, V: FetchValue>: ReadStore<K, V> + WriteStore<K, V> {}
impl<T, K, V> ReadWriteStore<K, V> for T
where
K: fmt::Display + fmt::Debug + Send + Sync + 'static,
V: Send + Sync + 'static,
K: FetchKey,
V: FetchValue,
T: ReadStore<K, V> + WriteStore<K, V>,
{
}
// TODO: Add attributes support
/// A typed, async key-value storage API
pub trait ReadStore<K: fmt::Display + fmt::Debug + Send + Sync + 'static, V: Send + Sync + 'static>:
Send + Sync + 'static
{
pub trait ReadStore<K: FetchKey, V: FetchValue>: Send + Sync + 'static {
/// Map a stream of keys to a stream of values by fetching from the underlying store
fn fetch_stream(self: Arc<Self>, keys: KeyStream<K>) -> FetchStream<K, V>;
}
// TODO: Add attributes support
/// A typed, async key-value storage API
pub trait WriteStore<K: fmt::Display + fmt::Debug + Send + Sync + 'static, V: Send + Sync + 'static>:
Send + Sync + 'static
{
pub trait WriteStore<K: FetchKey, V: FetchValue>: Send + Sync + 'static {
fn write_stream(self: Arc<Self>, values: WriteStream<V>) -> WriteResults<K>;
}