1use crate::xutil;
6use moka::Expiry;
7use moka::sync::Cache as MokaCache;
8use std::any::Any;
9use std::sync::Arc;
10use std::time::{Duration, Instant};
11
12#[derive(Clone)]
14struct CacheEntry {
15 value: Arc<dyn Any + Send + Sync>,
16 custom_ttl: Option<Duration>,
18}
19
20struct PerEntryExpiry;
24
25impl Expiry<String, CacheEntry> for PerEntryExpiry {
26 fn expire_after_create(
27 &self,
28 _key: &String,
29 value: &CacheEntry,
30 _current_time: Instant,
31 ) -> Option<Duration> {
32 value.custom_ttl
33 }
34
35 fn expire_after_update(
36 &self,
37 _key: &String,
38 value: &CacheEntry,
39 _updated_at: Instant,
40 _duration_until_expiry: Option<Duration>,
41 ) -> Option<Duration> {
42 value.custom_ttl
43 }
44}
45
46pub struct Cache {
63 inner: MokaCache<String, CacheEntry>,
64 default_ttl: Duration,
65}
66
67impl Cache {
68 pub fn new(max_capacity: u64, default_ttl: Duration) -> Self {
70 let inner = MokaCache::builder()
71 .max_capacity(max_capacity)
72 .time_to_live(default_ttl)
73 .expire_after(PerEntryExpiry)
74 .build();
75
76 Self { inner, default_ttl }
77 }
78
79 pub fn get<V: Any + Clone + Send + Sync>(&self, key: &str) -> Option<V> {
83 let entry = self.inner.get(key)?;
84 let result = entry.value.downcast_ref::<V>().cloned();
85 if result.is_none() {
86 xutil::warn_if_enable_debug(&format!(
87 "xcache: type mismatch for key=[{key}], expected=[{}]",
88 std::any::type_name::<V>()
89 ));
90 }
91 result
92 }
93
94 pub fn set<V: Any + Send + Sync + 'static>(&self, key: &str, value: V) {
96 self.inner.insert(
97 key.to_string(),
98 CacheEntry {
99 value: Arc::new(value),
100 custom_ttl: None,
101 },
102 );
103 }
104
105 pub fn set_with_ttl<V: Any + Send + Sync + 'static>(&self, key: &str, value: V, ttl: Duration) {
110 self.inner.insert(
111 key.to_string(),
112 CacheEntry {
113 value: Arc::new(value),
114 custom_ttl: Some(ttl),
115 },
116 );
117 }
118
119 pub fn del(&self, key: &str) {
121 self.inner.invalidate(key);
122 }
123
124 pub fn entry_count(&self) -> u64 {
126 self.inner.entry_count()
127 }
128
129 pub fn default_ttl(&self) -> Duration {
131 self.default_ttl
132 }
133}
134
135impl std::fmt::Debug for Cache {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 f.debug_struct("Cache")
138 .field("entry_count", &self.inner.entry_count())
139 .field("default_ttl", &self.default_ttl)
140 .finish()
141 }
142}