277 lines
6.2 KiB
Rust
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();
|
|
}
|
|
}
|