use std::{
error::Error as StdError,
fmt::{self, Display, Formatter},
task::Poll,
};
use futures_util::{Future, FutureExt};
use tokio::sync::oneshot::{self, Receiver as OneshotReceiver};
use tower::Service;
use crate::{common::transport::mock::MockSender, request::BoxRequest, response::BoxResponse};
use super::TransportError;
#[derive(Clone)]
pub struct Mock {
tx: MockSender,
}
impl Mock {
pub fn new(tx: MockSender) -> Self {
Self { tx }
}
}
impl Service<BoxRequest> for Mock {
type Response = BoxResponse;
type Error = TransportError<MockError>;
type Future = MockCallFuture;
fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> {
Ok(()).into()
}
fn call(&mut self, req: BoxRequest) -> Self::Future {
let (resp_tx, resp_rx) = oneshot::channel();
let send_res = self
.tx
.inner
.send((req, resp_tx))
.map_err(|err| MockError::Send(err.0 .0));
match send_res {
Ok(_) => MockCallFuture {
inner: MockCallFutureInner::Recv(resp_rx),
},
Err(err) => MockCallFuture {
inner: MockCallFutureInner::Err(Some(err)),
},
}
}
}
enum MockCallFutureInner {
Recv(OneshotReceiver<BoxResponse>),
Err(Option<MockError>),
}
pub struct MockCallFuture {
inner: MockCallFutureInner,
}
impl Future for MockCallFuture {
type Output = Result<BoxResponse, TransportError<MockError>>;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
match &mut self.get_mut().inner {
MockCallFutureInner::Err(err) => Poll::Ready(Err(TransportError::Transport(
err.take().expect("future polled after completion"),
))),
MockCallFutureInner::Recv(rx) => rx
.poll_unpin(cx)
.map_err(|_| MockError::Receive)
.map_err(TransportError::Transport),
}
}
}
#[derive(Debug)]
pub enum MockError {
Receive,
Send(BoxRequest),
}
impl Display for MockError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
MockError::Receive => f.write_str("failed to receive response"),
MockError::Send(_) => f.write_str("failed to send request"),
}
}
}
impl StdError for MockError {}