crates_docs/tools/docs/cache/
mod.rs1mod key;
24mod stats;
25mod ttl;
26
27use crate::cache::Cache;
28use std::sync::Arc;
29
30pub use key::CacheKeyGenerator;
32pub use stats::CacheStats;
33pub use ttl::DocCacheTtl;
34
35#[derive(Clone)]
45pub struct DocCache {
46 cache: Arc<dyn Cache>,
47 ttl: DocCacheTtl,
48 stats: CacheStats,
49}
50
51impl DocCache {
52 pub fn new(cache: Arc<dyn Cache>) -> Self {
69 Self {
70 cache,
71 ttl: DocCacheTtl::default(),
72 stats: CacheStats::new(),
73 }
74 }
75
76 #[must_use]
100 pub fn with_ttl(cache: Arc<dyn Cache>, ttl: DocCacheTtl) -> Self {
101 Self {
102 cache,
103 ttl,
104 stats: CacheStats::new(),
105 }
106 }
107
108 pub async fn get_crate_docs(&self, crate_name: &str, version: Option<&str>) -> Option<String> {
119 let key = CacheKeyGenerator::crate_cache_key(crate_name, version);
120 let result = self.cache.get(&key).await;
121 if result.is_some() {
122 self.stats.record_hit();
123 } else {
124 self.stats.record_miss();
125 }
126 result
127 }
128
129 pub async fn set_crate_docs(
141 &self,
142 crate_name: &str,
143 version: Option<&str>,
144 content: String,
145 ) -> crate::error::Result<()> {
146 let key = CacheKeyGenerator::crate_cache_key(crate_name, version);
147 let ttl = self.ttl.crate_docs_duration();
148 self.cache.set(key, content, Some(ttl)).await?;
149 self.stats.record_set();
150 Ok(())
151 }
152
153 pub async fn get_search_results(&self, query: &str, limit: u32) -> Option<String> {
164 let key = CacheKeyGenerator::search_cache_key(query, limit);
165 let result = self.cache.get(&key).await;
166 if result.is_some() {
167 self.stats.record_hit();
168 } else {
169 self.stats.record_miss();
170 }
171 result
172 }
173
174 pub async fn set_search_results(
186 &self,
187 query: &str,
188 limit: u32,
189 content: String,
190 ) -> crate::error::Result<()> {
191 let key = CacheKeyGenerator::search_cache_key(query, limit);
192 let ttl = self.ttl.search_results_duration();
193 self.cache.set(key, content, Some(ttl)).await?;
194 self.stats.record_set();
195 Ok(())
196 }
197
198 pub async fn get_item_docs(
210 &self,
211 crate_name: &str,
212 item_path: &str,
213 version: Option<&str>,
214 ) -> Option<String> {
215 let key = CacheKeyGenerator::item_cache_key(crate_name, item_path, version);
216 let result = self.cache.get(&key).await;
217 if result.is_some() {
218 self.stats.record_hit();
219 } else {
220 self.stats.record_miss();
221 }
222 result
223 }
224
225 pub async fn set_item_docs(
238 &self,
239 crate_name: &str,
240 item_path: &str,
241 version: Option<&str>,
242 content: String,
243 ) -> crate::error::Result<()> {
244 let key = CacheKeyGenerator::item_cache_key(crate_name, item_path, version);
245 let ttl = self.ttl.item_docs_duration();
246 self.cache.set(key, content, Some(ttl)).await?;
247 self.stats.record_set();
248 Ok(())
249 }
250
251 pub async fn clear(&self) -> crate::error::Result<()> {
257 self.cache.clear().await
258 }
259
260 #[must_use]
262 pub fn stats(&self) -> &CacheStats {
263 &self.stats
264 }
265
266 #[must_use]
268 pub fn ttl(&self) -> &DocCacheTtl {
269 &self.ttl
270 }
271}
272
273impl Default for DocCache {
274 fn default() -> Self {
275 let cache = Arc::new(crate::cache::memory::MemoryCache::new(1000));
276 Self::new(cache)
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283 use crate::cache::memory::MemoryCache;
284
285 #[tokio::test]
286 async fn test_doc_cache() {
287 let memory_cache = MemoryCache::new(100);
288 let cache = Arc::new(memory_cache);
289 let doc_cache = DocCache::new(cache);
290
291 doc_cache
293 .set_crate_docs("serde", Some("1.0"), "Test docs".to_string())
294 .await
295 .expect("set_crate_docs should succeed");
296 let cached = doc_cache.get_crate_docs("serde", Some("1.0")).await;
297 assert_eq!(cached, Some("Test docs".to_string()));
298
299 doc_cache
301 .set_search_results("web framework", 10, "Search results".to_string())
302 .await
303 .expect("set_search_results should succeed");
304 let search_cached = doc_cache.get_search_results("web framework", 10).await;
305 assert_eq!(search_cached, Some("Search results".to_string()));
306
307 doc_cache
309 .set_item_docs(
310 "serde",
311 "serde::Serialize",
312 Some("1.0"),
313 "Item docs".to_string(),
314 )
315 .await
316 .expect("set_item_docs should succeed");
317 let item_cached = doc_cache
318 .get_item_docs("serde", "serde::Serialize", Some("1.0"))
319 .await;
320 assert_eq!(item_cached, Some("Item docs".to_string()));
321
322 doc_cache.clear().await.expect("clear should succeed");
324 let cleared = doc_cache.get_crate_docs("serde", Some("1.0")).await;
325 assert_eq!(cleared, None);
326 }
327
328 #[tokio::test]
329 async fn test_doc_cache_with_ttl() {
330 let memory_cache = MemoryCache::new(100);
331 let cache = Arc::new(memory_cache);
332
333 let ttl = DocCacheTtl {
334 crate_docs_secs: 7200,
335 search_results_secs: 600,
336 item_docs_secs: 3600,
337 jitter_ratio: 0.0, };
339
340 let doc_cache = DocCache::with_ttl(cache, ttl);
341
342 assert_eq!(doc_cache.ttl().crate_docs_secs, 7200);
343 assert_eq!(doc_cache.ttl().search_results_secs, 600);
344 assert_eq!(doc_cache.ttl().item_docs_secs, 3600);
345 }
346
347 #[tokio::test]
348 async fn test_doc_cache_stats() {
349 let memory_cache = MemoryCache::new(100);
350 let cache = Arc::new(memory_cache);
351 let doc_cache = DocCache::new(cache);
352
353 doc_cache
355 .set_crate_docs("serde", None, "docs".to_string())
356 .await
357 .ok();
358 doc_cache.get_crate_docs("serde", None).await;
359
360 doc_cache.get_crate_docs("nonexistent", None).await;
362
363 assert_eq!(doc_cache.stats().hits(), 1);
364 assert_eq!(doc_cache.stats().misses(), 1);
365 assert_eq!(doc_cache.stats().sets(), 1);
366 }
367
368 #[test]
369 fn test_doc_cache_default() {
370 let doc_cache = DocCache::default();
371 assert_eq!(doc_cache.ttl().crate_docs_secs, 3600);
372 assert_eq!(doc_cache.ttl().search_results_secs, 300);
373 assert_eq!(doc_cache.ttl().item_docs_secs, 1800);
374 }
375}