use std::fmt::Debug;
use crate::{CacheEntry, CacheTier, Codec, Encoder, Error, SizeError};
type EncodeFn<A, B> = Box<dyn Fn(&A) -> Result<B, Error> + Send + Sync>;
pub struct TransformEncoder<A, B> {
encode_fn: EncodeFn<A, B>,
}
impl<A, B> TransformEncoder<A, B> {
pub fn new<EncodeError>(encode_fn: impl Fn(&A) -> Result<B, EncodeError> + Send + Sync + 'static) -> Self
where
EncodeError: std::error::Error + Send + Sync + 'static,
{
Self {
encode_fn: Box::new(move |a| encode_fn(a).map_err(|e| Error::from_source(e))),
}
}
pub fn infallible(encode_fn: impl Fn(&A) -> B + Send + Sync + 'static) -> Self {
Self {
encode_fn: Box::new(move |a| Ok(encode_fn(a))),
}
}
}
impl<A, B> Encoder<A, B> for TransformEncoder<A, B> {
fn encode(&self, value: &A) -> Result<B, Error> {
(self.encode_fn)(value)
}
}
impl<A, B> Debug for TransformEncoder<A, B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TransformEncoder")
.field("A", &std::any::type_name::<A>())
.field("B", &std::any::type_name::<B>())
.finish()
}
}
pub struct TransformCodec<A, B> {
encode_fn: EncodeFn<A, B>,
decode_fn: EncodeFn<B, A>,
}
impl<A, B> TransformCodec<A, B> {
pub fn new<EncodeError, DecodeError>(
encode_fn: impl Fn(&A) -> Result<B, EncodeError> + Send + Sync + 'static,
decode_fn: impl Fn(&B) -> Result<A, DecodeError> + Send + Sync + 'static,
) -> Self
where
EncodeError: std::error::Error + Send + Sync + 'static,
DecodeError: std::error::Error + Send + Sync + 'static,
{
Self {
encode_fn: Box::new(move |a| encode_fn(a).map_err(|e| Error::from_source(e))),
decode_fn: Box::new(move |b| decode_fn(b).map_err(|e| Error::from_source(e))),
}
}
}
impl<A, B> Encoder<A, B> for TransformCodec<A, B> {
fn encode(&self, value: &A) -> Result<B, Error> {
(self.encode_fn)(value)
}
}
impl<A, B> Codec<A, B> for TransformCodec<A, B> {
fn decode(&self, value: &B) -> Result<A, Error> {
(self.decode_fn)(value)
}
}
impl<A, B> Debug for TransformCodec<A, B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TransformCodec")
.field("A", &std::any::type_name::<A>())
.field("B", &std::any::type_name::<B>())
.finish()
}
}
pub(crate) struct TransformAdapter<K, KT, V, VT, S>
where
S: CacheTier<KT, VT>,
{
inner: S,
key_encoder: Box<dyn Encoder<K, KT>>,
value_codec: Box<dyn Codec<V, VT>>,
}
impl<K, KT, V, VT, S> TransformAdapter<K, KT, V, VT, S>
where
S: CacheTier<KT, VT>,
{
pub(crate) fn from_boxed(inner: S, key_encoder: Box<dyn Encoder<K, KT>>, value_codec: Box<dyn Codec<V, VT>>) -> Self {
Self {
inner,
key_encoder,
value_codec,
}
}
}
impl<K, KT, V, VT, S> CacheTier<K, V> for TransformAdapter<K, KT, V, VT, S>
where
K: Send + Sync,
V: Send + Sync,
KT: Send + Sync,
VT: Send + Sync,
S: CacheTier<KT, VT> + Send + Sync,
{
async fn get(&self, key: &K) -> Result<Option<CacheEntry<V>>, Error> {
let mapped_key = self.key_encoder.encode(key)?;
let entry_option = self.inner.get(&mapped_key).await?;
if let Some(entry) = entry_option {
entry.try_map_value(|v| self.value_codec.decode(&v)).map(Some)
} else {
Ok(None)
}
}
async fn insert(&self, key: K, entry: CacheEntry<V>) -> Result<(), Error> {
let mapped_key = self.key_encoder.encode(&key)?;
let mapped_entry = entry.try_map_value(|v| self.value_codec.encode(&v))?;
self.inner.insert(mapped_key, mapped_entry).await
}
async fn invalidate(&self, key: &K) -> Result<(), Error> {
let mapped_key = self.key_encoder.encode(key)?;
self.inner.invalidate(&mapped_key).await
}
async fn clear(&self) -> Result<(), Error> {
self.inner.clear().await
}
async fn len(&self) -> Result<u64, SizeError> {
self.inner.len().await
}
}
impl<K, KT, V, VT, S> Debug for TransformAdapter<K, KT, V, VT, S>
where
S: CacheTier<KT, VT> + Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TransformAdapter")
.field("inner", &self.inner)
.field("K", &std::any::type_name::<K>())
.field("KT", &std::any::type_name::<KT>())
.field("V", &std::any::type_name::<V>())
.field("VT", &std::any::type_name::<VT>())
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use cachet_tier::MockCache;
use super::*;
#[test]
fn transform_adapter_debug() {
let codec = TransformCodec::new(
|v: &String| v.parse::<i32>(),
|v: &i32| Ok::<_, std::convert::Infallible>(v.to_string()),
);
assert_eq!(codec.encode(&"42".to_string()).unwrap(), 42);
assert_eq!(codec.decode(&42).unwrap(), "42");
let key_encoder = TransformEncoder::new(|k: &String| k.parse::<i32>());
assert_eq!(key_encoder.encode(&"7".to_string()).unwrap(), 7);
let inner = MockCache::<i32, i32>::new();
let adapter = TransformAdapter::from_boxed(inner, Box::new(key_encoder), Box::new(codec));
let debug = format!("{adapter:?}");
assert!(debug.contains("TransformAdapter"));
}
#[test]
fn infallible_encoder_closure_is_covered() {
let encoder = TransformEncoder::infallible(|k: &i32| k.to_string());
assert_eq!(encoder.encode(&42).unwrap(), "42");
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn len_delegates_to_inner() {
use crate::infallible;
let data = vec![(1, CacheEntry::new(10)), (2, CacheEntry::new(20))];
let inner = MockCache::with_data(data.into_iter().collect());
let adapter = TransformAdapter::from_boxed(
inner,
Box::new(TransformEncoder::new(|k: &String| k.parse::<i32>())),
Box::new(TransformCodec::new(infallible(|v: &i32| *v), infallible(|v: &i32| *v))),
);
assert_eq!(adapter.len().await.expect("MockCache::len returns Ok"), 2);
}
}