use std::panic::AssertUnwindSafe;
use crate::handler::Handler;
use crate::world::World;
pub struct CatchAssertUnwindSafe<H> {
handler: H,
panic_count: u64,
}
impl<H> CatchAssertUnwindSafe<H> {
pub fn new(handler: H) -> Self {
Self {
handler,
panic_count: 0,
}
}
pub fn panic_count(&self) -> u64 {
self.panic_count
}
}
impl<E, H: Handler<E>> Handler<E> for CatchAssertUnwindSafe<H> {
fn run(&mut self, world: &mut World, event: E) {
let handler = &mut self.handler;
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
handler.run(world, event);
}));
if result.is_err() {
self.panic_count = self.panic_count.saturating_add(1);
}
}
fn name(&self) -> &'static str {
self.handler.name()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{IntoHandler, ResMut, WorldBuilder};
fn normal_handler(mut val: ResMut<u64>, event: u64) {
*val += event;
}
#[test]
fn forwards_run() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let handler = normal_handler.into_handler(world.registry());
let mut guarded = CatchAssertUnwindSafe::new(handler);
guarded.run(&mut world, 10);
assert_eq!(*world.resource::<u64>(), 10);
}
fn panicking_handler(_val: ResMut<u64>, _event: u64) {
panic!("boom");
}
#[test]
fn survives_panic() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let handler = panicking_handler.into_handler(world.registry());
let mut guarded = CatchAssertUnwindSafe::new(handler);
guarded.run(&mut world, 10);
guarded.run(&mut world, 10);
}
fn partial_write_then_panic(mut val: ResMut<u64>, event: u64) {
*val += event;
panic!("mid-flight");
}
#[test]
fn world_state_survives_handler_panic() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let handler = partial_write_then_panic.into_handler(world.registry());
let mut guarded = CatchAssertUnwindSafe::new(handler);
guarded.run(&mut world, 10);
assert_eq!(*world.resource::<u64>(), 10);
let handler2 = normal_handler.into_handler(world.registry());
let mut guarded2 = CatchAssertUnwindSafe::new(handler2);
guarded2.run(&mut world, 5);
assert_eq!(*world.resource::<u64>(), 15);
guarded.run(&mut world, 3);
assert_eq!(*world.resource::<u64>(), 18);
}
#[test]
fn forwards_name() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let world = builder.build();
let handler = normal_handler.into_handler(world.registry());
let guarded = CatchAssertUnwindSafe::new(handler);
assert!(guarded.name().contains("normal_handler"));
}
}