use std::{pin::Pin, task::Poll};
use futures::{Future, FutureExt, ready, task::Context};
use tower_service::Service;
impl<T: ?Sized, TRequest> ServiceExt<TRequest> for T where T: Service<TRequest> {}
pub trait ServiceExt<TRequest>: Service<TRequest> {
fn call_ready(&mut self, req: TRequest) -> ServiceCallReady<'_, Self, TRequest>
where Self::Future: Unpin {
ServiceCallReady::new(self, req)
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct ServiceCallReady<'a, S, TRequest>
where S: Service<TRequest> + ?Sized
{
service: &'a mut S,
request: Option<TRequest>,
pending: Option<S::Future>,
}
impl<S: ?Sized + Service<TRequest> + Unpin, TRequest> Unpin for ServiceCallReady<'_, S, TRequest> {}
impl<'a, S, TRequest> ServiceCallReady<'a, S, TRequest>
where
S: Service<TRequest> + ?Sized,
S::Future: Unpin,
{
fn new(service: &'a mut S, request: TRequest) -> Self {
Self {
service,
request: Some(request),
pending: None,
}
}
}
impl<S, TRequest> Future for ServiceCallReady<'_, S, TRequest>
where
S: Service<TRequest> + ?Sized + Unpin,
S::Future: Unpin,
{
type Output = Result<S::Response, S::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
loop {
match this.pending {
Some(ref mut fut) => return fut.poll_unpin(cx),
None => {
ready!(this.service.poll_ready(cx))?;
let req = this.request.take().expect("the request cannot be made twice");
this.pending = Some(this.service.call(req));
},
}
}
}
}
#[cfg(test)]
mod test {
use std::sync::{
Arc,
atomic::{AtomicBool, Ordering},
};
use futures::future;
use futures_test::task::panic_context;
use tower::service_fn;
use super::*;
#[test]
fn service_ready() {
let mut double_service = service_fn(|req: u32| future::ok::<_, ()>(req + req));
let mut cx = panic_context();
match ServiceCallReady::new(&mut double_service, 157).poll_unpin(&mut cx) {
Poll::Ready(Ok(v)) => assert_eq!(v, 314),
_ => panic!("Expected future to be ready"),
}
}
#[test]
fn service_ready_later() {
struct ReadyLater {
call_count: u32,
flag: Arc<AtomicBool>,
}
impl Service<u32> for ReadyLater {
type Error = ();
type Future = future::Ready<Result<Self::Response, Self::Error>>;
type Response = u32;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.flag.load(Ordering::SeqCst) {
Ok(()).into()
} else {
Poll::Pending
}
}
fn call(&mut self, req: u32) -> Self::Future {
self.call_count += 1;
future::ok(req + req)
}
}
let mut cx = panic_context();
let ready_flag = Arc::new(AtomicBool::new(false));
let mut service = ReadyLater {
flag: ready_flag.clone(),
call_count: 0,
};
let mut fut = ServiceCallReady::new(&mut service, 157);
match fut.poll_unpin(&mut cx) {
Poll::Pending => {},
_ => panic!("Expected future to be pending"),
}
ready_flag.store(true, Ordering::SeqCst);
match fut.poll_unpin(&mut cx) {
Poll::Ready(Ok(v)) => assert_eq!(v, 314),
_ => panic!("Expected future to be ready"),
}
assert_eq!(service.call_count, 1);
}
}