use cachet_tier::{CacheEntry, Error};
use layered::Service;
use crate::{CacheOperation, CacheResponse, GetRequest, InsertRequest, InvalidateRequest};
pub trait CacheServiceExt<K, V>: Sized {
fn get(&self, key: &K) -> impl Future<Output = Result<Option<CacheEntry<V>>, Error>> + Send;
fn insert(&self, key: K, entry: CacheEntry<V>) -> impl Future<Output = Result<(), Error>> + Send;
fn invalidate(&self, key: &K) -> impl Future<Output = Result<(), Error>> + Send;
fn clear(&self) -> impl Future<Output = Result<(), Error>> + Send;
}
impl<K, V, S> CacheServiceExt<K, V> for S
where
K: Clone + Send + Sync,
V: Clone + Send + Sync,
S: Service<CacheOperation<K, V>, Out = Result<CacheResponse<V>, Error>> + Send + Sync,
{
async fn get(&self, key: &K) -> Result<Option<CacheEntry<V>>, Error> {
let req = GetRequest { key: key.clone() };
match self.execute(CacheOperation::Get(req)).await? {
CacheResponse::Get(entry) => Ok(entry),
_ => Err(Error::from_message("unexpected response type")),
}
}
async fn insert(&self, key: K, entry: CacheEntry<V>) -> Result<(), Error> {
let req = InsertRequest { key: key.clone(), entry };
match self.execute(CacheOperation::Insert(req)).await? {
CacheResponse::Insert => Ok(()),
_ => Err(Error::from_message("unexpected response type")),
}
}
async fn invalidate(&self, key: &K) -> Result<(), Error> {
let req = InvalidateRequest { key: key.clone() };
match self.execute(CacheOperation::Invalidate(req)).await? {
CacheResponse::Invalidate => Ok(()),
_ => Err(Error::from_message("unexpected response type")),
}
}
async fn clear(&self) -> Result<(), Error> {
match self.execute(CacheOperation::Clear).await? {
CacheResponse::Clear => Ok(()),
_ => Err(Error::from_message("unexpected response type")),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct CorrectService;
impl Service<CacheOperation<String, i32>> for CorrectService {
type Out = Result<CacheResponse<i32>, Error>;
async fn execute(&self, input: CacheOperation<String, i32>) -> Self::Out {
match input {
CacheOperation::Get(_) => Ok(CacheResponse::Get(Some(CacheEntry::new(42)))),
CacheOperation::Insert(_) => Ok(CacheResponse::Insert),
CacheOperation::Invalidate(_) => Ok(CacheResponse::Invalidate),
CacheOperation::Clear => Ok(CacheResponse::Clear),
}
}
}
#[derive(Debug)]
struct WrongResponseService;
impl Service<CacheOperation<String, i32>> for WrongResponseService {
type Out = Result<CacheResponse<i32>, Error>;
async fn execute(&self, input: CacheOperation<String, i32>) -> Self::Out {
match input {
CacheOperation::Insert(_) => Ok(CacheResponse::Clear),
CacheOperation::Get(_) | CacheOperation::Invalidate(_) => Ok(CacheResponse::Insert),
CacheOperation::Clear => Ok(CacheResponse::Get(None)),
}
}
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn ext_get_returns_value() {
let svc = CorrectService;
let result = CacheServiceExt::get(&svc, &"key".to_string()).await.unwrap();
assert_eq!(*result.unwrap().value(), 42);
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn ext_insert_returns_ok() {
let svc = CorrectService;
CacheServiceExt::insert(&svc, "key".to_string(), CacheEntry::new(42)).await.unwrap();
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn ext_invalidate_returns_ok() {
let svc = CorrectService;
CacheServiceExt::invalidate(&svc, &"key".to_string()).await.unwrap();
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn ext_clear_returns_ok() {
let svc = CorrectService;
CacheServiceExt::clear(&svc).await.unwrap();
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn ext_get_wrong_response_returns_error() {
let svc = WrongResponseService;
CacheServiceExt::get(&svc, &"key".to_string()).await.unwrap_err();
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn ext_insert_wrong_response_returns_error() {
let svc = WrongResponseService;
CacheServiceExt::insert(&svc, "key".to_string(), CacheEntry::new(42))
.await
.unwrap_err();
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn ext_invalidate_wrong_response_returns_error() {
let svc = WrongResponseService;
CacheServiceExt::invalidate(&svc, &"key".to_string()).await.unwrap_err();
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn ext_clear_wrong_response_returns_error() {
let svc = WrongResponseService;
CacheServiceExt::clear(&svc).await.unwrap_err();
}
}