diff --git a/crates/tytix-core/src/actor.rs b/crates/tytix-core/src/actor.rs new file mode 100644 index 0000000..ddf679b --- /dev/null +++ b/crates/tytix-core/src/actor.rs @@ -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 { + fn handle(&mut self, message: M) -> impl Future>; +} + +pub trait ActorHandler: Any { + fn handle_message( + &mut self, + message: InternalMessage, + ) -> impl Future>; +} + +macro_rules! impl_actor_handle { + ( $($ty:ident),* ) => { + impl ActorHandler<($($ty,)*)> for ACTOR + where + ACTOR: Actor, + $( ACTOR: Handle<$ty>, )* + $( $ty: Message, )* + { + fn handle_message( + &mut self, + msg: InternalMessage, + ) -> impl Future> { + + #[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 { + actor: Box, + #[allow(clippy::type_complexity)] + handle: for<'a> fn( + &'a mut dyn Any, + msg: InternalMessage, + ) -> Pin> + 'a>>, + _pd: PhantomData, +} + +impl ActorHandle { + pub fn new>(actor: A) -> ActorHandle { + 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>( + &mut self, + message: M, + ) -> anyhow::Result { + const { + let true = >::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::() { + 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 for FActor { + async fn handle(&mut self, _message: Foo) -> anyhow::Result<::Reply> { + todo!() + } + } + + #[apply(test!)] + async fn test_name() { + let mut actor = ActorHandle::new(FActor); + + actor.handle(Zap).await.unwrap(); + } +} diff --git a/crates/tytix-core/src/lib.rs b/crates/tytix-core/src/lib.rs index 213fe7d..3a8bd5f 100644 --- a/crates/tytix-core/src/lib.rs +++ b/crates/tytix-core/src/lib.rs @@ -5,6 +5,7 @@ mod macros; pub mod address; pub mod message; +pub mod actor; use std::any::TypeId; diff --git a/crates/tytix-core/src/macros.rs b/crates/tytix-core/src/macros.rs index bcb7bc2..914ce3d 100644 --- a/crates/tytix-core/src/macros.rs +++ b/crates/tytix-core/src/macros.rs @@ -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) => {