Make everything async

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2025-11-04 09:42:03 +01:00
parent 9ff1cf1bac
commit f3f816acd9
5 changed files with 497 additions and 36 deletions

395
Cargo.lock generated
View file

@ -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",
]

View file

@ -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"

View file

@ -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<M: Message> MessageIdentifier for M {
const IDENT: BundleChain = BundleChain::of::<M>();
}
pub struct InternalMessage(Box<dyn std::any::Any>);
pub struct InternalMessage {
value: Box<dyn std::any::Any>,
name: &'static str,
}
impl InternalMessage {
pub fn new(message: impl Message) -> InternalMessage {
InternalMessage(Box::new(message))
pub fn new<M: Any>(message: M) -> InternalMessage {
InternalMessage {
value: Box::new(message),
name: std::any::type_name::<M>(),
}
}
pub fn into_inner<M: Message>(self) -> Result<M, InternalMessage> {
self.0.downcast().map(|v| *v).map_err(InternalMessage)
pub fn into_inner<M: Any>(self) -> Result<M, InternalMessage> {
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<MB>
where
MB: MessageBundle,
{
fn send<M: Message>(&mut self, message: M);
pub trait Address<MB> {
fn send<M: Message>(&mut self, message: M) -> impl Future<Output = anyhow::Result<M::Reply>>;
}
pub trait InternalMessageHandler {
type HandledMessages: MessageBundle;
fn handle_message(&mut self, msg: InternalMessage);
fn handle_message(
&mut self,
msg: InternalMessage,
) -> impl Future<Output = anyhow::Result<InternalMessage>>;
}
impl<MB, IMH> Address<MB> for IMH
@ -45,14 +63,26 @@ where
MB: MessageBundle,
IMH: InternalMessageHandler<HandledMessages = MB>,
{
fn send<M: Message>(&mut self, message: M) {
fn send<M: Message>(&mut self, message: M) -> impl Future<Output = anyhow::Result<M::Reply>> {
const {
let true = <M as IsContainedInBundle<MB>>::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::<M::Reply>().map_err(|e| {
anyhow::anyhow!(
"Expected a {}, but got a {}",
std::any::type_name::<M::Reply>(),
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<InternalMessage> {
Err(anyhow::anyhow!("Not implemented!"))
}
}
let mut s = Sender;
s.send(Foo);
s.send(Foo).await.unwrap_err();
}
}

View file

@ -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"

View file

@ -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<MB> {
fn map_before<F, M, R>(self, f: F) -> MappedBeforeAddress<Self, F, M, R>
fn map_message<F, M, R, U>(self, f: F) -> MappedBeforeAddress<Self, F, M, R>
where
MB: MessageBundle,
F: Fn(M) -> R + 'static,
F: Fn(M) -> U + 'static,
U: Future<Output = R>,
M: Message,
R: Message + IsContainedInBundle<MB>,
Self: Sized;
}
impl<MB: MessageBundle, A: Address<MB>> AddressExt<MB> for A {
fn map_before<F, M, R>(self, f: F) -> MappedBeforeAddress<Self, F, M, R>
fn map_message<F, M, R, U>(self, f: F) -> MappedBeforeAddress<Self, F, M, R>
where
MB: MessageBundle,
F: Fn(M) -> R + 'static,
F: Fn(M) -> U + 'static,
U: Future<Output = R>,
M: Message,
R: Message + IsContainedInBundle<MB>,
Self: Sized,
@ -58,23 +63,32 @@ where
const IDS: tytix_core::BundleChain = MB::IDS.without::<R>().with::<M>();
}
impl<A, F, M, R> InternalMessageHandler for MappedBeforeAddress<A, F, M, R>
impl<A, F, M, R, U> InternalMessageHandler for MappedBeforeAddress<A, F, M, R>
where
A: InternalMessageHandler,
M: Message,
R: Message + IsContainedInBundle<A::HandledMessages>,
F: FnMut(M) -> R,
F: FnMut(M) -> U,
U: Future<Output = R>,
{
type HandledMessages = MappedHandledMessages<A::HandledMessages, M, R>;
fn handle_message(&mut self, msg: tytix_core::InternalMessage) {
match msg.into_inner::<M>() {
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<Output = anyhow::Result<InternalMessage>> {
let msg = match msg.into_inner::<M>() {
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<Output = anyhow::Result<InternalMessage>> {
drop(msg);
async { Ok(InternalMessage::new(())) }
}
}
#[test]
fn check_mapping() {
#[apply(test!)]
async fn check_mapping() {
static MSG: OnceLock<bool> = 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!");
}