#[macro_export]
macro_rules! match_deser {
($msg:ident {$($tokens:tt)*}) => {{
let msg = $msg;
$crate::match_deser_internal!(
msg;
$($tokens)*
)
}};
(($msg:expr) {$($tokens:tt)*}) => {{
let msg = $msg;
$crate::match_deser_internal!(
msg;
$($tokens)*
)
}};
($msg:expr; {$($tokens:tt)*}) => {
compile_error!("You are using an old `match_deser!` format. See docs for the new format.");
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! match_deser_internal {
(@list
$msg:ident;
();
$cases:tt
) => {
$crate::match_deser_internal!(
@case
$msg;
$cases;
();
();
()
)
};
(@list
$msg:ident;
($case:ident ($($args:tt)*) $(: $msg_ty:ty)? $([using $deser_ty:ty])? => $body:expr, $($tail:tt)*);
($($head:tt)*)
) => {
$crate::match_deser_internal!(
@list
$msg;
($($tail)*);
($($head)* $case ($($args)*) $(: $msg_ty)* $([$deser_ty])* => { $body },)
)
};
(@list
$msg:ident;
($case:ident ($($args:tt)*) $(: $msg_ty:ty)? $([using $deser_ty:ty])? => $body:block, $($tail:tt)*);
($($head:tt)*)
) => {
$crate::match_deser_internal!(
@list
$msg;
($($tail)*);
($($head)* $case ($($args)*) $(: $msg_ty)* $([$deser_ty])* => { $body },)
)
};
(@list
$msg:ident;
($case:ident ($($args:tt)*) $(: $msg_ty:ty)? $([using $deser_ty:ty])? => $body:expr);
($($head:tt)*)
) => {
$crate::match_deser_internal!(
@list
$msg;
();
($($head)* $case ($($args)*) $(: $msg_ty)* $([$deser_ty])* => { $body },)
)
};
(@list
$msg:ident;
($case:ident ($($args:tt)*) $(: $msg_ty:ty)? $([using $deser_ty:ty])? => $body:expr,);
($($head:tt)*)
) => {
$crate::match_deser_internal!(
@list
$msg;
();
($($head)* $case ($($args)*) $(: $msg_ty)* $([$deser_ty])* => { $body },)
)
};
(@case
$msg:ident;
();
$msgs:tt;
();
()
) => {
$crate::match_deser_internal!(
@init
$msg;
$msgs;
(err(e) => { panic!("{:?}", e); });
(default(_) => { unimplemented!(); })
)
};
(@case
$msg:ident;
();
$msgs:tt;
$errors:tt;
()
) => {
$crate::match_deser_internal!(
@init
$msg;
$msgs;
$errors;
(default(_) => { unimplemented!(); })
)
};
(@case
$msg:ident;
();
$msgs:tt;
();
$default:tt
) => {
$crate::match_deser_internal!(
@init
$msg;
$msgs;
(err(e) => { panic!("{:?}", e); });
$default
)
};
(@case
$msg:ident;
();
$msgs:tt;
$error:tt;
$default:tt
) => {
$crate::match_deser_internal!(
@init
$msg;
$msgs;
$error;
$default
)
};
(@case
$msg:ident;
(msg($msg_pat:pat) : $msg_ty:ty => $body:tt, $($tail:tt)*);
($($msgs:tt)*);
$error:tt;
$default:tt
) => {
$crate::match_deser_internal!(
@case
$msg;
($($tail)*);
($($msgs)* msg($msg_pat) : $msg_ty [$msg_ty] => $body,);
$error;
$default
)
};
(@case
$msg:ident;
(msg($msg_pat:pat) : $msg_ty:ty [$deser_ty:ty] => $body:tt, $($tail:tt)*);
($($msgs:tt)*);
$error:tt;
$default:tt
) => {
$crate::match_deser_internal!(
@case
$msg;
($($tail)*);
($($msgs)* msg($msg_pat) : $msg_ty [$deser_ty] => $body,);
$error;
$default
)
};
(@case
$msg:ident;
(err($err_pat:pat) => $body:tt, $($tail:tt)*);
$msgs:tt;
();
$default:tt
) => {
$crate::match_deser_internal!(
@case
$msg;
($($tail)*);
$msgs;
(err($err_pat) => $body);
$default
)
};
(@case
$msg:ident;
(err($err_pat:pat) => $body:tt, $($tail:tt)*);
$msgs:tt;
$error:tt;
$default:tt
) => {
compile_error!("Only a single `err(_)` arm is allowed in `match_deser!`");
};
(@case
$msg:ident;
(default(_) => $body:tt, $($tail:tt)*);
$msgs:tt;
$error:tt;
()
) => {
$crate::match_deser_internal!(
@case
$msg;
($($tail)*);
$msgs;
$error;
(default(_) => $body)
)
};
(@case
$msg:ident;
(default(_) => $body:tt, $($tail:tt)*);
$msgs:tt;
$error:tt;
$default:tt
) => {
compile_error!("Only a single `default(_)` arm is allowed in `match_deser!`");
};
(@list
$msg:ident;
($($stuff:tt)*);
($($head:tt)*)
) => {
compile_error!(concat!(
"Illegal `match_deser!` item(s). See docs for example of legal items. Offending item(s): ",
stringify!($($stuff)*)
));
};
(@case
$msg:ident;
(msg($msg_pat:pat) => $body:tt, $($tail:tt)*);
($($msgs:tt)*);
$error:tt;
$default:tt
) => {
compile_error!("Must specify a deserialisation target type");
};
(@case
$msg:ident;
(msg($msg_pat:pat) [$($stuff:tt)*] => $body:tt, $($tail:tt)*);
($($msgs:tt)*);
$error:tt;
$default:tt
) => {
compile_error!("Must specify a deserialisation target type");
};
(@case
$msg:ident;
($other:ident($msg_pat:pat) $($stuff:tt)*);
($($msgs:tt)*);
$error:tt;
$default:tt
) => {
compile_error!(concat!("Illegal case `", stringify!($other), "`. Allowed values: [`msg`, `err`, `default`]"));
};
(@init
$msg:ident;
($(msg($msg_pat:pat) : $msg_ty:ty [$deser_ty:ty] => $body:tt,)*);
(err($err_pat:pat) => $err_body:tt);
(default(_) => $default_body:tt)
) => {
match $msg.ser_id() {
$( &<$deser_ty as $crate::prelude::Deserialiser<$msg_ty>>::SER_ID => {
match $msg.try_deserialise_unchecked::<$msg_ty, $deser_ty>() {
Ok($msg_pat) => $body,
Err($err_pat) => $err_body,
}
} ),*
_ => $default_body,
}
};
($msg:ident;) => {
compile_error!("Empty `match_deser!` block");
};
($msg:ident; $($tokens:tt)*) => {
$crate::match_deser_internal!(
@list
$msg;
($($tokens)*);
()
)
};
}
#[cfg(test)]
mod deser_macro_tests {
use crate::{
messaging::*,
serialisation::{Deserialiser, Serialisable, Serialiser},
};
use bytes::{Buf, BufMut};
use std::{fmt, str::FromStr};
#[test]
fn simple_macro_test() {
simple_macro_test_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrB::A(res),
msg(res): MsgB [using BSer] => EitherAOrB::B(res),
}
}
})
}
#[test]
fn generic_macro_test() {
generic_macro_test_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrBOrWrapped::A(res),
msg(res): MsgB [using BSer] => EitherAOrBOrWrapped::B(res),
msg(res): Wrapper<MsgA> => EitherAOrBOrWrapped::W(res),
}
}
})
}
#[test]
fn simple_macro_test_with_other() {
simple_macro_test_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrB::A(res),
msg(res): MsgB [using BSer] => EitherAOrB::B(res),
default(_) => unimplemented!("Should be either MsgA or MsgB!"),
}
}
})
}
#[test]
#[should_panic(expected = "test panic please ignore")]
fn simple_macro_test_with_err() {
simple_macro_test_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrB::A(res),
msg(res): MsgB [using BSer] => EitherAOrB::B(res),
err(_e) => panic!("test panic please ignore"),
}
}
});
simple_macro_test_err_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrB::A(res),
msg(res): MsgB [using BSer] => EitherAOrB::B(res),
err(_e) => panic!("test panic please ignore"),
}
}
});
}
#[test]
#[should_panic(expected = "test panic please ignore")]
fn simple_macro_test_with_err_and_other() {
simple_macro_test_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrB::A(res),
msg(res): MsgB [using BSer] => EitherAOrB::B(res),
err(_e) => panic!("test panic please ignore"),
default(_) => unimplemented!("Should be either MsgA or MsgB!"),
}
}
});
simple_macro_test_err_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrB::A(res),
msg(res): MsgB [using BSer] => EitherAOrB::B(res),
err(_e) => panic!("test panic please ignore"),
default(_) => unimplemented!("Should be either MsgA or MsgB!"),
}
}
});
}
#[test]
#[should_panic(expected = "test panic please ignore")]
fn generic_macro_test_with_err_and_other() {
generic_macro_test_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrBOrWrapped::A(res),
msg(res): MsgB [using BSer] => EitherAOrBOrWrapped::B(res),
msg(res): Wrapper<MsgA> => EitherAOrBOrWrapped::W(res),
err(_e) => panic!("test panic please ignore"),
default(_) => unimplemented!("Should be either MsgA or MsgB!"),
}
}
});
generic_macro_test_err_impl(|msg| {
match_deser! {
msg {
msg(res): MsgA => EitherAOrBOrWrapped::A(res),
msg(res): MsgB [using BSer] => EitherAOrBOrWrapped::B(res),
msg(res): Wrapper<MsgA> => EitherAOrBOrWrapped::W(res),
err(_e) => panic!("test panic please ignore"),
default(_) => unimplemented!("Should be either MsgA or MsgB!"),
}
}
});
}
#[test]
fn simple_no_macro_test() {
simple_macro_test_impl(|msg| match msg.ser_id() {
&MsgA::SER_ID => {
let res = msg
.try_deserialise_unchecked::<MsgA, MsgA>()
.expect("MsgA should deserialise!");
EitherAOrB::A(res)
}
&BSer::SER_ID => {
let res = msg
.try_deserialise_unchecked::<MsgB, BSer>()
.expect("MsgB should deserialise!");
EitherAOrB::B(res)
}
_ => unimplemented!("Should be either MsgA or MsgB!"),
})
}
fn simple_macro_test_impl<F>(f: F)
where
F: Fn(NetMessage) -> EitherAOrB,
{
let ap = ActorPath::from_str(crate::test_helpers::TEST_PATH).expect("an ActorPath");
let msg_a = MsgA::new(54);
let msg_b = MsgB::new(true);
let msg_a_ser = crate::serialisation::ser_helpers::serialise_to_msg(
ap.clone(),
ap.clone(),
Box::new(msg_a),
)
.expect("MsgA should serialise!");
let msg_b_ser = crate::serialisation::ser_helpers::serialise_to_msg(
ap.clone(),
ap,
(msg_b, BSer).into(),
)
.expect("MsgB should serialise!");
assert!(f(msg_a_ser).is_a());
assert!(f(msg_b_ser).is_b());
}
fn generic_macro_test_impl<F>(f: F)
where
F: Fn(NetMessage) -> EitherAOrBOrWrapped,
{
let ap = ActorPath::from_str(crate::test_helpers::TEST_PATH).expect("an ActorPath");
let msg_a = MsgA::new(54);
let msg_b = MsgB::new(true);
let msg_wrap_a = Wrapper::from(MsgA::new(66));
let msg_a_ser = crate::serialisation::ser_helpers::serialise_to_msg(
ap.clone(),
ap.clone(),
Box::new(msg_a),
)
.expect("MsgA should serialise!");
let msg_b_ser = crate::serialisation::ser_helpers::serialise_to_msg(
ap.clone(),
ap.clone(),
(msg_b, BSer).into(),
)
.expect("MsgB should serialise!");
let msg_wrap_a_ser = crate::serialisation::ser_helpers::serialise_to_msg(
ap.clone(),
ap,
Box::new(msg_wrap_a),
)
.expect("Wrapped<MsgA> should serialise!");
assert!(f(msg_a_ser).is_a());
assert!(f(msg_b_ser).is_b());
assert!(f(msg_wrap_a_ser).is_wrapped_a());
}
fn simple_macro_test_err_impl<F>(f: F)
where
F: Fn(NetMessage) -> EitherAOrB,
{
let ap = ActorPath::from_str(crate::test_helpers::TEST_PATH).expect("an ActorPath");
let msg = NetMessage::with_bytes(MsgA::SERID, ap.clone(), ap, Bytes::default());
f(msg);
}
fn generic_macro_test_err_impl<F>(f: F)
where
F: Fn(NetMessage) -> EitherAOrBOrWrapped,
{
let ap = ActorPath::from_str(crate::test_helpers::TEST_PATH).expect("an ActorPath");
let msg = NetMessage::with_bytes(MsgA::SERID, ap.clone(), ap, Bytes::default());
f(msg);
}
enum EitherAOrB {
#[allow(unused)]
A(MsgA),
#[allow(unused)]
B(MsgB),
}
impl EitherAOrB {
fn is_a(&self) -> bool {
match self {
EitherAOrB::A(_) => true,
EitherAOrB::B(_) => false,
}
}
fn is_b(&self) -> bool {
!self.is_a()
}
}
#[derive(Debug)]
struct MsgA {
index: u64,
}
impl MsgA {
const SERID: SerId = 42;
fn new(index: u64) -> MsgA {
MsgA { index }
}
}
impl Serialisable for MsgA {
fn ser_id(&self) -> SerId {
Self::SERID
}
fn size_hint(&self) -> Option<usize> {
Some(8)
}
fn serialise(&self, buf: &mut dyn BufMut) -> Result<(), SerError> {
buf.put_u64(self.index);
Ok(())
}
fn local(self: Box<Self>) -> Result<Box<dyn Any + Send>, Box<dyn Serialisable>> {
Ok(self)
}
}
impl Deserialiser<MsgA> for MsgA {
const SER_ID: SerId = Self::SERID;
fn deserialise(buf: &mut dyn Buf) -> Result<MsgA, SerError> {
if buf.remaining() < 8 {
return Err(SerError::InvalidData(
"Less than 8bytes remaining in buffer!".to_string(),
));
}
let index = buf.get_u64();
let msg = MsgA { index };
Ok(msg)
}
}
#[derive(Debug)]
struct MsgB {
flag: bool,
}
impl MsgB {
const SERID: SerId = 43;
fn new(flag: bool) -> MsgB {
MsgB { flag }
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct BSer;
impl Serialiser<MsgB> for BSer {
fn ser_id(&self) -> SerId {
MsgB::SERID
}
fn size_hint(&self) -> Option<usize> {
Some(1)
}
fn serialise(&self, v: &MsgB, buf: &mut dyn BufMut) -> Result<(), SerError> {
let num = if v.flag { 1u8 } else { 0u8 };
buf.put_u8(num);
Ok(())
}
}
impl Deserialiser<MsgB> for BSer {
const SER_ID: SerId = MsgB::SERID;
fn deserialise(buf: &mut dyn Buf) -> Result<MsgB, SerError> {
let num = buf.get_u8();
let flag = num == 1u8;
let msg = MsgB { flag };
Ok(msg)
}
}
enum EitherAOrBOrWrapped {
#[allow(unused)]
A(MsgA),
#[allow(unused)]
B(MsgB),
#[allow(unused)]
W(Wrapper<MsgA>),
}
impl EitherAOrBOrWrapped {
fn is_a(&self) -> bool {
matches!(self, EitherAOrBOrWrapped::A(_))
}
fn is_b(&self) -> bool {
matches!(self, EitherAOrBOrWrapped::B(_))
}
fn is_wrapped_a(&self) -> bool {
matches!(self, EitherAOrBOrWrapped::W(_))
}
}
#[derive(Debug)]
struct Wrapper<F> {
wrapped: F,
}
impl<F> Wrapper<F> {
const MAGIC_BYTE: u8 = 42u8;
pub fn from(f: F) -> Self {
Wrapper { wrapped: f }
}
}
impl<F> Serialisable for Wrapper<F>
where
F: Serialisable + Deserialiser<F> + fmt::Debug + 'static,
{
fn ser_id(&self) -> SerId {
F::SER_ID | ((Self::MAGIC_BYTE as SerId) << 2)
}
fn size_hint(&self) -> Option<usize> {
self.wrapped.size_hint().map(|a| a + 1)
}
fn serialise(&self, buf: &mut dyn BufMut) -> Result<(), SerError> {
buf.put_u8(Self::MAGIC_BYTE);
self.wrapped.serialise(buf)
}
fn local(self: Box<Self>) -> Result<Box<dyn Any + Send>, Box<dyn Serialisable>> {
Ok(self)
}
}
impl<F> Deserialiser<Wrapper<F>> for Wrapper<F>
where
F: Serialisable + Deserialiser<F> + fmt::Debug,
{
const SER_ID: SerId = F::SER_ID | ((Self::MAGIC_BYTE as SerId) << 2);
fn deserialise(buf: &mut dyn Buf) -> Result<Self, SerError> {
let magic_byte = buf.get_u8();
if magic_byte != Self::MAGIC_BYTE {
return Err(SerError::InvalidData(format!(
"Expected MAGIC_BYTE={}, but got {} instead",
Self::MAGIC_BYTE,
magic_byte
)));
}
let wrapped = F::deserialise(buf)?;
let msg = Wrapper { wrapped };
Ok(msg)
}
}
}