#![feature(const_cmp)] #![feature(const_trait_impl)] use std::any::Any; use std::any::TypeId; pub use anyhow; pub trait Message: Send + Any { type Reply: Send + Any; } pub trait MessageIdentifier { const IDENT: BundleChain; } impl MessageIdentifier for M { const IDENT: BundleChain = BundleChain::of::(); } pub struct InternalMessage { value: Box, name: &'static str, } impl InternalMessage { pub fn new(message: M) -> InternalMessage { InternalMessage { value: Box::new(message), name: std::any::type_name::(), } } pub fn into_inner(self) -> Result { self.value .downcast() .map(|v| *v) .map_err(|value| InternalMessage { value, name: self.name, }) } pub fn as_ref(&self) -> Option<&M> { self.value.downcast_ref() } pub fn type_name(&self) -> &'static str { self.name } } pub trait Address { fn send(&mut self, message: M) -> impl Future>; } pub trait InternalMessageHandler { type HandledMessages: MessageBundle; fn handle_message( &mut self, msg: InternalMessage, ) -> impl Future>; } impl Address for IMH where MB: MessageBundle, IMH: InternalMessageHandler, { fn send(&mut self, message: M) -> impl Future> { const { let true = >::IS_CONTAINED else { panic!("Message is not contained in MessageBundle",); }; } let message = self.handle_message(InternalMessage::new(message)); async { message.await.and_then(|msg| { msg.into_inner::().map_err(|e| { anyhow::anyhow!( "Expected a {}, but got a {}", std::any::type_name::(), e.type_name() ) }) }) } } } pub trait MessageReceiver {} pub trait MessageBundle { const IDS: BundleChain; } impl MessageBundle for () { const IDS: BundleChain = BundleChain { next: None, op: BundleOp::Remove(TypeId::of::<()>()), }; } impl MessageBundle for (M,) { const IDS: BundleChain = BundleChain::of::(); } #[derive(Debug, Clone, Copy)] pub struct BundleChain { op: BundleOp, next: Option<&'static BundleChain>, } #[derive(Debug, Clone, Copy)] pub enum BundleOp { Add(TypeId), Remove(TypeId), } impl BundleChain { pub const fn of() -> BundleChain { BundleChain { op: BundleOp::Add(TypeId::of::()), next: None, } } pub const fn with(&'static self) -> BundleChain { add_to_chain(self, TypeId::of::()) } pub const fn without(&'static self) -> BundleChain { remove_from_chain(self, TypeId::of::()) } } impl MessageBundle for (A, B) where A: Message, B: Message, { const IDS: BundleChain = BundleChain::of::().with::(); } impl MessageBundle for (A, B, C, D) where A: Message, B: Message, C: Message, D: Message, { const IDS: BundleChain = BundleChain::of::().with::().with::().with::(); } pub trait IsContainedInBundle { const IS_CONTAINED: bool; } impl IsContainedInBundle for M where M: Message, MB: MessageBundle, { const IS_CONTAINED: bool = check_is_contained(&MB::IDS, TypeId::of::()); } const fn check_is_contained(ids: &'static BundleChain, id: TypeId) -> bool { match ids.op { BundleOp::Add(added_id) => { if check_type_id_equal(added_id, id) { return true; } } BundleOp::Remove(removed_id) => { if check_type_id_equal(removed_id, id) { return false; } } } if let Some(next) = ids.next { check_is_contained(next, id) } else { false } } const fn check_type_id_equal(left: TypeId, right: TypeId) -> bool { left == right } const fn add_to_chain(prev: &'static BundleChain, to_add: TypeId) -> BundleChain { BundleChain { op: BundleOp::Add(to_add), next: Some(prev), } } const fn remove_from_chain(prev: &'static BundleChain, to_remove: TypeId) -> BundleChain { BundleChain { op: BundleOp::Remove(to_remove), next: Some(prev), } } #[cfg(test)] mod tests { use macro_rules_attribute::apply; use smol_macros::test; use super::*; struct Foo; struct Bar; impl Message for Foo { type Reply = (); } impl Message for Bar { type Reply = (); } struct Zap; impl Message for Zap { type Reply = (); } #[test] fn check_checker() { assert!(check_type_id_equal( TypeId::of::(), TypeId::of::() )); const BUNDLE: BundleChain = BundleChain::of::().with::(); assert!(check_is_contained(&BUNDLE, TypeId::of::())); assert!(!check_is_contained(&BUNDLE, TypeId::of::())); const WITHOUT_BUNDLE: BundleChain = BundleChain::of::().with::().without::(); assert!(!check_is_contained(&WITHOUT_BUNDLE, TypeId::of::())); const WITHOUT_WITH_BUNDLE: BundleChain = BundleChain::of::().without::().with::(); assert!(check_is_contained( &WITHOUT_WITH_BUNDLE, TypeId::of::() )); } #[apply(test!)] async fn check_sending_messages() { struct Sender; impl InternalMessageHandler for Sender { type HandledMessages = (Foo, Bar); async fn handle_message( &mut self, _msg: InternalMessage, ) -> anyhow::Result { Err(anyhow::anyhow!("Not implemented!")) } } let mut s = Sender; s.send(Foo).await.unwrap_err(); } }