util: Add ReadyAnd to do what Ready should do (#427)
* util: Add ReadyAnd to do what Ready should do `ServiceExt::ready` says that it produces "A future yielding the service when it is ready to accept a request." This is not true; it does _not_ yield the service when it is ready, it yields unit. This makes it impossible to chain service ready with service call, which is sad. This PR adds `ready_and`, which does what `ready` promised. It also deprecates `ready` with the intention that we remove `ready` in a future version, and make the strictly more general `ready_and` take its place. We can't do it now since it's not a backwards-compatible change even though it _probably_ wouldn't break any code. The PR also updates the docs so that they reflect the observed behavior.
This commit is contained in:
parent
b6f5f586c5
commit
52fde9767c
|
@ -23,7 +23,7 @@ pub use crate::{
|
||||||
either::Either,
|
either::Either,
|
||||||
oneshot::Oneshot,
|
oneshot::Oneshot,
|
||||||
optional::Optional,
|
optional::Optional,
|
||||||
ready::Ready,
|
ready::{Ready, ReadyAnd, ReadyOneshot},
|
||||||
service_fn::{service_fn, ServiceFn},
|
service_fn::{service_fn, ServiceFn},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,14 +48,32 @@ pub mod future {
|
||||||
/// An extension trait for `Service`s that provides a variety of convenient
|
/// An extension trait for `Service`s that provides a variety of convenient
|
||||||
/// adapters
|
/// adapters
|
||||||
pub trait ServiceExt<Request>: tower_service::Service<Request> {
|
pub trait ServiceExt<Request>: tower_service::Service<Request> {
|
||||||
/// A future yielding the service when it is ready to accept a request.
|
/// Resolves when the service is ready to accept a request.
|
||||||
|
#[deprecated(since = "0.3.1", note = "prefer `ready_and` which yields the service")]
|
||||||
fn ready(&mut self) -> Ready<'_, Self, Request>
|
fn ready(&mut self) -> Ready<'_, Self, Request>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
|
#[allow(deprecated)]
|
||||||
Ready::new(self)
|
Ready::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Yields a mutable reference to the service when it is ready to accept a request.
|
||||||
|
fn ready_and(&mut self) -> ReadyAnd<'_, Self, Request>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
ReadyAnd::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Yields the service when it is ready to accept a request.
|
||||||
|
fn ready_oneshot(self) -> ReadyOneshot<Self, Request>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
ReadyOneshot::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
/// Consume this `Service`, calling with the providing request once it is ready.
|
/// Consume this `Service`, calling with the providing request once it is ready.
|
||||||
fn oneshot(self, req: Request) -> Oneshot<Self, Request>
|
fn oneshot(self, req: Request) -> Oneshot<Self, Request>
|
||||||
where
|
where
|
||||||
|
|
|
@ -8,15 +8,104 @@ use std::{
|
||||||
};
|
};
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
|
||||||
/// Future yielding a `Service` once the service is ready to process a request
|
/// A future that yields the service when it is ready to accept a request.
|
||||||
///
|
///
|
||||||
/// `Ready` values are produced by `ServiceExt::ready`.
|
/// `ReadyOneshot` values are produced by `ServiceExt::ready_oneshot`.
|
||||||
pub struct Ready<'a, T, Request> {
|
pub struct ReadyOneshot<T, Request> {
|
||||||
inner: &'a mut T,
|
inner: Option<T>,
|
||||||
_p: PhantomData<fn() -> Request>,
|
_p: PhantomData<fn() -> Request>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: This is safe because `Services`'s are always `Unpin`.
|
// Safety: This is safe because `Services`'s are always `Unpin`.
|
||||||
|
impl<T, Request> Unpin for ReadyOneshot<T, Request> {}
|
||||||
|
|
||||||
|
impl<T, Request> ReadyOneshot<T, Request>
|
||||||
|
where
|
||||||
|
T: Service<Request>,
|
||||||
|
{
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub fn new(service: T) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Some(service),
|
||||||
|
_p: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Request> Future for ReadyOneshot<T, Request>
|
||||||
|
where
|
||||||
|
T: Service<Request>,
|
||||||
|
{
|
||||||
|
type Output = Result<T, T::Error>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
ready!(self
|
||||||
|
.inner
|
||||||
|
.as_mut()
|
||||||
|
.expect("poll after Poll::Ready")
|
||||||
|
.poll_ready(cx))?;
|
||||||
|
|
||||||
|
Poll::Ready(Ok(self.inner.take().expect("poll after Poll::Ready")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Request> fmt::Debug for ReadyOneshot<T, Request>
|
||||||
|
where
|
||||||
|
T: fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.debug_struct("ReadyOneshot")
|
||||||
|
.field("inner", &self.inner)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A future that yields a mutable reference to the service when it is ready to accept a request.
|
||||||
|
///
|
||||||
|
/// `ReadyAnd` values are produced by `ServiceExt::ready_and`.
|
||||||
|
pub struct ReadyAnd<'a, T, Request>(ReadyOneshot<&'a mut T, Request>);
|
||||||
|
|
||||||
|
// Safety: This is safe for the same reason that the impl for ReadyOneshot is safe.
|
||||||
|
impl<'a, T, Request> Unpin for ReadyAnd<'a, T, Request> {}
|
||||||
|
|
||||||
|
impl<'a, T, Request> ReadyAnd<'a, T, Request>
|
||||||
|
where
|
||||||
|
T: Service<Request>,
|
||||||
|
{
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub fn new(service: &'a mut T) -> Self {
|
||||||
|
Self(ReadyOneshot::new(service))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T, Request> Future for ReadyAnd<'a, T, Request>
|
||||||
|
where
|
||||||
|
T: Service<Request>,
|
||||||
|
{
|
||||||
|
type Output = Result<&'a mut T, T::Error>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
Pin::new(&mut self.0).poll(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T, Request> fmt::Debug for ReadyAnd<'a, T, Request>
|
||||||
|
where
|
||||||
|
T: fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.debug_tuple("ReadyAnd").field(&self.0).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== deprecated `Ready` that is just `ReadyAnd` with a unit return type.
|
||||||
|
|
||||||
|
/// A future that resolves when a `Service` is ready to accept a request.
|
||||||
|
///
|
||||||
|
/// `Ready` values are produced by `ServiceExt::ready`.
|
||||||
|
pub struct Ready<'a, T, Request>(ReadyAnd<'a, T, Request>);
|
||||||
|
|
||||||
|
// Safety: This is safe for the same reason that the impl for ReadyOneshot is safe.
|
||||||
impl<'a, T, Request> Unpin for Ready<'a, T, Request> {}
|
impl<'a, T, Request> Unpin for Ready<'a, T, Request> {}
|
||||||
|
|
||||||
impl<'a, T, Request> Ready<'a, T, Request>
|
impl<'a, T, Request> Ready<'a, T, Request>
|
||||||
|
@ -24,11 +113,9 @@ where
|
||||||
T: Service<Request>,
|
T: Service<Request>,
|
||||||
{
|
{
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
#[deprecated(since = "0.3.1", note = "prefer `ReadyAnd` which yields the service")]
|
||||||
pub fn new(service: &'a mut T) -> Self {
|
pub fn new(service: &'a mut T) -> Self {
|
||||||
Ready {
|
Self(ReadyAnd::new(service))
|
||||||
inner: service,
|
|
||||||
_p: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,8 +126,7 @@ where
|
||||||
type Output = Result<(), T::Error>;
|
type Output = Result<(), T::Error>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
ready!(self.inner.poll_ready(cx))?;
|
let _ = ready!(Pin::new(&mut self.0).poll(cx));
|
||||||
|
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +136,6 @@ where
|
||||||
T: fmt::Debug,
|
T: fmt::Debug,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
f.debug_struct("Ready").field("inner", &self.inner).finish()
|
f.debug_tuple("Ready").field(&self.0).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,7 @@ async fn builder_service() {
|
||||||
// allow a request through
|
// allow a request through
|
||||||
handle.allow(1);
|
handle.allow(1);
|
||||||
|
|
||||||
client.ready().await.unwrap();
|
let fut = client.ready_and().await.unwrap().call("hello");
|
||||||
let fut = client.call("hello");
|
|
||||||
assert_request_eq!(handle, "hello").send_response("world");
|
assert_request_eq!(handle, "hello").send_response("world");
|
||||||
assert_eq!(fut.await.unwrap(), "world");
|
assert_eq!(fut.await.unwrap(), "world");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue