make all combinator futures opaque (#509)

* make all combinator futures opaque

For stability reasons, we probably don't want to expose future
combinator types from the `futures_util` crate in public APIs. If we
were to change which combinators are used to implement these futures, or
if `futures_util` made breaking changes, this could cause API breakage.

This branch wraps all publicly exposed `futures_util` types in the
`opaque_future!` macro added in #508, to wrap them in newtypes that hide
their internal details. This way, we can change these futures' internals
whenever we like.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
Eliza Weisman 2021-01-06 15:06:31 -08:00 committed by GitHub
parent 2a7d47adda
commit 3b7c91ed58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 25 deletions

View File

@ -33,7 +33,6 @@ pub use self::{
use self::future::{AsyncResponseFuture, ResponseFuture};
use crate::BoxError;
use futures_core::ready;
use futures_util::{future::Either, TryFutureExt};
use std::task::{Context, Poll};
use tower_service::Service;

View File

@ -20,6 +20,13 @@ pub struct BackgroundReady<T, Request> {
_req: PhantomData<Request>,
}
opaque_future! {
/// Response future from [`SpawnReady`] services.
///
/// [`SpawnReady`]: crate::spawn_ready::SpawnReady
pub type ResponseFuture<F, E> = futures_util::future::MapErr<F, fn(E) -> crate::BoxError>;
}
pub(crate) fn background_ready<T, Request>(
service: T,
) -> (

View File

@ -1,6 +1,6 @@
use super::future::background_ready;
use super::future::{background_ready, ResponseFuture};
use futures_core::ready;
use futures_util::future::{MapErr, TryFutureExt};
use futures_util::future::TryFutureExt;
use std::{
future::Future,
pin::Pin,
@ -40,7 +40,7 @@ where
{
type Response = T::Response;
type Error = crate::BoxError;
type Future = MapErr<T::Future, fn(T::Error) -> crate::BoxError>;
type Future = ResponseFuture<T::Future, T::Error>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
loop {
@ -66,7 +66,9 @@ where
fn call(&mut self, request: Request) -> Self::Future {
match self.inner {
Inner::Service(Some(ref mut svc)) => svc.call(request).map_err(Into::into),
Inner::Service(Some(ref mut svc)) => {
ResponseFuture(svc.call(request).map_err(Into::into))
}
_ => unreachable!("poll_ready must be called"),
}
}

View File

@ -1,11 +1,8 @@
use futures_util::TryFutureExt;
use futures_util::{future, TryFutureExt};
use std::task::{Context, Poll};
use tower_layer::Layer;
use tower_service::Service;
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
pub use futures_util::future::MapErr as MapErrFuture;
/// Service returned by the [`map_err`] combinator.
///
/// [`map_err`]: crate::util::ServiceExt::map_err
@ -23,6 +20,13 @@ pub struct MapErrLayer<F> {
f: F,
}
opaque_future! {
/// Response future from [`MapErr`] services.
///
/// [`MapErr`]: crate::util::MapErr
pub type MapErrFuture<F, N> = future::MapErr<F, N>;
}
impl<S, F> MapErr<S, F> {
/// Creates a new [`MapErr`] service.
pub fn new(inner: S, f: F) -> Self {
@ -46,7 +50,7 @@ where
#[inline]
fn call(&mut self, request: Request) -> Self::Future {
self.inner.call(request).map_err(self.f.clone())
MapErrFuture(self.inner.call(request).map_err(self.f.clone()))
}
}

View File

@ -1,11 +1,8 @@
use futures_util::TryFutureExt;
use futures_util::{future::MapOk, TryFutureExt};
use std::task::{Context, Poll};
use tower_layer::Layer;
use tower_service::Service;
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
pub use futures_util::future::MapOk as MapResponseFuture;
/// Service returned by the [`map_response`] combinator.
///
/// [`map_response`]: crate::util::ServiceExt::map_response
@ -23,6 +20,13 @@ pub struct MapResponseLayer<F> {
f: F,
}
opaque_future! {
/// Response future from [`MapResponse`] services.
///
/// [`MapResponse`]: crate::util::MapResponse
pub type MapResponseFuture<F, N> = MapOk<F, N>;
}
impl<S, F> MapResponse<S, F> {
/// Creates a new `MapResponse` service.
pub fn new(inner: S, f: F) -> Self {
@ -46,7 +50,7 @@ where
#[inline]
fn call(&mut self, request: Request) -> Self::Future {
self.inner.call(request).map_ok(self.f.clone())
MapResponseFuture(self.inner.call(request).map_ok(self.f.clone()))
}
}

View File

@ -1,11 +1,8 @@
use futures_util::FutureExt;
use futures_util::{future::Map, FutureExt};
use std::task::{Context, Poll};
use tower_layer::Layer;
use tower_service::Service;
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
pub use futures_util::future::Map as MapResultFuture;
/// Service returned by the [`map_result`] combinator.
///
/// [`map_result`]: crate::util::ServiceExt::map_result
@ -23,6 +20,13 @@ pub struct MapResultLayer<F> {
f: F,
}
opaque_future! {
/// Response future from [`MapResult`] services.
///
/// [`MapResult`]: crate::util::MapResult
pub type MapResultFuture<F, N> = Map<F, N>;
}
impl<S, F> MapResult<S, F> {
/// Creates a new `MapResult` service.
pub fn new(inner: S, f: F) -> Self {
@ -47,7 +51,7 @@ where
#[inline]
fn call(&mut self, request: Request) -> Self::Future {
self.inner.call(request).map(self.f.clone())
MapResultFuture(self.inner.call(request).map(self.f.clone()))
}
}

View File

@ -1,4 +1,4 @@
use futures_util::FutureExt;
use futures_util::{future, FutureExt};
use std::{
future::Future,
task::{Context, Poll},
@ -6,9 +6,6 @@ use std::{
use tower_layer::Layer;
use tower_service::Service;
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
pub use futures_util::future::Then as ThenFuture;
/// Service returned by the [`then`] combinator.
///
/// [`then`]: crate::util::ServiceExt::then
@ -33,6 +30,13 @@ impl<S, F> Then<S, F> {
}
}
opaque_future! {
/// Response future from [`Then`] services.
///
/// [`Then`]: crate::util::Then
pub type ThenFuture<F1, F2, N> = future::Then<F1, F2, N>;
}
impl<S, F, Request, Response, Error, Fut> Service<Request> for Then<S, F>
where
S: Service<Request>,
@ -51,7 +55,7 @@ where
#[inline]
fn call(&mut self, request: Request) -> Self::Future {
self.inner.call(request).then(self.f.clone())
ThenFuture(self.inner.call(request).then(self.f.clone()))
}
}