Skip to content

Commit f57e31b

Browse files
authored
Add util::BoxCloneSyncServiceLayer (#802)
cc #777
1 parent da24532 commit f57e31b

File tree

4 files changed

+136
-3
lines changed

4 files changed

+136
-3
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use std::{fmt, sync::Arc};
2+
use tower_layer::{layer_fn, Layer};
3+
use tower_service::Service;
4+
5+
use crate::util::BoxCloneSyncService;
6+
7+
/// A [`Clone`] + [`Send`] + [`Sync`] boxed [`Layer`].
8+
///
9+
/// [`BoxCloneSyncServiceLayer`] turns a layer into a trait object, allowing both the [`Layer`] itself
10+
/// and the output [`Service`] to be dynamic, while having consistent types.
11+
///
12+
/// This [`Layer`] produces [`BoxCloneSyncService`] instances erasing the type of the
13+
/// [`Service`] produced by the wrapped [`Layer`].
14+
///
15+
/// This is similar to [`BoxCloneServiceLayer`](super::BoxCloneServiceLayer) except the layer and resulting
16+
/// service implements [`Sync`].
17+
///
18+
/// # Example
19+
///
20+
/// `BoxCloneSyncServiceLayer` can, for example, be useful to create layers dynamically that otherwise wouldn't have
21+
/// the same types, when the underlying service must be clone and sync (for example, when building a Hyper connector).
22+
/// In this example, we include a [`Timeout`] layer only if an environment variable is set. We can use
23+
/// `BoxCloneSyncServiceLayer` to return a consistent type regardless of runtime configuration:
24+
///
25+
/// ```
26+
/// use std::time::Duration;
27+
/// use tower::{Service, ServiceBuilder, BoxError};
28+
/// use tower::util::{BoxCloneSyncServiceLayer, BoxCloneSyncService};
29+
///
30+
/// #
31+
/// # struct Request;
32+
/// # struct Response;
33+
/// # impl Response {
34+
/// # fn new() -> Self { Self }
35+
/// # }
36+
///
37+
/// fn common_layer<S, T>() -> BoxCloneSyncServiceLayer<S, T, S::Response, BoxError>
38+
/// where
39+
/// S: Service<T> + Clone + Send + Sync + 'static,
40+
/// S::Future: Send + 'static,
41+
/// S::Error: Into<BoxError> + 'static,
42+
/// {
43+
/// let builder = ServiceBuilder::new()
44+
/// .concurrency_limit(100);
45+
///
46+
/// if std::env::var("SET_TIMEOUT").is_ok() {
47+
/// let layer = builder
48+
/// .timeout(Duration::from_secs(30))
49+
/// .into_inner();
50+
///
51+
/// BoxCloneSyncServiceLayer::new(layer)
52+
/// } else {
53+
/// let layer = builder
54+
/// .map_err(Into::into)
55+
/// .into_inner();
56+
///
57+
/// BoxCloneSyncServiceLayer::new(layer)
58+
/// }
59+
/// }
60+
///
61+
/// // We can clone the layer (this is true of BoxLayer as well)
62+
/// let boxed_clone_sync_layer = common_layer();
63+
///
64+
/// let cloned_sync_layer = boxed_clone_sync_layer.clone();
65+
///
66+
/// // Using the `BoxCloneSyncServiceLayer` we can create a `BoxCloneSyncService`
67+
/// let service: BoxCloneSyncService<Request, Response, BoxError> = ServiceBuilder::new().layer(cloned_sync_layer)
68+
/// .service_fn(|req: Request| async {
69+
/// Ok::<_, BoxError>(Response::new())
70+
/// });
71+
///
72+
/// # let service = assert_service(service);
73+
///
74+
/// // And we can still clone the service
75+
/// let cloned_service = service.clone();
76+
/// #
77+
/// # fn assert_service<S, R>(svc: S) -> S
78+
/// # where S: Service<R> { svc }
79+
///
80+
/// ```
81+
///
82+
/// [`Layer`]: tower_layer::Layer
83+
/// [`Service`]: tower_service::Service
84+
/// [`BoxService`]: super::BoxService
85+
/// [`Timeout`]: crate::timeout
86+
pub struct BoxCloneSyncServiceLayer<In, T, U, E> {
87+
boxed: Arc<dyn Layer<In, Service = BoxCloneSyncService<T, U, E>> + Send + Sync + 'static>,
88+
}
89+
90+
impl<In, T, U, E> BoxCloneSyncServiceLayer<In, T, U, E> {
91+
/// Create a new [`BoxCloneSyncServiceLayer`].
92+
pub fn new<L>(inner_layer: L) -> Self
93+
where
94+
L: Layer<In> + Send + Sync + 'static,
95+
L::Service: Service<T, Response = U, Error = E> + Send + Sync + Clone + 'static,
96+
<L::Service as Service<T>>::Future: Send + 'static,
97+
{
98+
let layer = layer_fn(move |inner: In| {
99+
let out = inner_layer.layer(inner);
100+
BoxCloneSyncService::new(out)
101+
});
102+
103+
Self {
104+
boxed: Arc::new(layer),
105+
}
106+
}
107+
}
108+
109+
impl<In, T, U, E> Layer<In> for BoxCloneSyncServiceLayer<In, T, U, E> {
110+
type Service = BoxCloneSyncService<T, U, E>;
111+
112+
fn layer(&self, inner: In) -> Self::Service {
113+
self.boxed.layer(inner)
114+
}
115+
}
116+
117+
impl<In, T, U, E> Clone for BoxCloneSyncServiceLayer<In, T, U, E> {
118+
fn clone(&self) -> Self {
119+
Self {
120+
boxed: Arc::clone(&self.boxed),
121+
}
122+
}
123+
}
124+
125+
impl<In, T, U, E> fmt::Debug for BoxCloneSyncServiceLayer<In, T, U, E> {
126+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
127+
fmt.debug_struct("BoxCloneSyncServiceLayer").finish()
128+
}
129+
}

tower/src/util/boxed/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
mod layer;
22
mod layer_clone;
3+
mod layer_clone_sync;
34
mod sync;
45
mod unsync;
56

67
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
78
pub use self::{
8-
layer::BoxLayer, layer_clone::BoxCloneServiceLayer, sync::BoxService, unsync::UnsyncBoxService,
9+
layer::BoxLayer, layer_clone::BoxCloneServiceLayer, layer_clone_sync::BoxCloneSyncServiceLayer,
10+
sync::BoxService, unsync::UnsyncBoxService,
911
};

tower/src/util/boxed_clone_sync.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub struct BoxCloneSyncService<T, U, E>(
2424
);
2525

2626
impl<T, U, E> BoxCloneSyncService<T, U, E> {
27-
/// Create a new `BoxCloneService`.
27+
/// Create a new `BoxCloneSyncService`.
2828
pub fn new<S>(inner: S) -> Self
2929
where
3030
S: Service<T, Response = U, Error = E> + Clone + Send + Sync + 'static,

tower/src/util/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ pub mod rng;
2424

2525
pub use self::{
2626
and_then::{AndThen, AndThenLayer},
27-
boxed::{BoxCloneServiceLayer, BoxLayer, BoxService, UnsyncBoxService},
27+
boxed::{
28+
BoxCloneServiceLayer, BoxCloneSyncServiceLayer, BoxLayer, BoxService, UnsyncBoxService,
29+
},
2830
boxed_clone::BoxCloneService,
2931
boxed_clone_sync::BoxCloneSyncService,
3032
either::Either,

0 commit comments

Comments
 (0)