tower_etag_cache/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::task::Poll;
4use tower_layer::Layer;
5use tower_service::Service;
6
7mod cache_provider;
8mod err;
9mod future;
10mod passthrough_predicate;
11mod response;
12
13#[cfg(feature = "simple-etag-cache-key")]
14pub mod simple_etag_cache_key;
15
16#[cfg(feature = "base64-blake3-body-etag")]
17pub mod base64_blake3_body_etag;
18
19#[cfg(feature = "const-lru-provider")]
20pub mod const_lru_provider;
21
22pub use cache_provider::*;
23pub use err::*;
24pub use future::*;
25pub use passthrough_predicate::*;
26pub use response::*;
27
28/// The eponymous tower `Service`
29#[derive(Clone, Copy, Debug)]
30pub struct EtagCache<C, P, S> {
31    cache_provider: C,
32    passthrough_predicate: P,
33    inner: S,
34}
35
36impl<C, P, S> EtagCache<C, P, S> {
37    pub fn new(cache_provider: C, passthrough_predicate: P, inner: S) -> Self {
38        Self {
39            cache_provider,
40            passthrough_predicate,
41            inner,
42        }
43    }
44}
45
46impl<C, S> EtagCache<C, DefaultPredicate, S> {
47    pub fn with_default_predicate(cache_provider: C, inner: S) -> Self {
48        Self {
49            cache_provider,
50            passthrough_predicate: DefaultPredicate,
51            inner,
52        }
53    }
54}
55
56/// The eponymous tower `Layer`
57#[derive(Clone, Copy, Debug)]
58pub struct EtagCacheLayer<C, P> {
59    cache_provider: C,
60    passthrough_predicate: P,
61}
62
63impl<C, P> EtagCacheLayer<C, P> {
64    pub fn new(cache_provider: C, passthrough_predicate: P) -> Self {
65        Self {
66            cache_provider,
67            passthrough_predicate,
68        }
69    }
70}
71
72impl<C> EtagCacheLayer<C, DefaultPredicate> {
73    pub fn with_default_predicate(cache_provider: C) -> Self {
74        Self {
75            cache_provider,
76            passthrough_predicate: DefaultPredicate,
77        }
78    }
79}
80
81impl<C: Clone, P: Clone, S> Layer<S> for EtagCacheLayer<C, P> {
82    type Service = EtagCache<C, P, S>;
83
84    fn layer(&self, inner: S) -> Self::Service {
85        EtagCache::new(
86            self.cache_provider.clone(),
87            self.passthrough_predicate.clone(),
88            inner,
89        )
90    }
91}
92
93impl<ReqBody, ResBody, C, P, S> Service<http::Request<ReqBody>> for EtagCache<C, P, S>
94where
95    C: CacheProvider<ReqBody, ResBody> + Clone,
96    P: PassthroughPredicate,
97    S: Service<http::Request<ReqBody>, Response = http::Response<ResBody>> + Clone,
98{
99    type Response = http::Response<EtagCacheResBody<ResBody, C::TResBody>>;
100
101    type Error = EtagCacheServiceError<
102        <C as Service<http::Request<ReqBody>>>::Error,
103        S::Error,
104        <C as Service<(C::Key, http::Response<ResBody>)>>::Error,
105    >;
106
107    type Future = EtagCacheServiceFuture<ReqBody, ResBody, C, P, S>;
108
109    /// `EtagCacheServiceFuture` poll_ready()s the different services depending on whether
110    /// the cache should be used
111    fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> {
112        Poll::Ready(Ok(()))
113    }
114
115    fn call(&mut self, req: http::Request<ReqBody>) -> Self::Future {
116        match self.passthrough_predicate.should_passthrough_req(&req) {
117            true => EtagCacheServiceFuture::passthrough(
118                self.cache_provider.clone(),
119                self.passthrough_predicate.clone(),
120                self.inner.clone(),
121                req,
122            ),
123            false => EtagCacheServiceFuture::start(
124                self.cache_provider.clone(),
125                self.passthrough_predicate.clone(),
126                self.inner.clone(),
127                req,
128            ),
129        }
130    }
131}