-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
util: Add and_then combinator (#485)
## Motivation https://docs.rs/futures/0.3.8/futures/future/trait.TryFutureExt.html#method.and_then is a useful method on futures. Perhaps it'd be nice to replicate this for the `ServiceExt` API. Co-authored-by: Harry Barber <harry.barber@disneystreaming.com> Co-authored-by: Eliza Weisman <eliza@buoyant.io>
- Loading branch information
1 parent
3b7c91e
commit f171390
Showing
2 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
use std::future::Future; | ||
use std::pin::Pin; | ||
use std::task::{Context, Poll}; | ||
|
||
use futures_core::TryFuture; | ||
use futures_util::{future, TryFutureExt}; | ||
use tower_layer::Layer; | ||
use tower_service::Service; | ||
|
||
/// Service returned by the [`and_then`] combinator. | ||
/// | ||
/// [`and_then`]: crate::util::ServiceExt::and_then | ||
#[derive(Clone, Debug)] | ||
pub struct AndThen<S, F> { | ||
inner: S, | ||
f: F, | ||
} | ||
|
||
/// Response future from [`AndThen`] services. | ||
/// | ||
/// [`AndThen`]: crate::util::AndThen | ||
#[pin_project::pin_project] | ||
pub struct AndThenFuture<F1, F2: TryFuture, N>( | ||
#[pin] pub(crate) future::AndThen<future::ErrInto<F1, F2::Error>, F2, N>, | ||
); | ||
|
||
impl<F1, F2: TryFuture, N> std::fmt::Debug for AndThenFuture<F1, F2, N> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.debug_tuple("AndThenFuture") | ||
.field(&format_args!("...")) | ||
.finish() | ||
} | ||
} | ||
|
||
impl<F1, F2: TryFuture, N> Future for AndThenFuture<F1, F2, N> | ||
where | ||
future::AndThen<future::ErrInto<F1, F2::Error>, F2, N>: Future, | ||
{ | ||
type Output = <future::AndThen<future::ErrInto<F1, F2::Error>, F2, N> as Future>::Output; | ||
|
||
#[inline] | ||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
self.project().0.poll(cx) | ||
} | ||
} | ||
|
||
/// A [`Layer`] that produces a [`AndThen`] service. | ||
/// | ||
/// [`Layer`]: tower_layer::Layer | ||
#[derive(Debug)] | ||
pub struct AndThenLayer<F> { | ||
f: F, | ||
} | ||
|
||
impl<S, F> AndThen<S, F> { | ||
/// Creates a new `AndThen` service. | ||
pub fn new(inner: S, f: F) -> Self { | ||
AndThen { f, inner } | ||
} | ||
} | ||
|
||
impl<S, F, Request, Fut> Service<Request> for AndThen<S, F> | ||
where | ||
S: Service<Request>, | ||
S::Error: Into<Fut::Error>, | ||
F: FnOnce(S::Response) -> Fut + Clone, | ||
Fut: TryFuture, | ||
{ | ||
type Response = Fut::Ok; | ||
type Error = Fut::Error; | ||
type Future = AndThenFuture<S::Future, Fut, F>; | ||
|
||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { | ||
self.inner.poll_ready(cx).map_err(Into::into) | ||
} | ||
|
||
fn call(&mut self, request: Request) -> Self::Future { | ||
AndThenFuture(self.inner.call(request).err_into().and_then(self.f.clone())) | ||
} | ||
} | ||
|
||
impl<F> AndThenLayer<F> { | ||
/// Creates a new [`AndThenLayer`] layer. | ||
pub fn new(f: F) -> Self { | ||
AndThenLayer { f } | ||
} | ||
} | ||
|
||
impl<S, F> Layer<S> for AndThenLayer<F> | ||
where | ||
F: Clone, | ||
{ | ||
type Service = AndThen<S, F>; | ||
|
||
fn layer(&self, inner: S) -> Self::Service { | ||
AndThen { | ||
f: self.f.clone(), | ||
inner, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters