Skip to content

Commit

Permalink
Implement UnsyncBoxCloneService
Browse files Browse the repository at this point in the history
  • Loading branch information
inq committed Sep 4, 2023
1 parent bf4ea94 commit 6405287
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 3 deletions.
7 changes: 7 additions & 0 deletions tower/src/util/boxed_clone/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod sync;
mod unsync;

#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
pub use self::{
sync::BoxCloneService, unsync::UnsyncBoxCloneService,
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::ServiceExt;
use crate::ServiceExt;
use futures_util::future::BoxFuture;
use std::{
fmt,
Expand Down
90 changes: 90 additions & 0 deletions tower/src/util/boxed_clone/unsync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use crate::ServiceExt;
use futures_util::future::LocalBoxFuture;
use std::{
fmt,
task::{Context, Poll},
};
use tower_layer::{layer_fn, LayerFn};
use tower_service::Service;

/// A boxed [`CloneService`] trait object.
///
/// This type alias represents a boxed future that is *not* [`Send`] and must
/// remain on the current thread.
pub struct UnsyncBoxCloneService<T, U, E>(
Box<
dyn UnsyncCloneService<T, Response = U, Error = E, Future = LocalBoxFuture<'static, Result<U, E>>>
>,
);

impl<T, U, E> UnsyncBoxCloneService<T, U, E> {
/// Create a new `BoxCloneService`.
pub fn new<S>(inner: S) -> Self
where
S: Service<T, Response = U, Error = E> + Clone + 'static,
S::Future: 'static,
{
let inner = inner.map_future(|f| Box::pin(f) as _);
UnsyncBoxCloneService(Box::new(inner))
}

/// Returns a [`Layer`] for wrapping a [`Service`] in a [`BoxCloneService`]
/// middleware.
///
/// [`Layer`]: crate::Layer
pub fn layer<S>() -> LayerFn<fn(S) -> Self>
where
S: Service<T, Response = U, Error = E> + Clone + 'static,
S::Future: 'static,
{
layer_fn(Self::new)
}
}

impl<T, U, E> Service<T> for UnsyncBoxCloneService<T, U, E> {
type Response = U;
type Error = E;
type Future = LocalBoxFuture<'static, Result<U, E>>;

#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), E>> {
self.0.poll_ready(cx)
}

#[inline]
fn call(&mut self, request: T) -> Self::Future {
self.0.call(request)
}
}

impl<T, U, E> Clone for UnsyncBoxCloneService<T, U, E> {
fn clone(&self) -> Self {
Self(self.0.clone_box())
}
}

trait UnsyncCloneService<R>: Service<R> {
fn clone_box(
&self,
) -> Box<
dyn UnsyncCloneService<R, Response = Self::Response, Error = Self::Error, Future = Self::Future>,
>;
}

impl<R, T> UnsyncCloneService<R> for T
where
T: Service<R> + Clone + 'static,
{
fn clone_box(
&self,
) -> Box<dyn UnsyncCloneService<R, Response = T::Response, Error = T::Error, Future = T::Future>>
{
Box::new(self.clone())
}
}

impl<T, U, E> fmt::Debug for UnsyncBoxCloneService<T, U, E> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BoxCloneService").finish()
}
}
2 changes: 1 addition & 1 deletion tower/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub mod rng;
pub use self::{
and_then::{AndThen, AndThenLayer},
boxed::{BoxCloneServiceLayer, BoxLayer, BoxService, UnsyncBoxService},
boxed_clone::BoxCloneService,
boxed_clone::{BoxCloneService, UnsyncBoxCloneService},
either::Either,
future_service::{future_service, FutureService},
map_err::{MapErr, MapErrLayer},
Expand Down
14 changes: 13 additions & 1 deletion tower/tests/util/service_fn.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use futures_util::future::ready;
use tower::util::service_fn;
use tower::util::{service_fn, UnsyncBoxCloneService};
use tower_service::Service;

#[tokio::test(flavor = "current_thread")]
Expand All @@ -10,3 +10,15 @@ async fn simple() {
let answer = add_one.call(1).await.unwrap();
assert_eq!(answer, 2);
}

#[tokio::test(flavor = "current_thread")]
async fn boxed_clone() {
let _t = super::support::trace_init();
let x = std::rc::Rc::new(1);
let mut add_one = service_fn(|req| ready(Ok::<_, ()>(req + 1)));
let mut cloned = UnsyncBoxCloneService::new(add_one);
let answer = cloned.call(1).await.unwrap();
assert_eq!(answer, 2);
let answer = add_one.call(1).await.unwrap();
assert_eq!(answer, 2);
}

0 comments on commit 6405287

Please sign in to comment.