Skip to main content

tower_resilience_cache/
layer.rs

1use crate::{Cache, CacheConfig};
2use std::hash::Hash;
3use std::sync::Arc;
4use tower::Layer;
5
6/// A Tower [`Layer`] that applies response caching to a service.
7///
8/// This layer wraps a service with a [`Cache`] middleware that stores
9/// successful responses and returns cached values for subsequent requests
10/// with the same key.
11///
12/// # State Isolation
13///
14/// **Note:** Each call to [`layer()`](Layer::layer) creates a new cache store.
15/// If you need multiple services to share the same cache (e.g., when using a
16/// `ServiceFactory` that creates per-session services), use
17/// [`SharedCacheLayer`](crate::SharedCacheLayer) instead, or call
18/// [`.shared()`](CacheLayer::shared) on this layer.
19///
20/// # Examples
21///
22/// ```
23/// use tower_resilience_cache::CacheLayer;
24/// use tower::ServiceBuilder;
25/// use std::time::Duration;
26///
27/// # async fn example() {
28/// let cache_layer = CacheLayer::builder()
29///     .max_size(100)
30///     .ttl(Duration::from_secs(60))
31///     .key_extractor(|req: &String| req.clone())
32///     .build();
33///
34/// let service = ServiceBuilder::new()
35///     .layer(cache_layer)
36///     .service(my_service());
37/// # }
38/// # fn my_service() -> impl tower::Service<String, Response = String, Error = std::io::Error> {
39/// #     tower::service_fn(|req: String| async move { Ok::<_, std::io::Error>(req) })
40/// # }
41/// ```
42#[derive(Clone)]
43pub struct CacheLayer<Req, K> {
44    config: Arc<CacheConfig<Req, K>>,
45}
46
47impl<Req, K> CacheLayer<Req, K>
48where
49    K: Hash + Eq + Clone + Send + 'static,
50{
51    /// Creates a new `CacheLayer` with the given configuration.
52    pub fn new(config: CacheConfig<Req, K>) -> Self {
53        Self {
54            config: Arc::new(config),
55        }
56    }
57
58    /// Creates a new builder for configuring a cache layer.
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// use tower_resilience_cache::CacheLayer;
64    /// use std::time::Duration;
65    ///
66    /// let layer = CacheLayer::builder()
67    ///     .max_size(100)
68    ///     .ttl(Duration::from_secs(60))
69    ///     .key_extractor(|req: &String| req.clone())
70    ///     .build();
71    /// ```
72    pub fn builder() -> crate::CacheConfigBuilder<Req, K> {
73        crate::CacheConfigBuilder::new()
74    }
75
76    /// Converts this cache layer into a [`SharedCacheLayer`](crate::SharedCacheLayer) that shares
77    /// the cache store across all services created via [`layer()`](Layer::layer).
78    ///
79    /// This is useful when multiple service instances need to share the same cache,
80    /// such as when services are created per-session or per-request.
81    ///
82    /// # Type Parameters
83    ///
84    /// - `Resp`: The response type that will be cached. This must match the
85    ///   `Response` type of any service this layer is applied to.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use tower_resilience_cache::CacheLayer;
91    /// use tower::ServiceBuilder;
92    /// use std::time::Duration;
93    ///
94    /// # async fn example() {
95    /// let shared_cache = CacheLayer::builder()
96    ///     .max_size(100)
97    ///     .ttl(Duration::from_secs(60))
98    ///     .key_extractor(|req: &String| req.clone())
99    ///     .build()
100    ///     .shared::<String>();  // Specify the response type
101    ///
102    /// // Both services share the same cache
103    /// let service1 = ServiceBuilder::new()
104    ///     .layer(shared_cache.clone())
105    ///     .service(my_service());
106    ///
107    /// let service2 = ServiceBuilder::new()
108    ///     .layer(shared_cache)
109    ///     .service(my_service());
110    /// # }
111    /// # fn my_service() -> impl tower::Service<String, Response = String, Error = std::io::Error> {
112    /// #     tower::service_fn(|req: String| async move { Ok::<_, std::io::Error>(req) })
113    /// # }
114    /// ```
115    pub fn shared<Resp>(self) -> crate::shared_layer::SharedCacheLayer<Req, K, Resp>
116    where
117        Resp: Clone + Send + 'static,
118    {
119        crate::shared_layer::SharedCacheLayer::from_config(self.config)
120    }
121}
122
123impl<S, Req, K> Layer<S> for CacheLayer<Req, K>
124where
125    K: Hash + Eq + Clone + Send + 'static,
126    S: tower::Service<Req>,
127    S::Response: Clone + Send + 'static,
128{
129    type Service = Cache<S, Req, K, S::Response>;
130
131    fn layer(&self, service: S) -> Self::Service {
132        Cache::new(service, Arc::clone(&self.config))
133    }
134}