http_cache_quickcache/
lib.rs

1//! HTTP caching manager implementation using QuickCache.
2//!
3//! This crate provides a [`CacheManager`] implementation using the
4//! [QuickCache](https://github.com/arthurprs/quick-cache) in-memory cache.
5//! QuickCache is an in-memory cache that can be used for applications that
6//! need cache access with predictable memory usage.
7//!
8//! ## Basic Usage
9//!
10//! ```rust
11//! use http_cache_quickcache::QuickManager;
12//! use quick_cache::sync::Cache;
13//!
14//! // Create a cache with a maximum of 1000 entries
15//! let cache = Cache::new(1000);
16//! let manager = QuickManager::new(cache);
17//!
18//! // Use with any HTTP cache implementation that accepts a CacheManager
19//! ```
20//!
21//! ## Integration with HTTP Cache Middleware
22//!
23//! ### With Tower Services
24//!
25//! ```no_run
26//! use tower::{Service, ServiceExt};
27//! use http::{Request, Response, StatusCode};
28//! use http_body_util::Full;
29//! use bytes::Bytes;
30//! use http_cache_quickcache::QuickManager;
31//! use std::convert::Infallible;
32//!
33//! // Example Tower service that uses QuickManager for caching
34//! #[derive(Clone)]
35//! struct CachingService {
36//!     cache_manager: QuickManager,
37//! }
38//!
39//! impl Service<Request<Full<Bytes>>> for CachingService {
40//!     type Response = Response<Full<Bytes>>;
41//!     type Error = Box<dyn std::error::Error + Send + Sync>;
42//!     type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send>>;
43//!
44//!     fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
45//!         std::task::Poll::Ready(Ok(()))
46//!     }
47//!
48//!     fn call(&mut self, req: Request<Full<Bytes>>) -> Self::Future {
49//!         let manager = self.cache_manager.clone();
50//!         Box::pin(async move {
51//!             // Cache logic using the manager would go here
52//!             let response = Response::builder()
53//!                 .status(StatusCode::OK)
54//!                 .body(Full::new(Bytes::from("Hello from cached service!")))?;
55//!             Ok(response)
56//!         })
57//!     }
58//! }
59//! ```
60//!
61//! ### With Hyper
62//!
63//! ```no_run
64//! use hyper::{Request, Response, StatusCode, body::Incoming};
65//! use http_body_util::Full;
66//! use bytes::Bytes;
67//! use http_cache_quickcache::QuickManager;
68//! use std::convert::Infallible;
69//!
70//! async fn handle_request(
71//!     _req: Request<Incoming>,
72//!     cache_manager: QuickManager,
73//! ) -> Result<Response<Full<Bytes>>, Infallible> {
74//!     // Use cache_manager here for caching responses
75//!     Ok(Response::builder()
76//!         .status(StatusCode::OK)
77//!         .header("cache-control", "max-age=3600")
78//!         .body(Full::new(Bytes::from("Hello from Hyper with caching!")))
79//!         .unwrap())
80//! }
81//! ```
82//!
83//! ## Usage Characteristics
84//!
85//! QuickCache is designed for scenarios where:
86//! - You need predictable memory usage
87//! - In-memory storage is acceptable
88//! - You want to avoid complex configuration
89//! - Memory-based caching fits your use case
90//!
91//! For applications that need persistent caching across restarts, consider using
92//! [`CACacheManager`](https://docs.rs/http-cache/latest/http_cache/struct.CACacheManager.html)
93//! instead, which provides disk-based storage.
94
95use http_cache::{CacheManager, HttpResponse, Result};
96
97use std::{fmt, sync::Arc};
98
99use http_cache_semantics::CachePolicy;
100use quick_cache::sync::Cache;
101use serde::{Deserialize, Serialize};
102
103/// HTTP cache manager implementation using QuickCache.
104///
105/// This manager provides in-memory caching using the QuickCache library and implements
106/// the [`CacheManager`] trait for HTTP caching support.
107///
108/// ## Examples
109///
110/// ### Basic Usage
111///
112/// ```rust
113/// use http_cache_quickcache::QuickManager;
114/// use quick_cache::sync::Cache;
115///
116/// // Create a cache with 1000 entry limit
117/// let cache = Cache::new(1000);
118/// let manager = QuickManager::new(cache);
119/// ```
120///
121/// ## Default Configuration
122///
123/// The default configuration creates a cache with 42 entries:
124///
125/// ```rust
126/// use http_cache_quickcache::QuickManager;
127///
128/// let manager = QuickManager::default();
129/// ```
130#[derive(Clone)]
131pub struct QuickManager {
132    /// The underlying QuickCache instance.
133    ///
134    /// This is wrapped in an `Arc` to allow sharing across threads while
135    /// maintaining the `Clone` implementation for the manager.
136    cache: Arc<Cache<String, Arc<Vec<u8>>>>,
137}
138
139impl fmt::Debug for QuickManager {
140    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141        f.debug_struct("QuickManager")
142            .field("cache", &"Cache<String, Arc<Vec<u8>>>")
143            .finish_non_exhaustive()
144    }
145}
146
147impl Default for QuickManager {
148    /// Creates a new QuickManager with a default cache size of 42 entries.
149    ///
150    /// For production use, consider using [`QuickManager::new`] with a
151    /// cache size appropriate for your use case.
152    fn default() -> Self {
153        Self::new(Cache::new(42))
154    }
155}
156
157#[derive(Debug, Deserialize, Serialize)]
158struct Store {
159    response: HttpResponse,
160    policy: CachePolicy,
161}
162
163impl QuickManager {
164    /// Creates a new QuickManager from a pre-configured QuickCache.
165    ///
166    /// This allows you to customize the cache configuration, such as setting
167    /// the maximum number of entries.
168    ///
169    /// # Arguments
170    ///
171    /// * `cache` - A configured QuickCache instance
172    ///
173    /// # Examples
174    ///
175    /// ```rust
176    /// use http_cache_quickcache::QuickManager;
177    /// use quick_cache::sync::Cache;
178    ///
179    /// // Create a cache with 10,000 entry limit
180    /// let cache = Cache::new(10_000);
181    /// let manager = QuickManager::new(cache);
182    /// ```
183    ///
184    /// ## Cache Size Considerations
185    ///
186    /// Choose your cache size based on:
187    /// - Available memory
188    /// - Expected number of unique cacheable requests
189    /// - Average response size
190    /// - Cache hit rate requirements
191    ///
192    /// ```rust
193    /// use http_cache_quickcache::QuickManager;
194    /// use quick_cache::sync::Cache;
195    ///
196    /// // For an application with many unique endpoints
197    /// let large_cache = QuickManager::new(Cache::new(50_000));
198    ///
199    /// // For an application with few cacheable responses
200    /// let small_cache = QuickManager::new(Cache::new(100));
201    /// ```
202    pub fn new(cache: Cache<String, Arc<Vec<u8>>>) -> Self {
203        Self { cache: Arc::new(cache) }
204    }
205}
206
207#[async_trait::async_trait]
208impl CacheManager for QuickManager {
209    async fn get(
210        &self,
211        cache_key: &str,
212    ) -> Result<Option<(HttpResponse, CachePolicy)>> {
213        let store: Store = match self.cache.get(cache_key) {
214            Some(d) => bincode::deserialize(&d)?,
215            None => return Ok(None),
216        };
217        Ok(Some((store.response, store.policy)))
218    }
219
220    async fn put(
221        &self,
222        cache_key: String,
223        response: HttpResponse,
224        policy: CachePolicy,
225    ) -> Result<HttpResponse> {
226        let data = Store { response: response.clone(), policy };
227        let bytes = bincode::serialize(&data)?;
228        self.cache.insert(cache_key, Arc::new(bytes));
229        Ok(response)
230    }
231
232    async fn delete(&self, cache_key: &str) -> Result<()> {
233        self.cache.remove(cache_key);
234        Ok(())
235    }
236}
237
238#[cfg(test)]
239mod test;