Add Actor interface
This is the early idea. The error message is still not great if the HandledMessages don't fit the Actor. (Aka forgot to impl Handle> Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
bf3e65a6e2
commit
d38b04396c
3 changed files with 162 additions and 0 deletions
139
crates/tytix-core/src/actor.rs
Normal file
139
crates/tytix-core/src/actor.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
use std::any::Any;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
|
||||
use crate::InternalMessage;
|
||||
use crate::IsContainedInBundle;
|
||||
use crate::Message;
|
||||
use crate::MessageBundle;
|
||||
|
||||
pub trait Actor: Any {
|
||||
type HandledMessages: MessageBundle;
|
||||
}
|
||||
|
||||
pub trait Handle<M: Message> {
|
||||
fn handle(&mut self, message: M) -> impl Future<Output = anyhow::Result<M::Reply>>;
|
||||
}
|
||||
|
||||
pub trait ActorHandler<MB>: Any {
|
||||
fn handle_message(
|
||||
&mut self,
|
||||
message: InternalMessage,
|
||||
) -> impl Future<Output = anyhow::Result<InternalMessage>>;
|
||||
}
|
||||
|
||||
macro_rules! impl_actor_handle {
|
||||
( $($ty:ident),* ) => {
|
||||
impl<ACTOR, $($ty,)*> ActorHandler<($($ty,)*)> for ACTOR
|
||||
where
|
||||
ACTOR: Actor<HandledMessages = ($($ty,)*)>,
|
||||
$( ACTOR: Handle<$ty>, )*
|
||||
$( $ty: Message, )*
|
||||
{
|
||||
fn handle_message(
|
||||
&mut self,
|
||||
msg: InternalMessage,
|
||||
) -> impl Future<Output = anyhow::Result<InternalMessage>> {
|
||||
|
||||
#[allow(unused_variables)]
|
||||
async {
|
||||
$(
|
||||
let msg = match msg.into_inner::<$ty>() {
|
||||
Ok(msg) => {
|
||||
return self.handle(msg).await.map(InternalMessage::new);
|
||||
}
|
||||
Err(msg) => msg,
|
||||
};
|
||||
)*
|
||||
|
||||
Err(anyhow::anyhow!("Could not handle message"))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
all_the_tuples!(impl_actor_handle);
|
||||
|
||||
pub struct ActorHandle<MB> {
|
||||
actor: Box<dyn Any>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
handle: for<'a> fn(
|
||||
&'a mut dyn Any,
|
||||
msg: InternalMessage,
|
||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<InternalMessage>> + 'a>>,
|
||||
_pd: PhantomData<fn(MB)>,
|
||||
}
|
||||
|
||||
impl<MB: MessageBundle + 'static> ActorHandle<MB> {
|
||||
pub fn new<A: ActorHandler<MB>>(actor: A) -> ActorHandle<MB> {
|
||||
ActorHandle {
|
||||
actor: Box::new(actor),
|
||||
handle: |actor, msg| {
|
||||
let actor: &mut A = actor.downcast_mut().unwrap();
|
||||
|
||||
Box::pin(actor.handle_message(msg))
|
||||
},
|
||||
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle<M: Message + IsContainedInBundle<MB>>(
|
||||
&mut self,
|
||||
message: M,
|
||||
) -> anyhow::Result<M::Reply> {
|
||||
const {
|
||||
let true = <M as IsContainedInBundle<MB>>::IS_CONTAINED else {
|
||||
panic!("Message is not contained in MessageBundle",);
|
||||
};
|
||||
}
|
||||
|
||||
(self.handle)(self.actor.as_mut(), InternalMessage::new(message))
|
||||
.await
|
||||
.map(|msg| match msg.into_inner::<M::Reply>() {
|
||||
Ok(t) => t,
|
||||
Err(msg) => panic!("Could not process reply of type: {}", msg.type_name()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use macro_rules_attribute::apply;
|
||||
use smol_macros::test;
|
||||
|
||||
use super::*;
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Message for Foo {
|
||||
type Reply = ();
|
||||
}
|
||||
|
||||
struct Zap;
|
||||
|
||||
impl Message for Zap {
|
||||
type Reply = ();
|
||||
}
|
||||
|
||||
struct FActor;
|
||||
|
||||
impl Actor for FActor {
|
||||
type HandledMessages = (Foo,);
|
||||
}
|
||||
|
||||
impl Handle<Foo> for FActor {
|
||||
async fn handle(&mut self, _message: Foo) -> anyhow::Result<<Foo as Message>::Reply> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[apply(test!)]
|
||||
async fn test_name() {
|
||||
let mut actor = ActorHandle::new(FActor);
|
||||
|
||||
actor.handle(Zap).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
mod macros;
|
||||
pub mod address;
|
||||
pub mod message;
|
||||
pub mod actor;
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,25 @@
|
|||
#[rustfmt::skip]
|
||||
macro_rules! all_the_tuples {
|
||||
($name:ident) => {
|
||||
$name!(M1);
|
||||
$name!(M1, M2);
|
||||
$name!(M1, M2, M3);
|
||||
$name!(M1, M2, M3, M4);
|
||||
$name!(M1, M2, M3, M4, M5);
|
||||
$name!(M1, M2, M3, M4, M5, M6);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8, M9);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8, M9, M10);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15);
|
||||
$name!(M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15, M16);
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
macro_rules! all_the_tuples_special_first {
|
||||
($name:ident) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue