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;
|
mod macros;
|
||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
pub mod actor;
|
||||||
|
|
||||||
use std::any::TypeId;
|
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]
|
#[rustfmt::skip]
|
||||||
macro_rules! all_the_tuples_special_first {
|
macro_rules! all_the_tuples_special_first {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue