From e09764eed84e77a56495baf03331c9c25485c149 Mon Sep 17 00:00:00 2001 From: Matthieu Le brazidec Date: Thu, 24 Oct 2019 20:34:37 +0200 Subject: [PATCH] Added a "message" macro to match a boxed Message against types --- bastion/examples/send_recv.rs | 16 ++++++--- bastion/src/lib.rs | 5 +-- bastion/src/macros.rs | 65 +++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 bastion/src/macros.rs diff --git a/bastion/examples/send_recv.rs b/bastion/examples/send_recv.rs index 5fb7038a..f41d9705 100644 --- a/bastion/examples/send_recv.rs +++ b/bastion/examples/send_recv.rs @@ -10,11 +10,16 @@ fn main() { let try_recv = ctx.try_recv().await; println!("try_recv.is_some() == {}", try_recv.is_some()); // false - ctx.send_msg(ctx.as_ref(), Box::new("Hello World!")).ok(); + ctx.current().send_msg(Box::new("Hello World!")).ok(); - // This will return Ok(Box("Hello World!")). - let recv = ctx.recv().await; - println!("recv.is_ok() == {}", recv.is_ok()); // true + message! { ctx.recv().await?, + msg: &'static str => { + println!(r#"msg == "Hello World!" => {}"#, msg == &"Hello World!"); // true + }, + // This won't happen because we know that this + // example only sends a `&'static str`... + _ => unreachable!(), + } // Panicking will restart the children group. panic!("Oh no!"); @@ -22,7 +27,8 @@ fn main() { .into() }, 1, - ).expect("Couldn't start a new children group."); + ) + .expect("Couldn't start a new children group."); Bastion::start(); Bastion::block_until_stopped(); diff --git a/bastion/src/lib.rs b/bastion/src/lib.rs index 4434a6af..a18d0930 100644 --- a/bastion/src/lib.rs +++ b/bastion/src/lib.rs @@ -3,6 +3,7 @@ pub use self::bastion::Bastion; mod bastion; mod broadcast; mod context; +mod macros; mod proc; mod system; @@ -10,9 +11,9 @@ pub mod children; pub mod supervisor; pub mod prelude { - pub use crate::Bastion; - pub use crate::children::{ChildRef, ChildrenRef, Closure, Fut, Message, Shell}; pub use crate::context::BastionContext; + pub use crate::message; pub use crate::supervisor::{SupervisionStrategy, Supervisor, SupervisorRef}; + pub use crate::Bastion; } diff --git a/bastion/src/macros.rs b/bastion/src/macros.rs new file mode 100644 index 00000000..f1db7334 --- /dev/null +++ b/bastion/src/macros.rs @@ -0,0 +1,65 @@ +#[macro_export] +/// Matches a boxed [`Message`] (`Box` as returned +/// by [`BastionContext::recv`] or [`BastionContext::try_recv`]) +/// with different types. +/// +/// Each case is defined as a variable name followed by a colon, +/// a type, and arrow and the code that will be executed if the +/// message is of the specified type. +/// +/// Only a default case is required, which is defined in the +/// same way a `match` would define one (`_ => { ... }`). +/// +/// # Example +/// +/// ``` +/// # use bastion::prelude::*; +/// # +/// # fn main() { +/// # Bastion::init(); +/// // The message that will be sent... +/// let msg = "A message containing data."; +/// +/// Bastion::children(|ctx: BastionContext| +/// async move { +/// let msg = ctx.recv().await?; +/// message! { msg, +/// msg: &'static str => { +/// assert_eq!(msg, &"A message containing data."); +/// }, +/// // We are only sending a `&'static str` in this example, +/// // so we know that this won't happen... +/// _ => unreachable!(), +/// } +/// +/// Ok(()) +/// }.into(), +/// 1, +/// ).expect("Couldn't start the children group."); +/// # +/// # Bastion::start(); +/// # Bastion::broadcast(Box::new(msg)).unwrap(); +/// # Bastion::stop(); +/// # Bastion::block_until_stopped(); +/// # } +/// ``` +/// +/// [`Message`]: children/trait.Message.html +/// [`BastionContext::recv`]: struct.BastionContext.html#method.recv +/// [`BastionContext::try_recv`]: struct.BastionContext.html#method.try_recv +macro_rules! message { + ($msg:expr, + $($var:ident: $ty:ty => $handle:expr,)* + _ => $fallback:expr, + ) => { + if false {} + $( + if let Some($var) = $msg.as_any().downcast_ref::<$ty>() { + $handle + } + )* + else { + $fallback + } + }; +}