use async_trait::async_trait;
use std::fmt;
use tokio::sync::oneshot;
use crate::{Actor, Context};
pub trait Message: Send {
type Result: Send;
}
impl<T> Message for Option<T>
where
T: Message,
{
type Result = T::Result;
}
#[async_trait]
pub trait Handler<M: Message>
where
Self: Actor,
{
async fn handle(&mut self, msg: M, ctx: &mut Context<Self>) -> M::Result;
}
#[async_trait]
pub trait Deliver<A: Actor> {
async fn deliver(&mut self, actor: &mut A, ctx: &mut Context<A>);
}
struct Inner<M: Message> {
msg: M,
result_tx: oneshot::Sender<M::Result>,
}
struct MessageRsvp<M: Message>(Option<Inner<M>>);
#[async_trait]
impl<A, M> Deliver<A> for MessageRsvp<M>
where
A: Actor + Handler<M>,
M: Message,
{
async fn deliver(&mut self, actor: &mut A, ctx: &mut Context<A>) {
let Inner { msg, result_tx } = self.0.take().expect("Envelope can only be delivered once");
let res = <A as Handler<M>>::handle(actor, msg, ctx).await;
if result_tx.send(res).is_err() {
log::error!("cannot send result to sender shut down");
}
}
}
struct MessageOneway<M: Message>(Option<M>);
#[async_trait]
impl<A, M> Deliver<A> for MessageOneway<M>
where
A: Actor + Handler<M>,
M: Message,
{
async fn deliver(&mut self, actor: &mut A, ctx: &mut Context<A>) {
let msg = self.0.take().expect("Envelope can only be delivered once");
<A as Handler<M>>::handle(actor, msg, ctx).await;
}
}
pub enum Envelope<A: Actor> {
Message(Box<dyn Deliver<A> + Send>),
Stop(oneshot::Sender<A>),
}
impl<A: Actor> fmt::Debug for Envelope<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Envelope::*;
match self {
Message(_) => write!(f, "Envelope::Message"),
Stop(_) => write!(f, "Envelope::Stop"),
}
}
}
impl<A: Actor> Envelope<A> {
pub fn message_rsvp<M>(msg: M, result_tx: oneshot::Sender<M::Result>) -> Self
where
A: Handler<M>,
M: Message + 'static,
{
Envelope::Message(Box::new(MessageRsvp(Some(Inner { msg, result_tx }))))
}
pub fn message<M>(msg: M) -> Self
where
A: Handler<M>,
M: Message + 'static,
{
Envelope::Message(Box::new(MessageOneway(Some(msg))))
}
pub fn stop(result_tx: oneshot::Sender<A>) -> Self {
Envelope::Stop(result_tx)
}
pub async fn deliver(&mut self, actor: &mut A, ctx: &mut Context<A>) {
use Envelope::*;
match self {
Message(msg) => msg.deliver(actor, ctx).await,
Stop(_) => unreachable!("context::Worker will not call deliver on Stop"),
}
}
}