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}