pub use {paste, tor_bytes};
#[macro_export]
macro_rules! restricted_msg {
{
$(#[$meta:meta])*
$(@omit_from $omit_from:literal)?
$v:vis enum $name:ident : RelayMsg {
$($tt:tt)*
}
} => {
$crate::restrict::restricted_msg!{
[
any_type: $crate::relaycell::msg::AnyRelayMsg,
msg_mod: $crate::relaycell::msg,
cmd_type: $crate::relaycell::RelayCmd,
unrecognized: $crate::relaycell::msg::Unrecognized,
body_trait: $crate::relaycell::msg::Body,
msg_trait: $crate::relaycell::RelayMsg,
omit_from: $($omit_from)?
]
$(#[$meta])*
$v enum $name { $($tt)*}
}
};
{
$(#[$meta:meta])*
$(@omit_from $omit_from:literal)?
$v:vis enum $name:ident : ChanMsg {
$($tt:tt)*
}
} => {
$crate::restrict::restricted_msg!{
[
any_type: $crate::chancell::msg::AnyChanMsg,
msg_mod: $crate::chancell::msg,
cmd_type: $crate::chancell::ChanCmd,
unrecognized: $crate::chancell::msg::Unrecognized,
body_trait: $crate::chancell::msg::Body,
msg_trait: $crate::chancell::ChanMsg,
omit_from: $($omit_from)?
]
$(#[$meta])*
$v enum $name { $($tt)*}
}
};
{
[
any_type: $any_msg:ty,
msg_mod: $msg_mod:path,
cmd_type: $cmd_type:ty,
unrecognized: $unrec_type:ty,
body_trait: $body_type:ty,
msg_trait: $msg_trait:ty,
omit_from: $($omit_from:literal)?
]
$(#[$meta:meta])*
$v:vis enum $name:ident {
$(
$(#[$case_meta:meta])*
$([feature=$feat:literal])?
$case:ident
),*
$(, _ =>
$(#[$unrec_meta:meta])*
$unrecognized:ident )?
$(,)?
}
} => {
$crate::restrict::paste::paste!{
$(#[$meta])*
$v enum $name {
$(
$(#[$case_meta])*
$( #[cfg(feature=$feat)] )?
$case($msg_mod :: $case),
)*
$(
$(#[$unrec_meta])*
$unrecognized($unrec_type)
)?
}
impl $msg_trait for $name {
fn cmd(&self) -> $cmd_type {
match self {
$(
$( #[cfg(feature=$feat)] )?
Self::$case(_) => $cmd_type:: [<$case:snake:upper>] ,
)*
$(
Self::$unrecognized(u) => u.cmd(),
)?
}
}
fn encode_onto<W:>(self, w: &mut W) -> $crate::restrict::tor_bytes::EncodeResult<()>
where
W: $crate::restrict::tor_bytes::Writer + ?Sized
{
match self {
$(
$( #[cfg(feature=$feat)] )?
Self::$case(m) => $body_type::encode_onto(m, w),
)*
$(
Self::$unrecognized(u) => $body_type::encode_onto(u, w),
)?
}
}
fn decode_from_reader(cmd: $cmd_type, r: &mut $crate::restrict::tor_bytes::Reader<'_>) -> $crate::restrict::tor_bytes::Result<Self> {
Ok(match cmd {
$(
$( #[cfg(feature=$feat)] )?
$cmd_type:: [<$case:snake:upper>] => Self::$case( <$msg_mod :: $case as $body_type> :: decode_from_reader(r)? ),
)*
$(
_ => Self::$unrecognized($unrec_type::decode_with_cmd(cmd, r)?),
)?
#[allow(unreachable_patterns)] _ => return Err($crate::restrict::tor_bytes::Error::InvalidMessage(
format!("Unexpected command {} in {}", cmd, stringify!($name)).into()
)),
})
}
}
impl $crate::restrict::RestrictedMsg for $name {
type Cmd = $cmd_type;
fn cmds_for_logging() -> &'static [Self::Cmd] {
&[$(
$( #[cfg(feature=$feat)] )?
$cmd_type:: [<$case:snake:upper>],
)*]
}
}
#[allow(unexpected_cfgs)]
const _: () = {
$(
#[cfg(feature = $omit_from)]
)?
impl From<$name> for $any_msg {
fn from(msg: $name) -> $any_msg {
match msg {
$(
$( #[cfg(feature=$feat)] )?
$name::$case(b) => Self::$case(b),
)*
$(
$name::$unrecognized(u) => $any_msg::Unrecognized(u),
)?
}
}
}
};
#[allow(unexpected_cfgs)]
const _: () = {
$(
#[cfg(feature = $omit_from)]
)?
impl TryFrom<$any_msg> for $name {
type Error = $any_msg;
fn try_from(msg: $any_msg) -> std::result::Result<$name, $any_msg> {
Ok(match msg {
$(
$( #[cfg(feature=$feat)] )?
$any_msg::$case(b) => $name::$case(b),
)*
$(
$any_msg::Unrecognized(u) => Self::$unrecognized(u),
)?
#[allow(unreachable_patterns)]
other => return Err(other),
})
}
}
};
$(
$( #[cfg(feature=$feat)] )?
impl From<$msg_mod :: $case> for $name {
fn from(m: $msg_mod::$case) -> $name {
$name :: $case(m)
}
}
)*
$(
impl From<$unrec_type> for $name {
fn from (u: $unrec_type) -> $name {
$name::$unrecognized(u)
}
}
)?
}
}
}
pub use restricted_msg;
pub trait RestrictedMsg {
type Cmd: Copy + Clone + std::fmt::Debug + std::fmt::Display + Eq + PartialEq + 'static;
fn cmds_for_logging() -> &'static [Self::Cmd];
}
#[cfg(test)]
mod test {
restricted_msg! {
enum StrictOpenStreamMsg : RelayMsg {
Data,
Sendme,
End,
}
}
restricted_msg! {
enum CircuitBuildReply : ChanMsg {
Created,
Created2,
CreatedFast,
Destroy,
_ => Unrecognized,
}
}
restricted_msg! {
enum StrictCircuitBuildReply : ChanMsg {
Created,
Created2,
CreatedFast,
Destroy,
}
}
}