diff --git a/crates/tytix-core/src/lib.rs b/crates/tytix-core/src/lib.rs index 7651dee..a02b34a 100644 --- a/crates/tytix-core/src/lib.rs +++ b/crates/tytix-core/src/lib.rs @@ -1,8 +1,13 @@ #![feature(const_cmp)] #![feature(const_trait_impl)] +#[macro_use] +mod macros; + use std::any::Any; use std::any::TypeId; +use std::marker::PhantomData; +use std::pin::Pin; pub use anyhow; @@ -10,14 +15,6 @@ 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, @@ -58,6 +55,46 @@ pub trait Address { fn send(&mut self, message: M) -> impl Future>; } +pub struct BoxedAddress { + addr: Box, + #[allow(clippy::type_complexity)] + send: for<'a> fn( + &'a mut dyn Any, + InternalMessage, + ) -> Pin> + 'a>>, + _pd: PhantomData, +} + +impl BoxedAddress { + pub fn new(addr: IMH) -> Self + where + IMH: InternalMessageHandler + 'static, + { + BoxedAddress { + addr: Box::new(addr), + send: |addr, msg| { + let addr: &mut IMH = addr.downcast_mut().unwrap(); + + Box::pin(addr.handle_message(msg)) + }, + _pd: PhantomData, + } + } +} + +impl InternalMessageHandler for BoxedAddress +where + MB: MessageBundle, +{ + type HandledMessages = MB; + fn handle_message( + &mut self, + msg: InternalMessage, + ) -> impl Future> { + (self.send)(self.addr.as_mut(), msg) + } +} + pub trait InternalMessageHandler { type HandledMessages: MessageBundle; fn handle_message( @@ -93,22 +130,27 @@ where } } -pub trait MessageReceiver {} - pub trait MessageBundle { - const IDS: BundleChain; + const CHAIN: BundleChain; } impl MessageBundle for () { - const IDS: BundleChain = BundleChain { - next: None, - op: BundleOp::Remove(TypeId::of::<()>()), + const CHAIN: BundleChain = BundleChain::empty(); +} + +macro_rules! impl_message_bundles { + ( [ $($ty:ident),* ] , $last:ident ) => { + impl<$($ty,)* $last> MessageBundle for ($($ty,)* $last,) + where + $( $ty: Message, )* + $last: Message, + { + const CHAIN: BundleChain = <($($ty,)*) as MessageBundle>::CHAIN.with::<$last>(); + } }; } -impl MessageBundle for (M,) { - const IDS: BundleChain = BundleChain::of::(); -} +all_the_tuples_special_first!(impl_message_bundles); #[derive(Debug, Clone, Copy)] pub struct BundleChain { @@ -121,6 +163,7 @@ pub enum BundleOp { Add(TypeId), Remove(TypeId), Chain(&'static BundleChain), + None, } impl BundleChain { @@ -135,6 +178,40 @@ impl BundleChain { check_is_contained(self, id) } + pub const fn is_subset_of(&self, ids: &BundleChain) -> bool { + let mut current = self; + + loop { + match current.op { + BundleOp::Add(type_id) => { + if !ids.contains(type_id) { + return false; + } + } + BundleOp::Remove(..) => (), + BundleOp::Chain(bundle_chain) => { + let chain_result = bundle_chain.is_subset_of(ids); + let own_result = if let Some(bc) = self.next { + bc.is_subset_of(ids) + } else { + true + }; + + return chain_result && own_result; + } + BundleOp::None => (), + } + + if let Some(next) = current.next { + current = next; + } else { + break; + }; + } + + true + } + pub const fn with(&'static self) -> BundleChain { let to_add = TypeId::of::(); BundleChain { @@ -157,24 +234,13 @@ impl BundleChain { next: Some(ids), } } -} -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 const fn empty() -> BundleChain { + BundleChain { + op: BundleOp::None, + next: None, + } + } } pub trait IsContainedInBundle { @@ -186,7 +252,7 @@ where M: Message, MB: MessageBundle, { - const IS_CONTAINED: bool = check_is_contained(&MB::IDS, TypeId::of::()); + const IS_CONTAINED: bool = check_is_contained(&MB::CHAIN, TypeId::of::()); } const fn check_is_contained(ids: &BundleChain, id: TypeId) -> bool { @@ -206,6 +272,7 @@ const fn check_is_contained(ids: &BundleChain, id: TypeId) -> bool { return true; } } + BundleOp::None => (), } if let Some(next) = ids.next { @@ -269,6 +336,16 @@ mod tests { )); } + #[test] + fn check_subset() { + const CHAIN: BundleChain = BundleChain::of::().join(&BundleChain::of::()); + const SUB: BundleChain = BundleChain::of::().with::(); + + assert!(SUB.is_subset_of(&CHAIN)); + + assert!(!BundleChain::of::().is_subset_of(&CHAIN)); + } + #[apply(test!)] async fn check_sending_messages() { struct Sender; diff --git a/crates/tytix-core/src/macros.rs b/crates/tytix-core/src/macros.rs new file mode 100644 index 0000000..bcb7bc2 --- /dev/null +++ b/crates/tytix-core/src/macros.rs @@ -0,0 +1,21 @@ +#[rustfmt::skip] +macro_rules! all_the_tuples_special_first { + ($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); + }; +} diff --git a/crates/tytix/src/lib.rs b/crates/tytix/src/lib.rs index f1222ef..523c5df 100644 --- a/crates/tytix/src/lib.rs +++ b/crates/tytix/src/lib.rs @@ -1,6 +1,7 @@ use std::marker::PhantomData; use futures::FutureExt; +use tytix_core::BoxedAddress; use tytix_core::InternalMessage; use tytix_core::InternalMessageHandler; use tytix_core::IsContainedInBundle; @@ -18,17 +19,29 @@ pub trait AddressExt { fn inspect(self, f: F) -> Inspect where - F: Fn(&M) -> U, - M: Message + IsContainedInBundle, + Inspect: InternalMessageHandler, Self: Sized; fn join(self, o: Other) -> Joined where Joined: InternalMessageHandler, Self: Sized; + + fn simplify(self) -> SimpleAddress + where + SimpleAddress: InternalMessageHandler, + Self: Sized; + + fn boxed(self) -> BoxedAddress + where + BoxedAddress: InternalMessageHandler; } -impl> AddressExt for A { +impl AddressExt for A +where + MB: MessageBundle, + A: InternalMessageHandler + 'static, +{ fn map(self, f: F, r: RF) -> MappedMessage where MappedMessage: InternalMessageHandler, @@ -44,16 +57,9 @@ impl> Address fn inspect(self, f: F) -> Inspect where - F: Fn(&M) -> U, + Inspect: InternalMessageHandler, Self: Sized, - M: Message + IsContainedInBundle, { - const { - let true = >::IS_CONTAINED else { - panic!("Message is not contained in MessageBundle",); - }; - } - Inspect { address: self, func: f, @@ -71,6 +77,47 @@ impl> Address right: o, } } + + fn simplify(self) -> SimpleAddress + where + SimpleAddress: InternalMessageHandler, + Self: Sized, + { + SimpleAddress { + inner: self, + _pd: PhantomData, + } + } + + fn boxed(self) -> BoxedAddress { + BoxedAddress::new(self) + } +} + +pub struct SimpleAddress { + inner: A, + _pd: PhantomData, +} + +impl InternalMessageHandler for SimpleAddress +where + SMB: MessageBundle, + MB: MessageBundle, + A: InternalMessageHandler, +{ + type HandledMessages = SMB; + + fn handle_message( + &mut self, + msg: InternalMessage, + ) -> impl Future> { + const { + let true = SMB::CHAIN.is_subset_of(&MB::CHAIN) else { + panic!("Message is not contained in MessageBundle",); + }; + } + self.inner.handle_message(msg) + } } pub struct Joined { @@ -87,7 +134,8 @@ where L: MessageBundle, R: MessageBundle, { - const IDS: tytix_core::BundleChain = ::IDS.join(&::IDS); + const CHAIN: tytix_core::BundleChain = + ::CHAIN.join(&::CHAIN); } impl InternalMessageHandler for Joined @@ -100,7 +148,7 @@ where async fn handle_message(&mut self, msg: InternalMessage) -> anyhow::Result { let message_id = msg.type_id(); - if L::HandledMessages::IDS.contains(message_id) { + if L::HandledMessages::CHAIN.contains(message_id) { self.left.handle_message(msg).await } else { self.right.handle_message(msg).await @@ -148,7 +196,7 @@ where M: Message, R: Message + IsContainedInBundle, { - const IDS: tytix_core::BundleChain = MB::IDS.without::().with::(); + const CHAIN: tytix_core::BundleChain = MB::CHAIN.without::().with::(); } impl InternalMessageHandler for MappedMessage @@ -219,9 +267,9 @@ mod tests { type Reply = usize; } - struct SimpleAddress; + struct FooBarAddress; - impl InternalMessageHandler for SimpleAddress { + impl InternalMessageHandler for FooBarAddress { type HandledMessages = (Foo, Bar); async fn handle_message( @@ -253,7 +301,7 @@ mod tests { async fn check_mapping() { static MSG: OnceLock = OnceLock::new(); - let mut sa = SimpleAddress.map( + let mut sa = FooBarAddress.map( |_b: Bar| { let _ = MSG.set(true); async { Foo } @@ -270,7 +318,7 @@ mod tests { async fn check_inspect() { static MSG: OnceLock = OnceLock::new(); - let mut sa = SimpleAddress.inspect(|_b: &Bar| { + let mut sa = FooBarAddress.inspect(|_b: &Bar| { let _ = MSG.set(true); async {} }); @@ -285,11 +333,11 @@ mod tests { } #[apply(test!)] - async fn check_join() { + async fn check_simplify() { static MSG_SA: OnceLock = OnceLock::new(); static MSG_ZAP: OnceLock = OnceLock::new(); - let sa = SimpleAddress.inspect(|_b: &Bar| { + let sa = FooBarAddress.inspect(|_b: &Bar| { MSG_SA.set(true).unwrap(); async {} }); @@ -308,4 +356,42 @@ mod tests { MSG_ZAP.get().expect("The message was NOT inspected!"); } + + #[apply(test!)] + async fn check_join() { + static MSG_SA: OnceLock = OnceLock::new(); + + let sa = FooBarAddress.inspect(|_b: &Bar| { + MSG_SA.set(true).unwrap(); + async {} + }); + + let zap = ZapAddress.join(ZapAddress).join(ZapAddress); + + let joined = sa.join(zap); + + let mut simple = joined.simplify::<(Foo, Bar, Zap)>(); + + simple.send(Bar).await.unwrap(); + + MSG_SA.get().expect("The message was not :CC inspected!"); + } + + #[apply(test!)] + async fn check_boxed() { + static MSG_SA: OnceLock = OnceLock::new(); + + let sa = FooBarAddress.inspect(|_b: &Bar| { + MSG_SA.set(true).unwrap(); + async {} + }); + + let zap = ZapAddress.join(ZapAddress).join(ZapAddress); + + let mut boxed = zap.join(sa).simplify::<(Bar, Zap, Foo)>().boxed(); + + boxed.send(Bar).await.unwrap(); + + MSG_SA.get().expect("The message was not :CC inspected!"); + } }