Skip to main content

timvw_rust_runtime/
local_service.rs

1use std::fmt;
2use std::future::Future;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use tower::{Service, ServiceExt};
7
8type LocalBoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
9
10/// A boxed service that can be cloned, like tower's `BoxCloneService`, but neither the service nor its future need be
11/// `Send`: [`HttpService`](crate::HttpService) on WebAssembly (`target_family = "wasm"`), where reqwest's futures are
12/// not `Send`.
13///
14/// Natively [`HttpService`](crate::HttpService) is tower's `BoxCloneSyncService`; this type compiles there too, for
15/// code of your own.
16pub struct LocalBoxCloneService<T, U, E>(
17    Box<dyn CloneService<T, Response = U, Error = E, Future = LocalBoxFuture<Result<U, E>>>>,
18);
19
20impl<T, U, E> LocalBoxCloneService<T, U, E> {
21    /// Boxes `inner`.
22    pub fn new<S>(inner: S) -> Self
23    where
24        S: Service<T, Response = U, Error = E> + Clone + 'static,
25        S::Future: 'static,
26    {
27        let inner = inner.map_future(|future| Box::pin(future) as LocalBoxFuture<_>);
28        Self(Box::new(inner))
29    }
30}
31
32impl<T, U, E> Service<T> for LocalBoxCloneService<T, U, E> {
33    type Response = U;
34    type Error = E;
35    type Future = LocalBoxFuture<Result<U, E>>;
36
37    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), E>> {
38        self.0.poll_ready(cx)
39    }
40
41    fn call(&mut self, request: T) -> Self::Future {
42        self.0.call(request)
43    }
44}
45
46impl<T, U, E> Clone for LocalBoxCloneService<T, U, E> {
47    fn clone(&self) -> Self {
48        Self(self.0.clone_box())
49    }
50}
51
52impl<T, U, E> fmt::Debug for LocalBoxCloneService<T, U, E> {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        f.debug_struct("LocalBoxCloneService")
55            .finish_non_exhaustive()
56    }
57}
58
59trait CloneService<R>: Service<R> {
60    #[allow(clippy::type_complexity)]
61    fn clone_box(
62        &self,
63    ) -> Box<
64        dyn CloneService<R, Response = Self::Response, Error = Self::Error, Future = Self::Future>,
65    >;
66}
67
68impl<R, S> CloneService<R> for S
69where
70    S: Service<R> + Clone + 'static,
71{
72    fn clone_box(
73        &self,
74    ) -> Box<dyn CloneService<R, Response = S::Response, Error = S::Error, Future = S::Future>>
75    {
76        Box::new(self.clone())
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use std::rc::Rc;
83
84    use super::*;
85
86    #[tokio::test]
87    async fn holds_and_clones_a_service_that_is_not_send() {
88        let shared = Rc::new(2);
89        let service = tower::service_fn(move |n: i32| {
90            let shared = shared.clone();
91            async move { Ok::<_, std::convert::Infallible>(n * *shared) }
92        });
93        let boxed = LocalBoxCloneService::new(service);
94        let copy = boxed.clone();
95        assert_eq!(boxed.oneshot(3).await.unwrap(), 6);
96        assert_eq!(copy.oneshot(4).await.unwrap(), 8);
97    }
98}