use std::{
any::Any,
fmt,
sync::{
Arc,
atomic::{AtomicBool, Ordering::Relaxed},
},
};
pub trait EventArgs: AnyEventArgs + Clone {
fn handle<F, R>(&self, handler: F) -> Option<R>
where
F: FnOnce(&Self) -> R,
{
if self.propagation().is_stopped() {
None
} else {
let r = handler(self);
self.propagation().stop();
Some(r)
}
}
}
pub trait AnyEventArgs: fmt::Debug + Send + Sync + Any {
fn clone_any(&self) -> Box<dyn AnyEventArgs>;
fn as_any(&self) -> &dyn Any;
fn timestamp(&self) -> crate::DInstant;
fn delivery_list(&self, list: &mut UpdateDeliveryList);
fn propagation(&self) -> &EventPropagationHandle;
}
#[derive(Debug, Clone)]
pub struct EventPropagationHandle(Arc<AtomicBool>);
impl EventPropagationHandle {
pub fn new() -> Self {
EventPropagationHandle(Arc::new(AtomicBool::new(false)))
}
pub fn stop(&self) {
self.0.store(true, Relaxed);
}
pub fn is_stopped(&self) -> bool {
self.0.load(Relaxed)
}
}
impl Default for EventPropagationHandle {
fn default() -> Self {
EventPropagationHandle::new()
}
}
impl PartialEq for EventPropagationHandle {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for EventPropagationHandle {}
impl std::hash::Hash for EventPropagationHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let ptr = Arc::as_ptr(&self.0) as usize;
std::hash::Hash::hash(&ptr, state);
}
}
#[macro_export]
macro_rules! event_args {
($(
$(#[$outer:meta])*
$vis:vis struct $Args:ident {
$($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
..
$(#[$delivery_list_outer:meta])*
fn delivery_list(&$self:ident, $delivery_list_ident:ident: &mut UpdateDeliveryList) { $($delivery_list:tt)* }
$(
$(#[$validate_outer:meta])*
fn validate(&$self_v:ident) -> Result<(), $ValidationError:path> { $($validate:tt)+ }
)?
}
)+) => {$(
$crate::__event_args! {
$(#[$outer])*
$vis struct $Args {
$($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
..
$(#[$delivery_list_outer])*
fn delivery_list(&$self, $delivery_list_ident: &mut UpdateDeliveryList) { $($delivery_list)* }
$(
$(#[$validate_outer])*
fn validate(&$self_v) -> Result<(), $ValidationError> { $($validate)+ }
)?
}
}
)+};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __event_args {
(
$(#[$outer:meta])*
$vis:vis struct $Args:ident {
$($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
..
$(#[$delivery_list_outer:meta])*
fn delivery_list(&$self:ident, $delivery_list_ident:ident: &mut UpdateDeliveryList) { $($delivery_list:tt)* }
$(#[$validate_outer:meta])*
fn validate(&$self_v:ident) -> Result<(), $ValidationError:path> { $($validate:tt)+ }
}
) => {
$crate::__event_args! {common=>
$(#[$outer])*
$vis struct $Args {
$($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
..
$(#[$delivery_list_outer])*
fn delivery_list(&$self, $delivery_list_ident: &mut UpdateDeliveryList) { $($delivery_list)* }
}
}
impl $Args {
#[allow(clippy::too_many_arguments)]
pub fn new(
timestamp: impl Into<$crate::DInstant>,
propagation_handle: $crate::event::EventPropagationHandle,
$($arg : impl Into<$arg_ty>),*
) -> Self {
let args = $Args {
timestamp: timestamp.into(),
$($arg: $arg.into(),)*
propagation_handle,
};
args.assert_valid();
args
}
#[allow(clippy::too_many_arguments)]
pub fn try_new(
timestamp: impl Into<$crate::DInstant>,
propagation_handle: $crate::event::EventPropagationHandle,
$($arg : impl Into<$arg_ty>),*
) -> Result<Self, $ValidationError> {
let args = $Args {
timestamp: timestamp.into(),
$($arg: $arg.into(),)*
propagation_handle,
};
args.validate()?;
Ok(args)
}
#[allow(clippy::too_many_arguments)]
pub fn now($($arg : impl Into<$arg_ty>),*) -> Self {
Self::new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
}
#[allow(clippy::too_many_arguments)]
pub fn try_now($($arg : impl Into<$arg_ty>),*) -> Result<Self, $ValidationError> {
Self::try_new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
}
$(#[$validate_outer])*
pub fn validate(&$self_v) -> Result<(), $ValidationError> {
$($validate)+
}
#[track_caller]
pub fn assert_valid(&self) {
if let Err(e) = self.validate() {
panic!("invalid `{}`, {e:?}", stringify!($Args));
}
}
}
};
(
$(#[$outer:meta])*
$vis:vis struct $Args:ident {
$($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
..
$(#[$delivery_list_outer:meta])*
fn delivery_list(&$self:ident, $delivery_list_ident:ident: &mut UpdateDeliveryList) { $($delivery_list:tt)* }
}
) => {
$crate::__event_args! {common=>
$(#[$outer])*
$vis struct $Args {
$($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
..
$(#[$delivery_list_outer])*
fn delivery_list(&$self, $delivery_list_ident: &mut UpdateDeliveryList) { $($delivery_list)* }
}
}
impl $Args {
#[allow(clippy::too_many_arguments)]
pub fn new(
timestamp: impl Into<$crate::DInstant>,
propagation_handle: $crate::event::EventPropagationHandle,
$($arg : impl Into<$arg_ty>),*
) -> Self {
$Args {
timestamp: timestamp.into(),
$($arg: $arg.into(),)*
propagation_handle,
}
}
#[allow(clippy::too_many_arguments)]
pub fn now($($arg : impl Into<$arg_ty>),*) -> Self {
Self::new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
}
}
};
(common=>
$(#[$outer:meta])*
$vis:vis struct $Args:ident {
$($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
..
$(#[$delivery_list_outer:meta])*
fn delivery_list(&$self:ident, $delivery_list_ident:ident: &mut UpdateDeliveryList) { $($delivery_list:tt)* }
}
) => {
$(#[$outer])*
#[derive(Debug, Clone)]
$vis struct $Args {
/// Instant the event happened.
pub timestamp: $crate::DInstant,
$($(#[$arg_outer])* $arg_vis $arg : $arg_ty,)*
propagation_handle: $crate::event::EventPropagationHandle,
}
impl $crate::event::EventArgs for $Args {
}
impl $crate::event::AnyEventArgs for $Args {
fn clone_any(&self) -> std::boxed::Box<dyn $crate::event::AnyEventArgs> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn timestamp(&self) -> $crate::DInstant {
self.timestamp
}
$(#[$delivery_list_outer])*
fn delivery_list(&$self, $delivery_list_ident: &mut $crate::update::UpdateDeliveryList) {
#[allow(unused_imports)]
use $crate::update::UpdateDeliveryList;
$($delivery_list)*
}
fn propagation(&self) -> &$crate::event::EventPropagationHandle {
&self.propagation_handle
}
}
};
}
#[doc(inline)]
pub use crate::event_args;
use crate::update::UpdateDeliveryList;