use std::ops::{Deref, DerefMut}; /// A generic trait which exposes types of cache's key and value, /// as well as the notion of cache entry invalidation. /// This is useful for [`Cached`]. #[allow(async_fn_in_trait)] pub trait Cache { /// Used for entry invalidation. type LookupInfo; /// Invalidate an entry using a lookup info. /// We don't have an empty default impl because it's error-prone. async fn invalidate(&self, _: &Self::LookupInfo); } impl Cache for &C { type LookupInfo = C::LookupInfo; async fn invalidate(&self, info: &Self::LookupInfo) { C::invalidate(self, info).await } } /// Wrapper for convenient entry invalidation. pub struct Cached { /// Cache + lookup info. pub token: Option<(C, C::LookupInfo)>, /// The value itself. pub value: V, } impl Cached { /// Place any entry into this wrapper; invalidation will be a no-op. pub fn new_uncached(value: V) -> Self { Self { token: None, value } } pub fn take_value(self) -> (Cached, V) { ( Cached { token: self.token, value: (), }, self.value, ) } /// Drop this entry from a cache if it's still there. pub async fn invalidate(self) -> V { if let Some((cache, info)) = &self.token { cache.invalidate(info).await; } self.value } /// Tell if this entry is actually cached. pub fn cached(&self) -> bool { self.token.is_some() } } impl Deref for Cached { type Target = V; fn deref(&self) -> &Self::Target { &self.value } } impl DerefMut for Cached { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value } } impl Cache for moka::future::Cache where K: std::hash::Hash + Eq + Send + Sync + 'static, V: Clone + Send + Sync + 'static, S: std::hash::BuildHasher + Clone + Send + Sync + 'static, { type LookupInfo = K; async fn invalidate(&self, key: &Self::LookupInfo) { moka::future::Cache::invalidate(self, key).await } }