tytix/crates/tytix-core/src/lib.rs
Marcel Müller 66971a4f26 Add inspect
Signed-off-by: Marcel Müller <neikos@neikos.email>
2025-11-04 10:24:21 +01:00

277 lines
6.2 KiB
Rust

#![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<M: Message> MessageIdentifier for M {
const IDENT: BundleChain = BundleChain::of::<M>();
}
pub struct InternalMessage {
value: Box<dyn std::any::Any>,
name: &'static str,
}
impl InternalMessage {
pub fn new<M: Any>(message: M) -> InternalMessage {
InternalMessage {
value: Box::new(message),
name: std::any::type_name::<M>(),
}
}
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 as_ref<M: Any>(&self) -> Option<&M> {
self.value.downcast_ref()
}
pub fn type_name(&self) -> &'static str {
self.name
}
}
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,
) -> impl Future<Output = anyhow::Result<InternalMessage>>;
}
impl<MB, IMH> Address<MB> for IMH
where
MB: MessageBundle,
IMH: InternalMessageHandler<HandledMessages = MB>,
{
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",);
};
}
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()
)
})
})
}
}
}
pub trait MessageReceiver {}
pub trait MessageBundle {
const IDS: BundleChain;
}
impl MessageBundle for () {
const IDS: BundleChain = BundleChain {
next: None,
op: BundleOp::Remove(TypeId::of::<()>()),
};
}
impl<M: Message> MessageBundle for (M,) {
const IDS: BundleChain = BundleChain::of::<M>();
}
#[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<M: Message>() -> BundleChain {
BundleChain {
op: BundleOp::Add(TypeId::of::<M>()),
next: None,
}
}
pub const fn with<M: Message>(&'static self) -> BundleChain {
add_to_chain(self, TypeId::of::<M>())
}
pub const fn without<M: Message>(&'static self) -> BundleChain {
remove_from_chain(self, TypeId::of::<M>())
}
}
impl<A, B> MessageBundle for (A, B)
where
A: Message,
B: Message,
{
const IDS: BundleChain = BundleChain::of::<A>().with::<B>();
}
impl<A, B, C, D> MessageBundle for (A, B, C, D)
where
A: Message,
B: Message,
C: Message,
D: Message,
{
const IDS: BundleChain = BundleChain::of::<A>().with::<B>().with::<C>().with::<D>();
}
pub trait IsContainedInBundle<MB> {
const IS_CONTAINED: bool;
}
impl<M, MB> IsContainedInBundle<MB> for M
where
M: Message,
MB: MessageBundle,
{
const IS_CONTAINED: bool = check_is_contained(&MB::IDS, TypeId::of::<M>());
}
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::<bool>(),
TypeId::of::<bool>()
));
const BUNDLE: BundleChain = BundleChain::of::<Foo>().with::<Zap>();
assert!(check_is_contained(&BUNDLE, TypeId::of::<Foo>()));
assert!(!check_is_contained(&BUNDLE, TypeId::of::<Bar>()));
const WITHOUT_BUNDLE: BundleChain = BundleChain::of::<Foo>().with::<Zap>().without::<Zap>();
assert!(!check_is_contained(&WITHOUT_BUNDLE, TypeId::of::<Zap>()));
const WITHOUT_WITH_BUNDLE: BundleChain =
BundleChain::of::<Foo>().without::<Zap>().with::<Zap>();
assert!(check_is_contained(
&WITHOUT_WITH_BUNDLE,
TypeId::of::<Zap>()
));
}
#[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<InternalMessage> {
Err(anyhow::anyhow!("Not implemented!"))
}
}
let mut s = Sender;
s.send(Foo).await.unwrap_err();
}
}