use crate::Handler;
use crate::world::World;
#[macro_export]
macro_rules! fan_out {
($handler:expr $(,)?) => {
compile_error!("fan_out! requires at least 2 handlers");
};
($($handler:expr),+ $(,)?) => {
$crate::FanOut(($($handler,)+))
};
}
pub struct FanOut<T>(pub T);
macro_rules! impl_fanout {
($($idx:tt: $H:ident),+) => {
impl<E, $($H),+> Handler<E> for FanOut<($($H,)+)>
where
$($H: for<'e> Handler<&'e E> + Send,)+
{
fn run(&mut self, world: &mut World, event: E) {
$(self.0.$idx.run(world, &event);)+
}
fn name(&self) -> &'static str {
"FanOut"
}
}
};
}
impl_fanout!(0: H0, 1: H1);
impl_fanout!(0: H0, 1: H1, 2: H2);
impl_fanout!(0: H0, 1: H1, 2: H2, 3: H3);
impl_fanout!(0: H0, 1: H1, 2: H2, 3: H3, 4: H4);
impl_fanout!(0: H0, 1: H1, 2: H2, 3: H3, 4: H4, 5: H5);
impl_fanout!(0: H0, 1: H1, 2: H2, 3: H3, 4: H4, 5: H5, 6: H6);
impl_fanout!(0: H0, 1: H1, 2: H2, 3: H3, 4: H4, 5: H5, 6: H6, 7: H7);
trait RefHandler<E>: Send {
fn run_ref(&mut self, world: &mut World, event: &E);
}
impl<E, H> RefHandler<E> for H
where
H: for<'e> Handler<&'e E> + Send,
{
fn run_ref(&mut self, world: &mut World, event: &E) {
self.run(world, event);
}
}
pub struct Broadcast<E> {
handlers: Vec<Box<dyn RefHandler<E>>>,
}
impl<E> Default for Broadcast<E> {
fn default() -> Self {
Self::new()
}
}
impl<E> Broadcast<E> {
pub fn new() -> Self {
Self {
handlers: Vec::new(),
}
}
pub fn add<H: for<'e> Handler<&'e E> + Send + 'static>(&mut self, handler: H) {
self.handlers.push(Box::new(handler));
}
pub fn len(&self) -> usize {
self.handlers.len()
}
pub fn is_empty(&self) -> bool {
self.handlers.is_empty()
}
}
impl<E> Handler<E> for Broadcast<E> {
fn run(&mut self, world: &mut World, event: E) {
for h in &mut self.handlers {
h.run_ref(world, &event);
}
}
fn name(&self) -> &'static str {
"Broadcast"
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Cloned, IntoHandler, ResMut, WorldBuilder};
fn write_u64(mut sink: ResMut<u64>, event: &u32) {
*sink += *event as u64;
}
fn write_i64(mut sink: ResMut<i64>, event: &u32) {
*sink += *event as i64 * 2;
}
fn write_f64(mut sink: ResMut<f64>, event: &u32) {
*sink += *event as f64 * 0.5;
}
fn owned_handler(mut sink: ResMut<u64>, event: u32) {
*sink += event as u64 * 10;
}
#[test]
fn fanout_two_way() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<i64>(0);
let mut world = builder.build();
let h1 = write_u64.into_handler(world.registry());
let h2 = write_i64.into_handler(world.registry());
let mut fan = fan_out!(h1, h2);
fan.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 5);
assert_eq!(*world.resource::<i64>(), 10);
}
#[test]
fn fanout_three_way() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<i64>(0);
builder.register::<f64>(0.0);
let mut world = builder.build();
let h1 = write_u64.into_handler(world.registry());
let h2 = write_i64.into_handler(world.registry());
let h3 = write_f64.into_handler(world.registry());
let mut fan = fan_out!(h1, h2, h3);
fan.run(&mut world, 10u32);
assert_eq!(*world.resource::<u64>(), 10);
assert_eq!(*world.resource::<i64>(), 20);
#[allow(clippy::float_cmp)]
{
assert_eq!(*world.resource::<f64>(), 5.0);
}
}
#[test]
fn fanout_with_cloned_adapter() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let ref_h = write_u64.into_handler(world.registry());
let owned_h = owned_handler.into_handler(world.registry());
let mut fan = fan_out!(ref_h, Cloned(owned_h));
fan.run(&mut world, 3u32);
assert_eq!(*world.resource::<u64>(), 33); }
#[test]
fn fanout_boxable() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<i64>(0);
let mut world = builder.build();
let h1 = write_u64.into_handler(world.registry());
let h2 = write_i64.into_handler(world.registry());
let mut boxed: Box<dyn Handler<u32>> = Box::new(fan_out!(h1, h2));
boxed.run(&mut world, 7u32);
assert_eq!(*world.resource::<u64>(), 7);
assert_eq!(*world.resource::<i64>(), 14);
}
#[test]
fn broadcast_dispatch() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let mut broadcast: Broadcast<u32> = Broadcast::new();
broadcast.add(write_u64.into_handler(world.registry()));
broadcast.add(write_u64.into_handler(world.registry()));
broadcast.add(write_u64.into_handler(world.registry()));
broadcast.run(&mut world, 4u32);
assert_eq!(*world.resource::<u64>(), 12); }
#[test]
fn broadcast_empty() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let mut broadcast: Broadcast<u32> = Broadcast::new();
assert!(broadcast.is_empty());
broadcast.run(&mut world, 1u32);
assert_eq!(*world.resource::<u64>(), 0);
}
#[test]
fn broadcast_len() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let world = builder.build();
let mut broadcast: Broadcast<u32> = Broadcast::new();
assert_eq!(broadcast.len(), 0);
broadcast.add(write_u64.into_handler(world.registry()));
assert_eq!(broadcast.len(), 1);
broadcast.add(write_u64.into_handler(world.registry()));
assert_eq!(broadcast.len(), 2);
}
#[test]
fn broadcast_with_cloned_adapter() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let mut broadcast: Broadcast<u32> = Broadcast::new();
broadcast.add(write_u64.into_handler(world.registry()));
broadcast.add(Cloned(owned_handler.into_handler(world.registry())));
broadcast.run(&mut world, 2u32);
assert_eq!(*world.resource::<u64>(), 22); }
}