Skip to content

Commit da24532

Browse files
authored
Add util::BoxCloneSyncService (#777)
Closes #770
1 parent 6283f3a commit da24532

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

tower/src/util/boxed_clone_sync.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use super::ServiceExt;
2+
use futures_util::future::BoxFuture;
3+
use std::{
4+
fmt,
5+
task::{Context, Poll},
6+
};
7+
use tower_layer::{layer_fn, LayerFn};
8+
use tower_service::Service;
9+
10+
/// A [`Clone`] + [`Send`] + [`Sync`] boxed [`Service`].
11+
///
12+
/// [`BoxCloneSyncService`] turns a service into a trait object, allowing the
13+
/// response future type to be dynamic, and allowing the service to be cloned and shared.
14+
///
15+
/// This is similar to [`BoxCloneService`](super::BoxCloneService) except the resulting
16+
/// service implements [`Sync`].
17+
/// ```
18+
pub struct BoxCloneSyncService<T, U, E>(
19+
Box<
20+
dyn CloneService<T, Response = U, Error = E, Future = BoxFuture<'static, Result<U, E>>>
21+
+ Send
22+
+ Sync,
23+
>,
24+
);
25+
26+
impl<T, U, E> BoxCloneSyncService<T, U, E> {
27+
/// Create a new `BoxCloneService`.
28+
pub fn new<S>(inner: S) -> Self
29+
where
30+
S: Service<T, Response = U, Error = E> + Clone + Send + Sync + 'static,
31+
S::Future: Send + 'static,
32+
{
33+
let inner = inner.map_future(|f| Box::pin(f) as _);
34+
BoxCloneSyncService(Box::new(inner))
35+
}
36+
37+
/// Returns a [`Layer`] for wrapping a [`Service`] in a [`BoxCloneSyncService`]
38+
/// middleware.
39+
///
40+
/// [`Layer`]: crate::Layer
41+
pub fn layer<S>() -> LayerFn<fn(S) -> Self>
42+
where
43+
S: Service<T, Response = U, Error = E> + Clone + Send + Sync + 'static,
44+
S::Future: Send + 'static,
45+
{
46+
layer_fn(Self::new)
47+
}
48+
}
49+
50+
impl<T, U, E> Service<T> for BoxCloneSyncService<T, U, E> {
51+
type Response = U;
52+
type Error = E;
53+
type Future = BoxFuture<'static, Result<U, E>>;
54+
55+
#[inline]
56+
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), E>> {
57+
self.0.poll_ready(cx)
58+
}
59+
60+
#[inline]
61+
fn call(&mut self, request: T) -> Self::Future {
62+
self.0.call(request)
63+
}
64+
}
65+
66+
impl<T, U, E> Clone for BoxCloneSyncService<T, U, E> {
67+
fn clone(&self) -> Self {
68+
Self(self.0.clone_box())
69+
}
70+
}
71+
72+
trait CloneService<R>: Service<R> {
73+
fn clone_box(
74+
&self,
75+
) -> Box<
76+
dyn CloneService<R, Response = Self::Response, Error = Self::Error, Future = Self::Future>
77+
+ Send
78+
+ Sync,
79+
>;
80+
}
81+
82+
impl<R, T> CloneService<R> for T
83+
where
84+
T: Service<R> + Send + Sync + Clone + 'static,
85+
{
86+
fn clone_box(
87+
&self,
88+
) -> Box<
89+
dyn CloneService<R, Response = T::Response, Error = T::Error, Future = T::Future>
90+
+ Send
91+
+ Sync,
92+
> {
93+
Box::new(self.clone())
94+
}
95+
}
96+
97+
impl<T, U, E> fmt::Debug for BoxCloneSyncService<T, U, E> {
98+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
99+
fmt.debug_struct("BoxCloneSyncService").finish()
100+
}
101+
}

tower/src/util/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod and_then;
44
mod boxed;
55
mod boxed_clone;
6+
mod boxed_clone_sync;
67
mod call_all;
78
mod either;
89

@@ -25,6 +26,7 @@ pub use self::{
2526
and_then::{AndThen, AndThenLayer},
2627
boxed::{BoxCloneServiceLayer, BoxLayer, BoxService, UnsyncBoxService},
2728
boxed_clone::BoxCloneService,
29+
boxed_clone_sync::BoxCloneSyncService,
2830
either::Either,
2931
future_service::{future_service, FutureService},
3032
map_err::{MapErr, MapErrLayer},

0 commit comments

Comments
 (0)