#[macro_export]
macro_rules! policy {
() => {};
(GatePolicy: $name:ident | $arg:ident | { $($body:tt)* } $($rest:tt)*) => {
#[derive(Default)]
pub struct $name;
impl $crate::policy::GatePolicy for $name {
#[allow(unused_variables)]
fn evaluate(&self, $arg: &$crate::Frame) -> $crate::policy::TransitStatus {
$($body)*
}
}
$crate::policy! { $($rest)* }
};
(GatePolicy: $name:ident { $($body:tt)* } $($rest:tt)*) => {
#[derive(Default)]
pub struct $name;
impl $crate::policy::GatePolicy for $name {
#[allow(unused_variables)]
fn evaluate(&self, frame: &$crate::Frame) -> $crate::policy::TransitStatus {
$($body)*
}
}
$crate::policy! { $($rest)* }
};
(ReceptorPolicy<$msg:ty>: $name:ident | $arg:ident | { $($body:tt)* } $($rest:tt)*) => {
#[derive(Default)]
pub struct $name;
impl $crate::policy::ReceptorPolicy<$msg> for $name {
#[allow(unused_variables)]
fn evaluate(&self, $arg: &$msg) -> $crate::policy::TransitStatus {
$($body)*
}
}
$crate::policy! { $($rest)* }
};
(ReceptorPolicy<$msg:ty>: $name:ident { $($body:tt)* } $($rest:tt)*) => {
#[derive(Default)]
pub struct $name;
impl $crate::policy::ReceptorPolicy<$msg> for $name {
#[allow(unused_variables)]
fn evaluate(&self, message: &$msg) -> $crate::policy::TransitStatus {
$($body)*
}
}
$crate::policy! { $($rest)* }
};
(RestartPolicy: $name:ident ($max:expr, $delay:expr) | $frame_arg:ident, $failure_arg:ident, $attempt_arg:ident | { $($body:tt)* } $($rest:tt)*) => {
#[derive(Default)]
pub struct $name;
impl $crate::transport::policy::CoreRetryPolicy for $name {
fn max_attempts(&self) -> usize { $max }
fn delay_ms(&self, attempt: usize) -> u64 { ($delay as u64).saturating_mul(attempt as u64 + 1) }
}
impl $crate::transport::policy::RestartPolicy for $name {
#[allow(unused_variables)]
fn evaluate(
&self,
$frame_arg: Box<$crate::Frame>,
$failure_arg: &$crate::transport::error::TransportFailure,
$attempt_arg: usize,
) -> $crate::transport::policy::RetryAction {
$($body)*
}
}
$crate::policy! { $($rest)* }
};
(RestartPolicy: $name:ident ($max:expr) | $frame_arg:ident, $failure_arg:ident, $attempt_arg:ident | { $($body:tt)* } $($rest:tt)*) => {
$crate::policy! { RestartPolicy: $name ($max, 0) | $frame_arg, $failure_arg, $attempt_arg | { $($body)* } $($rest)* }
};
(RestartPolicy: $name:ident | $frame_arg:ident, $failure_arg:ident, $attempt_arg:ident | { $($body:tt)* } $($rest:tt)*) => {
#[derive(Default)]
pub struct $name;
impl $crate::transport::policy::CoreRetryPolicy for $name {
fn max_attempts(&self) -> usize { 1 }
fn delay_ms(&self, _attempt: usize) -> u64 { 0 }
}
impl $crate::transport::policy::RestartPolicy for $name {
#[allow(unused_variables)]
fn evaluate(
&self,
$frame_arg: Box<$crate::Frame>,
$failure_arg: &$crate::transport::error::TransportFailure,
$attempt_arg: usize,
) -> $crate::transport::policy::RetryAction {
$($body)*
}
}
$crate::policy! { $($rest)* }
};
}
#[cfg(test)]
mod tests {
#![allow(unused_variables)]
use crate::der::Sequence;
use crate::policy::{GatePolicy, ReceptorPolicy, TransitStatus};
use crate::transport::policy::RetryAction;
use crate::Beamable;
#[derive(Beamable, Sequence, Clone, Debug, PartialEq)]
struct DummyMessage {
value: u64,
}
policy! {
GatePolicy: TestGateBusy |_frame| {
TransitStatus::Busy
}
GatePolicy: TestGateAccept |_frame| {
TransitStatus::Accepted
}
GatePolicy: TestGateImplicitArg {
TransitStatus::Accepted
}
ReceptorPolicy<DummyMessage>: TestReceptorReject |message| {
if message.value == 0 {
TransitStatus::Forbidden
} else {
TransitStatus::Accepted
}
}
ReceptorPolicy<DummyMessage>: TestReceptorImplicitArg {
TransitStatus::Accepted
}
RestartPolicy: TestRestart |frame, _failure, _attempt| {
RetryAction::Retry(frame)
}
RestartPolicy: TestRestartMaxOnly (2) |frame, _failure, _attempt| {
RetryAction::Retry(frame)
}
RestartPolicy: TestRestartConfigured (3, 250) |frame, _failure, _attempt| {
RetryAction::Retry(frame)
}
}
#[allow(dead_code)]
impl TestGateAccept {}
#[allow(dead_code)]
impl TestRestart {}
#[test]
fn test_gate_policy() -> Result<(), crate::TightBeamError> {
let gate = TestGateBusy;
let frame = compose! {
V0: id: b"test", message: DummyMessage { value: 42 }
}?;
assert_eq!(gate.evaluate(&frame), TransitStatus::Busy);
Ok(())
}
#[test]
fn test_receptor_policy() {
let receptor = TestReceptorReject;
assert_eq!(receptor.evaluate(&DummyMessage { value: 1 }), TransitStatus::Accepted);
assert_eq!(receptor.evaluate(&DummyMessage { value: 0 }), TransitStatus::Forbidden);
}
#[test]
fn test_implicit_argument_arms() -> Result<(), crate::TightBeamError> {
let frame = compose! {
V0: id: b"test", message: DummyMessage { value: 42 }
}?;
assert_eq!(TestGateImplicitArg.evaluate(&frame), TransitStatus::Accepted);
assert_eq!(
TestReceptorImplicitArg.evaluate(&DummyMessage { value: 42 }),
TransitStatus::Accepted
);
Ok(())
}
#[test]
fn test_restart_policy_retry_configuration() {
use crate::transport::policy::CoreRetryPolicy;
assert_eq!(TestRestart.max_attempts(), 1);
assert_eq!(TestRestart.delay_ms(0), 0);
assert_eq!(TestRestartMaxOnly.max_attempts(), 2);
assert_eq!(TestRestartMaxOnly.delay_ms(1), 0);
assert_eq!(TestRestartConfigured.max_attempts(), 3);
assert_eq!(TestRestartConfigured.delay_ms(1), 500);
}
}