use std::fmt;
use std::ops;
use async_trait::async_trait;
use tokio::{
sync::{mpsc, oneshot},
task::JoinHandle,
};
use crate::error::Error;
pub type RequestResult<Rsp> = Result<Option<Rsp>, Error>;
type OptionalResponder<Rsp> = Option<oneshot::Sender<Option<Rsp>>>;
#[repr(transparent)]
#[derive(Debug)]
pub struct Requestor<Req, Rsp>(mpsc::Sender<(Req, OptionalResponder<Rsp>)>);
impl<Req, Rsp> Clone for Requestor<Req, Rsp>
where
Req: Send,
{
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<Req, Rsp> ops::Deref for Requestor<Req, Rsp> {
type Target = mpsc::Sender<(Req, OptionalResponder<Rsp>)>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<Req, Rsp> Requestor<Req, Rsp>
where
Req: Send + 'static,
Rsp: Send + 'static,
{
pub async fn request(&self, request: Req) -> RequestResult<Rsp> {
let (rsp_tx, rsp_rx) = oneshot::channel::<Option<Rsp>>();
self.0
.send((request, Some(rsp_tx)))
.await
.map_err(|_e| Error::RequestError)?;
rsp_rx.await.map_err(|_e| Error::ResponseError)
}
pub fn send_event(&self, event: Req) -> JoinHandle<Result<(), Error>> {
let sender = self.0.clone();
tokio::spawn(async move {
sender
.send((event, None))
.await
.map_err(|_e| Error::EventError)
})
}
}
pub struct Handle<T>
where
T: Actor + Send,
<T as Actor>::Request: Send,
<T as Actor>::Response: Send,
{
pub requestor: Requestor<<T as Actor>::Request, <T as Actor>::Response>,
pub handle: JoinHandle<()>,
}
impl<T> fmt::Debug for Handle<T>
where
T: Actor + Send + fmt::Debug,
<T as Actor>::Request: Send + fmt::Debug,
<T as Actor>::Response: Send + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Handle")
.field("requestor", &self.requestor)
.field("handle", &self.handle)
.finish()
}
}
impl<T> Handle<T>
where
T: Actor + Send,
<T as Actor>::Request: Send,
<T as Actor>::Response: Send,
{
pub fn abort(&self) {
self.handle.abort();
}
pub async fn join(self) {
let _ = self.handle.await;
}
}
#[async_trait]
pub trait Actor: Send + 'static {
type Request;
type Response;
async fn handle(&mut self, message: Self::Request) -> Option<Self::Response>;
}
pub fn run<T>(mut actor: T, buffer: usize) -> Handle<T>
where
T: Actor + Send,
<T as Actor>::Request: Send,
<T as Actor>::Response: Send,
{
type Request<T> = <T as Actor>::Request;
type Response<T> = <T as Actor>::Response;
type RequestMessage<T> = (Request<T>, OptionalResponder<Response<T>>);
let (tx, mut rx) = mpsc::channel::<RequestMessage<T>>(buffer);
let handle = tokio::spawn(async move {
while let Some((msg, rsp_tx)) = rx.recv().await {
let response = actor.handle(msg).await;
if let Some(rsp_tx) = rsp_tx {
let _ = rsp_tx.send(response);
}
}
});
Handle {
handle,
requestor: Requestor(tx),
}
}