#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::manual_async_fn)]
use crate::time::Duration;
#[cfg(feature = "proc_macro")]
#[cfg_attr(docsrs, doc(cfg(feature = "proc_macro")))]
pub use macros::{cached, concurrent_cached, once, Return};
#[cfg(feature = "async_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "async_core")))]
use std::future::Future;
#[cfg(any(feature = "redis_smol", feature = "redis_tokio"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "redis_smol", feature = "redis_tokio"))))]
pub use stores::{AsyncRedisCache, AsyncRedisCacheBuilder};
pub use stores::{
BuildError, CacheEvict, Expires, ExpiringLruCache, ExpiringLruCacheBuilder, LruCache,
LruCacheBuilder, UnboundCache, UnboundCacheBuilder,
};
#[cfg(feature = "disk_store")]
#[cfg_attr(docsrs, doc(cfg(feature = "disk_store")))]
pub use stores::{DiskCache, DiskCacheBuildError, DiskCacheBuilder, DiskCacheError};
#[cfg(feature = "time_stores")]
#[cfg_attr(docsrs, doc(cfg(feature = "time_stores")))]
pub use stores::{
HasEvict, LruTtlCache, LruTtlCacheBuilder, NoEvict, TtlCache, TtlCacheBuilder, TtlSortedCache,
TtlSortedCacheBuilder, TtlSortedCacheError,
};
#[cfg(feature = "redis_store")]
#[cfg_attr(docsrs, doc(cfg(feature = "redis_store")))]
pub use stores::{RedisCache, RedisCacheBuildError, RedisCacheBuilder, RedisCacheError};
mod lru_list;
#[cfg(feature = "proc_macro")]
#[cfg_attr(docsrs, doc(cfg(feature = "proc_macro")))]
pub mod macros;
pub mod stores;
pub use web_time as time;
#[doc(hidden)]
pub use web_time;
#[cfg(feature = "async")]
#[doc(hidden)]
pub mod async_sync {
pub use tokio::sync::Mutex;
pub use tokio::sync::OnceCell;
pub use tokio::sync::RwLock;
}
#[doc(hidden)]
pub mod sync_sync {
pub use parking_lot::Mutex;
pub use parking_lot::RwLock;
}
pub trait Cached<K, V> {
fn cache_get<Q>(&mut self, k: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized;
fn cache_get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized;
fn cache_set(&mut self, k: K, v: V) -> Option<V>;
fn cache_try_set(&mut self, k: K, v: V) -> Result<Option<V>, Box<dyn std::error::Error>> {
Ok(self.cache_set(k, v))
}
fn cache_get_or_set_with<F: FnOnce() -> V>(&mut self, key: K, f: F) -> &mut V;
fn cache_try_get_or_set_with<F: FnOnce() -> Result<V, E>, E>(
&mut self,
key: K,
f: F,
) -> Result<&mut V, E>;
fn cache_remove<Q>(&mut self, k: &Q) -> Option<V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized;
fn cache_clear(&mut self);
fn cache_reset(&mut self);
fn cache_size(&self) -> usize;
fn cache_reset_metrics(&mut self) {}
fn cache_hits(&self) -> Option<u64> {
None
}
fn cache_misses(&self) -> Option<u64> {
None
}
fn cache_capacity(&self) -> Option<usize> {
None
}
fn get<Q>(&mut self, k: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.cache_get(k)
}
fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.cache_get_mut(k)
}
fn set(&mut self, k: K, v: V) -> Option<V> {
self.cache_set(k, v)
}
fn try_set(&mut self, k: K, v: V) -> Result<Option<V>, Box<dyn std::error::Error>> {
self.cache_try_set(k, v)
}
fn get_or_set_with<F: FnOnce() -> V>(&mut self, key: K, f: F) -> &mut V {
self.cache_get_or_set_with(key, f)
}
fn try_get_or_set_with<F: FnOnce() -> Result<V, E>, E>(
&mut self,
k: K,
f: F,
) -> Result<&mut V, E> {
self.cache_try_get_or_set_with(k, f)
}
fn remove<Q>(&mut self, k: &Q) -> Option<V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.cache_remove(k)
}
fn contains<Q>(&mut self, k: &Q) -> bool
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.get(k).is_some()
}
fn clear(&mut self) {
self.cache_clear()
}
fn len(&self) -> usize {
self.cache_size()
}
fn is_empty(&self) -> bool {
self.cache_size() == 0
}
fn hits(&self) -> Option<u64> {
self.cache_hits()
}
fn misses(&self) -> Option<u64> {
self.cache_misses()
}
fn metrics(&self) -> CacheMetrics {
CacheMetrics {
hits: self.cache_hits(),
misses: self.cache_misses(),
evictions: self.cache_evictions(),
size: self.cache_size(),
capacity: self.cache_capacity(),
}
}
fn cache_evictions(&self) -> Option<u64> {
None
}
}
pub trait CachedIter<K, V> {
fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a K, &'a V)> + 'a
where
Self: Sized,
K: 'a,
V: 'a;
fn keys<'a>(&'a self) -> impl Iterator<Item = &'a K> + 'a
where
Self: Sized,
K: 'a,
V: 'a,
{
self.iter().map(|(k, _)| k)
}
fn values<'a>(&'a self) -> impl Iterator<Item = &'a V> + 'a
where
Self: Sized,
K: 'a,
V: 'a,
{
self.iter().map(|(_, v)| v)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CacheMetrics {
pub hits: Option<u64>,
pub misses: Option<u64>,
pub evictions: Option<u64>,
pub size: usize,
pub capacity: Option<usize>,
}
impl CacheMetrics {
pub fn hit_ratio(&self) -> Option<f64> {
let hits = self.hits?;
let misses = self.misses?;
let total = hits + misses;
if total == 0 {
None
} else {
Some(hits as f64 / total as f64)
}
}
}
pub trait CachedPeek<K, V> {
fn cache_peek<Q>(&self, k: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized;
}
pub trait CachedRead<K, V>: CachedPeek<K, V> {
fn cache_get_read<Q>(&self, k: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.cache_peek(k)
}
}
pub trait CloneCached<K, V> {
fn cache_get_with_expiry_status<Q>(&mut self, key: &Q) -> (Option<V>, bool)
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized;
fn get_with_expiry_status<Q>(&mut self, key: &Q) -> (Option<V>, bool)
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
self.cache_get_with_expiry_status(key)
}
}
#[cfg(feature = "time_stores")]
#[cfg_attr(docsrs, doc(cfg(feature = "time_stores")))]
pub trait CacheTtl {
fn ttl(&self) -> Option<Duration>;
fn set_ttl(&mut self, ttl: Duration) -> Option<Duration>;
fn unset_ttl(&mut self) -> Option<Duration>;
fn refresh_on_hit(&self) -> bool {
false
}
fn set_refresh_on_hit(&mut self, _refresh: bool) -> bool {
false
}
}
#[cfg(feature = "async_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "async_core")))]
pub trait CachedAsync<K, V> {
fn async_get_or_set_with<'a, F, Fut>(
&'a mut self,
k: K,
f: F,
) -> impl Future<Output = &'a mut V> + Send + 'a
where
K: 'a,
V: Send + 'a,
F: FnOnce() -> Fut + Send + 'a,
Fut: Future<Output = V> + Send + 'a;
fn async_try_get_or_set_with<'a, F, Fut, E>(
&'a mut self,
k: K,
f: F,
) -> impl Future<Output = Result<&'a mut V, E>> + Send + 'a
where
K: 'a,
V: Send + 'a,
E: 'a,
F: FnOnce() -> Fut + Send + 'a,
Fut: Future<Output = Result<V, E>> + Send + 'a;
fn get_async<'a, Q>(&'a mut self, k: &'a Q) -> impl Future<Output = Option<&'a V>> + Send + 'a
where
Self: Cached<K, V> + Send,
K: std::borrow::Borrow<Q> + 'a,
Q: std::hash::Hash + Eq + ?Sized + Sync,
V: 'a,
{
async move { self.get(k) }
}
fn set_async(&mut self, k: K, v: V) -> impl Future<Output = Option<V>> + Send
where
Self: Cached<K, V> + Send,
K: Send,
V: Send,
{
async move { self.set(k, v) }
}
fn remove_async<'a, Q>(&'a mut self, k: &'a Q) -> impl Future<Output = Option<V>> + Send + 'a
where
Self: Cached<K, V> + Send,
K: std::borrow::Borrow<Q> + 'a,
Q: std::hash::Hash + Eq + ?Sized + Sync,
V: 'a,
{
async move { self.remove(k) }
}
fn clear_async(&mut self) -> impl Future<Output = ()> + Send
where
Self: Cached<K, V> + Send,
{
async move { self.clear() }
}
}
pub trait ConcurrentCached<K, V> {
type Error;
fn cache_get(&self, k: &K) -> Result<Option<V>, Self::Error>;
fn cache_set(&self, k: K, v: V) -> Result<Option<V>, Self::Error>;
fn cache_remove(&self, k: &K) -> Result<Option<V>, Self::Error>;
fn cache_delete(&self, k: &K) -> Result<bool, Self::Error> {
self.cache_remove(k).map(|removed| removed.is_some())
}
fn set_refresh_on_hit(&mut self, refresh: bool) -> bool;
fn ttl(&self) -> Option<Duration> {
None
}
fn set_ttl(&mut self, _ttl: Duration) -> Option<Duration> {
None
}
fn unset_ttl(&mut self) -> Option<Duration> {
None
}
}
#[cfg(feature = "async_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "async_core")))]
pub trait ConcurrentCachedAsync<K, V> {
type Error;
fn cache_get(&self, k: &K) -> impl Future<Output = Result<Option<V>, Self::Error>> + Send;
fn cache_set(&self, k: K, v: V) -> impl Future<Output = Result<Option<V>, Self::Error>> + Send;
fn cache_remove(&self, k: &K) -> impl Future<Output = Result<Option<V>, Self::Error>> + Send;
fn cache_delete(&self, k: &K) -> impl Future<Output = Result<bool, Self::Error>> + Send
where
Self: Sync,
K: Sync,
{
async move { self.cache_remove(k).await.map(|removed| removed.is_some()) }
}
fn set_refresh_on_hit(&mut self, refresh: bool) -> bool;
fn ttl(&self) -> Option<Duration> {
None
}
fn set_ttl(&mut self, _ttl: Duration) -> Option<Duration> {
None
}
fn unset_ttl(&mut self) -> Option<Duration> {
None
}
}