From f3f816acd9f6c5bc497e037b2e75eb6c74d4ca3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Tue, 4 Nov 2025 09:42:03 +0100 Subject: [PATCH] Make everything async MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- Cargo.lock | 395 +++++++++++++++++++++++++++++++++++ crates/tytix-core/Cargo.toml | 3 + crates/tytix-core/src/lib.rs | 72 +++++-- crates/tytix/Cargo.toml | 3 + crates/tytix/src/lib.rs | 60 ++++-- 5 files changed, 497 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6155e1..775ef39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,408 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "async-executor" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys", +] + +[[package]] +name = "async-lock" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "macro_rules_attribute" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smol-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfcaedb62e0475a6898988138995ec7b1e5d116167a72bb12c7b59d0649fbbc2" +dependencies = [ + "async-executor", + "async-io", + "async-lock", + "event-listener", + "futures-lite", +] + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tytix" version = "0.1.0" dependencies = [ + "futures", + "macro_rules_attribute", + "smol-macros", "tytix-core", ] [[package]] name = "tytix-core" version = "0.1.0" +dependencies = [ + "anyhow", + "macro_rules_attribute", + "smol-macros", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/crates/tytix-core/Cargo.toml b/crates/tytix-core/Cargo.toml index 071be85..16eeaba 100644 --- a/crates/tytix-core/Cargo.toml +++ b/crates/tytix-core/Cargo.toml @@ -13,5 +13,8 @@ categories = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1" [dev-dependencies] +macro_rules_attribute = "0.2" +smol-macros = "0.1" diff --git a/crates/tytix-core/src/lib.rs b/crates/tytix-core/src/lib.rs index 2f9d629..b65d9c4 100644 --- a/crates/tytix-core/src/lib.rs +++ b/crates/tytix-core/src/lib.rs @@ -4,6 +4,8 @@ use std::any::Any; use std::any::TypeId; +pub use anyhow; + pub trait Message: Send + Any { type Reply: Send + Any; } @@ -16,28 +18,44 @@ impl MessageIdentifier for M { const IDENT: BundleChain = BundleChain::of::(); } -pub struct InternalMessage(Box); +pub struct InternalMessage { + value: Box, + name: &'static str, +} impl InternalMessage { - pub fn new(message: impl Message) -> InternalMessage { - InternalMessage(Box::new(message)) + pub fn new(message: M) -> InternalMessage { + InternalMessage { + value: Box::new(message), + name: std::any::type_name::(), + } } - pub fn into_inner(self) -> Result { - self.0.downcast().map(|v| *v).map_err(InternalMessage) + pub fn into_inner(self) -> Result { + self.value + .downcast() + .map(|v| *v) + .map_err(|value| InternalMessage { + value, + name: self.name, + }) + } + + pub fn type_name(&self) -> &'static str { + self.name } } -pub trait Address -where - MB: MessageBundle, -{ - fn send(&mut self, message: M); +pub trait Address { + fn send(&mut self, message: M) -> impl Future>; } pub trait InternalMessageHandler { type HandledMessages: MessageBundle; - fn handle_message(&mut self, msg: InternalMessage); + fn handle_message( + &mut self, + msg: InternalMessage, + ) -> impl Future>; } impl Address for IMH @@ -45,14 +63,26 @@ where MB: MessageBundle, IMH: InternalMessageHandler, { - fn send(&mut self, message: M) { + fn send(&mut self, message: M) -> impl Future> { const { let true = >::IS_CONTAINED else { panic!("Message is not contained in MessageBundle",); }; } - self.handle_message(InternalMessage(Box::new(message))); + 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() + ) + }) + }) + } } } @@ -173,6 +203,9 @@ const fn remove_from_chain(prev: &'static BundleChain, to_remove: TypeId) -> Bun #[cfg(test)] mod tests { + use macro_rules_attribute::apply; + use smol_macros::test; + use super::*; struct Foo; @@ -218,18 +251,23 @@ mod tests { )); } - #[test] - fn check_sending_messages() { + #[apply(test!)] + async fn check_sending_messages() { struct Sender; impl InternalMessageHandler for Sender { type HandledMessages = (Foo, Bar); - fn handle_message(&mut self, _msg: InternalMessage) {} + async fn handle_message( + &mut self, + _msg: InternalMessage, + ) -> anyhow::Result { + Err(anyhow::anyhow!("Not implemented!")) + } } let mut s = Sender; - s.send(Foo); + s.send(Foo).await.unwrap_err(); } } diff --git a/crates/tytix/Cargo.toml b/crates/tytix/Cargo.toml index e7e8af6..2cc6a2a 100644 --- a/crates/tytix/Cargo.toml +++ b/crates/tytix/Cargo.toml @@ -7,6 +7,9 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +futures = "0.3.31" tytix-core = { path = "../tytix-core/" } [dev-dependencies] +macro_rules_attribute = "0.2.2" +smol-macros = "0.1.1" diff --git a/crates/tytix/src/lib.rs b/crates/tytix/src/lib.rs index 5b3512b..fe601a6 100644 --- a/crates/tytix/src/lib.rs +++ b/crates/tytix/src/lib.rs @@ -1,26 +1,31 @@ use std::marker::PhantomData; +use futures::FutureExt; use tytix_core::Address; +use tytix_core::InternalMessage; use tytix_core::InternalMessageHandler; use tytix_core::IsContainedInBundle; use tytix_core::Message; use tytix_core::MessageBundle; +use tytix_core::anyhow; pub trait AddressExt { - fn map_before(self, f: F) -> MappedBeforeAddress + fn map_message(self, f: F) -> MappedBeforeAddress where MB: MessageBundle, - F: Fn(M) -> R + 'static, + F: Fn(M) -> U + 'static, + U: Future, M: Message, R: Message + IsContainedInBundle, Self: Sized; } impl> AddressExt for A { - fn map_before(self, f: F) -> MappedBeforeAddress + fn map_message(self, f: F) -> MappedBeforeAddress where MB: MessageBundle, - F: Fn(M) -> R + 'static, + F: Fn(M) -> U + 'static, + U: Future, M: Message, R: Message + IsContainedInBundle, Self: Sized, @@ -58,23 +63,32 @@ where const IDS: tytix_core::BundleChain = MB::IDS.without::().with::(); } -impl InternalMessageHandler for MappedBeforeAddress +impl InternalMessageHandler for MappedBeforeAddress where A: InternalMessageHandler, M: Message, R: Message + IsContainedInBundle, - F: FnMut(M) -> R, + F: FnMut(M) -> U, + U: Future, { type HandledMessages = MappedHandledMessages; - fn handle_message(&mut self, msg: tytix_core::InternalMessage) { - match msg.into_inner::() { - Ok(removed_message) => { - let new_message = (self.func)(removed_message); - - self.address.send(new_message); + fn handle_message( + &mut self, + msg: tytix_core::InternalMessage, + ) -> impl Future> { + let msg = match msg.into_inner::() { + Ok(incoming) => { + let map_fut = (self.func)(incoming); + async { InternalMessage::new(map_fut.await) }.left_future() } - Err(other_message) => self.address.handle_message(other_message), + Err(other) => async { other }.right_future(), + }; + + async { + let msg = msg.await; + + self.address.handle_message(msg).await } } } @@ -83,6 +97,9 @@ where mod tests { use std::sync::OnceLock; + use macro_rules_attribute::apply; + use smol_macros::test; + use super::*; struct Foo; @@ -102,21 +119,26 @@ mod tests { impl InternalMessageHandler for SimpleAddress { type HandledMessages = (Foo,); - fn handle_message(&mut self, msg: tytix_core::InternalMessage) { + fn handle_message( + &mut self, + msg: tytix_core::InternalMessage, + ) -> impl Future> { drop(msg); + + async { Ok(InternalMessage::new(())) } } } - #[test] - fn check_mapping() { + #[apply(test!)] + async fn check_mapping() { static MSG: OnceLock = OnceLock::new(); - let mut sa = SimpleAddress.map_before(|_b: Bar| { + let mut sa = SimpleAddress.map_message(|_b: Bar| { let _ = MSG.set(true); - Foo + async { Foo } }); - sa.send(Bar); + sa.send(Bar).await.unwrap(); MSG.get().expect("The message was mapped!"); }