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/// .unwrap();
34///
35/// let service = ServiceBuilder::new()
36/// .layer(cache_layer)
37/// .service(my_service());
38/// # }
39/// # fn my_service() -> impl tower::Service<String, Response = String, Error = std::io::Error> {
40/// # tower::service_fn(|req: String| async move { Ok::<_, std::io::Error>(req) })
41/// # }
42/// ```
43#[derive(Clone)]
44pub struct CacheLayer<Req, K> {
45 config: Arc<CacheConfig<Req, K>>,
46}
47
48impl<Req, K> CacheLayer<Req, K>
49where
50 K: Hash + Eq + Clone + Send + 'static,
51{
52 /// Creates a new `CacheLayer` with the given configuration.
53 pub fn new(config: CacheConfig<Req, K>) -> Self {
54 Self {
55 config: Arc::new(config),
56 }
57 }
58
59 /// Creates a new builder for configuring a cache layer.
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// use tower_resilience_cache::CacheLayer;
65 /// use std::time::Duration;
66 ///
67 /// let layer = CacheLayer::builder()
68 /// .max_size(100)
69 /// .ttl(Duration::from_secs(60))
70 /// .key_extractor(|req: &String| req.clone())
71 /// .build()
72 /// .unwrap();
73 /// ```
74 pub fn builder() -> crate::CacheConfigBuilder<Req, K> {
75 crate::CacheConfigBuilder::new()
76 }
77
78 /// Converts this cache layer into a [`SharedCacheLayer`](crate::SharedCacheLayer) that shares
79 /// the cache store across all services created via [`layer()`](Layer::layer).
80 ///
81 /// This is useful when multiple service instances need to share the same cache,
82 /// such as when services are created per-session or per-request.
83 ///
84 /// # Type Parameters
85 ///
86 /// - `Resp`: The response type that will be cached. This must match the
87 /// `Response` type of any service this layer is applied to.
88 ///
89 /// # Examples
90 ///
91 /// ```
92 /// use tower_resilience_cache::CacheLayer;
93 /// use tower::ServiceBuilder;
94 /// use std::time::Duration;
95 ///
96 /// # async fn example() {
97 /// let shared_cache = CacheLayer::builder()
98 /// .max_size(100)
99 /// .ttl(Duration::from_secs(60))
100 /// .key_extractor(|req: &String| req.clone())
101 /// .build()
102 /// .unwrap()
103 /// .shared::<String>(); // Specify the response type
104 ///
105 /// // Both services share the same cache
106 /// let service1 = ServiceBuilder::new()
107 /// .layer(shared_cache.clone())
108 /// .service(my_service());
109 ///
110 /// let service2 = ServiceBuilder::new()
111 /// .layer(shared_cache)
112 /// .service(my_service());
113 /// # }
114 /// # fn my_service() -> impl tower::Service<String, Response = String, Error = std::io::Error> {
115 /// # tower::service_fn(|req: String| async move { Ok::<_, std::io::Error>(req) })
116 /// # }
117 /// ```
118 pub fn shared<Resp>(self) -> crate::shared_layer::SharedCacheLayer<Req, K, Resp>
119 where
120 Resp: Clone + Send + 'static,
121 {
122 crate::shared_layer::SharedCacheLayer::from_config(self.config)
123 }
124}
125
126impl<S, Req, K> Layer<S> for CacheLayer<Req, K>
127where
128 K: Hash + Eq + Clone + Send + 'static,
129 S: tower::Service<Req>,
130 S::Response: Clone + Send + 'static,
131{
132 type Service = Cache<S, Req, K, S::Response>;
133
134 fn layer(&self, service: S) -> Self::Service {
135 Cache::new(service, Arc::clone(&self.config))
136 }
137}