use std::fmt::Debug;
use std::future::Future;
const POLL_AFTER_READY_ERROR: &str = "ResultIntoCachedFuture can't be polled after finishing";
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use chrono::Utc;
use pin_project::pin_project;
use crate::{
CachePolicy, EntityPolicyConfig,
predicate::{Predicate, PredicateResult},
value::CacheValue,
};
pub type ResponseCachePolicy<C> = CachePolicy<CacheValue<<C as CacheableResponse>::Cached>, C>;
#[derive(Debug, PartialEq, Eq)]
pub enum CacheState<Cached> {
Stale(Cached),
Actual(Cached),
Expired(Cached),
}
pub trait CacheableResponse
where
Self: Sized + Send + 'static,
Self::Cached: Clone,
{
type Cached;
type Subject: CacheableResponse;
type IntoCachedFuture: Future<Output = CachePolicy<Self::Cached, Self>> + Send;
type FromCachedFuture: Future<Output = Self> + Send;
fn cache_policy<P>(
self,
predicates: P,
config: &EntityPolicyConfig,
) -> impl Future<Output = ResponseCachePolicy<Self>> + Send
where
P: Predicate<Subject = Self::Subject> + Send + Sync;
fn into_cached(self) -> Self::IntoCachedFuture;
fn from_cached(cached: Self::Cached) -> Self::FromCachedFuture;
}
macro_rules! impl_cacheable_response_for_scalar {
($($ty:ty),* $(,)?) => {
$(
impl CacheableResponse for $ty {
type Cached = Self;
type Subject = Self;
type IntoCachedFuture = std::future::Ready<CachePolicy<Self, Self>>;
type FromCachedFuture = std::future::Ready<Self>;
async fn cache_policy<P>(
self,
predicates: P,
config: &EntityPolicyConfig,
) -> ResponseCachePolicy<Self>
where
P: Predicate<Subject = Self::Subject> + Send + Sync,
{
match predicates.check(self).await {
PredicateResult::Cacheable(data) => {
let cached = data.clone();
CachePolicy::Cacheable(CacheValue::new(
cached,
config.ttl.map(|d| Utc::now() + d),
config.stale_ttl.map(|d| Utc::now() + d),
))
}
PredicateResult::NonCacheable(data) => CachePolicy::NonCacheable(data),
}
}
fn into_cached(self) -> Self::IntoCachedFuture {
std::future::ready(CachePolicy::Cacheable(self))
}
fn from_cached(cached: Self) -> Self::FromCachedFuture {
std::future::ready(cached)
}
}
)*
};
}
impl_cacheable_response_for_scalar! {
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
bool, char,
String,
}
impl<T> CacheableResponse for Vec<T>
where
T: CacheableResponse<Cached = T, Subject = T> + Clone + Send + 'static,
{
type Cached = Self;
type Subject = Self;
type IntoCachedFuture = std::future::Ready<CachePolicy<Self, Self>>;
type FromCachedFuture = std::future::Ready<Self>;
async fn cache_policy<P>(
self,
predicates: P,
config: &EntityPolicyConfig,
) -> ResponseCachePolicy<Self>
where
P: Predicate<Subject = Self::Subject> + Send + Sync,
{
match predicates.check(self).await {
PredicateResult::Cacheable(data) => {
let cached = data.clone();
CachePolicy::Cacheable(CacheValue::new(
cached,
config.ttl.map(|d| Utc::now() + d),
config.stale_ttl.map(|d| Utc::now() + d),
))
}
PredicateResult::NonCacheable(data) => CachePolicy::NonCacheable(data),
}
}
fn into_cached(self) -> Self::IntoCachedFuture {
std::future::ready(CachePolicy::Cacheable(self))
}
fn from_cached(cached: Self) -> Self::FromCachedFuture {
std::future::ready(cached)
}
}
#[doc(hidden)]
#[pin_project(project = ResultIntoCachedProj)]
pub enum ResultIntoCachedFuture<T, E>
where
T: CacheableResponse,
{
Ok(#[pin] T::IntoCachedFuture),
Err(Option<E>),
}
impl<T, E> Future for ResultIntoCachedFuture<T, E>
where
T: CacheableResponse,
{
type Output = CachePolicy<T::Cached, Result<T, E>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.project() {
ResultIntoCachedProj::Ok(fut) => fut.poll(cx).map(|policy| match policy {
CachePolicy::Cacheable(res) => CachePolicy::Cacheable(res),
CachePolicy::NonCacheable(res) => CachePolicy::NonCacheable(Ok(res)),
}),
ResultIntoCachedProj::Err(e) => Poll::Ready(CachePolicy::NonCacheable(Err(e
.take()
.expect(POLL_AFTER_READY_ERROR)))),
}
}
}
#[doc(hidden)]
#[pin_project]
pub struct ResultFromCachedFuture<T, E>
where
T: CacheableResponse,
{
#[pin]
inner: T::FromCachedFuture,
_marker: PhantomData<E>,
}
impl<T, E> Future for ResultFromCachedFuture<T, E>
where
T: CacheableResponse,
{
type Output = Result<T, E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().inner.poll(cx).map(Ok)
}
}
impl<T, E> CacheableResponse for Result<T, E>
where
T: CacheableResponse + 'static,
E: Send + 'static,
T::Cached: Send,
{
type Cached = <T as CacheableResponse>::Cached;
type Subject = T;
type IntoCachedFuture = ResultIntoCachedFuture<T, E>;
type FromCachedFuture = ResultFromCachedFuture<T, E>;
async fn cache_policy<P>(
self,
predicates: P,
config: &EntityPolicyConfig,
) -> ResponseCachePolicy<Self>
where
P: Predicate<Subject = Self::Subject> + Send + Sync,
{
match self {
Ok(response) => match predicates.check(response).await {
PredicateResult::Cacheable(cacheable) => match cacheable.into_cached().await {
CachePolicy::Cacheable(res) => CachePolicy::Cacheable(CacheValue::new(
res,
config.ttl.map(|duration| Utc::now() + duration),
config.stale_ttl.map(|duration| Utc::now() + duration),
)),
CachePolicy::NonCacheable(res) => CachePolicy::NonCacheable(Ok(res)),
},
PredicateResult::NonCacheable(res) => CachePolicy::NonCacheable(Ok(res)),
},
Err(error) => ResponseCachePolicy::NonCacheable(Err(error)),
}
}
fn into_cached(self) -> Self::IntoCachedFuture {
match self {
Ok(response) => ResultIntoCachedFuture::Ok(response.into_cached()),
Err(error) => ResultIntoCachedFuture::Err(Some(error)),
}
}
fn from_cached(cached: Self::Cached) -> Self::FromCachedFuture {
ResultFromCachedFuture {
inner: T::from_cached(cached),
_marker: PhantomData,
}
}
}