1use std::panic::AssertUnwindSafe;
4
5use crate::handler::Handler;
6use crate::world::World;
7
8pub struct CatchAssertUnwindSafe<H> {
44 handler: H,
45}
46
47impl<H> CatchAssertUnwindSafe<H> {
48 pub fn new(handler: H) -> Self {
53 Self { handler }
54 }
55}
56
57impl<E, H: Handler<E>> Handler<E> for CatchAssertUnwindSafe<H> {
58 fn run(&mut self, world: &mut World, event: E) {
59 let handler = &mut self.handler;
60 let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
61 handler.run(world, event);
62 }));
63 }
64
65 fn name(&self) -> &'static str {
66 self.handler.name()
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::{IntoHandler, ResMut, WorldBuilder};
74
75 fn normal_handler(mut val: ResMut<u64>, event: u64) {
76 *val += event;
77 }
78
79 #[test]
80 fn forwards_run() {
81 let mut builder = WorldBuilder::new();
82 builder.register::<u64>(0);
83 let mut world = builder.build();
84
85 let handler = normal_handler.into_handler(world.registry());
86 let mut guarded = CatchAssertUnwindSafe::new(handler);
87
88 guarded.run(&mut world, 10);
89 assert_eq!(*world.resource::<u64>(), 10);
90 }
91
92 fn panicking_handler(_val: ResMut<u64>, _event: u64) {
93 panic!("boom");
94 }
95
96 #[test]
97 fn survives_panic() {
98 let mut builder = WorldBuilder::new();
99 builder.register::<u64>(0);
100 let mut world = builder.build();
101
102 let handler = panicking_handler.into_handler(world.registry());
103 let mut guarded = CatchAssertUnwindSafe::new(handler);
104
105 guarded.run(&mut world, 10);
107
108 guarded.run(&mut world, 10);
110 }
111
112 fn partial_write_then_panic(mut val: ResMut<u64>, event: u64) {
113 *val += event;
114 panic!("mid-flight");
115 }
116
117 #[test]
118 fn world_state_survives_handler_panic() {
119 let mut builder = WorldBuilder::new();
120 builder.register::<u64>(0);
121 let mut world = builder.build();
122
123 let handler = partial_write_then_panic.into_handler(world.registry());
124 let mut guarded = CatchAssertUnwindSafe::new(handler);
125
126 guarded.run(&mut world, 10);
128 assert_eq!(*world.resource::<u64>(), 10);
129
130 let handler2 = normal_handler.into_handler(world.registry());
132 let mut guarded2 = CatchAssertUnwindSafe::new(handler2);
133 guarded2.run(&mut world, 5);
134 assert_eq!(*world.resource::<u64>(), 15);
135
136 guarded.run(&mut world, 3);
138 assert_eq!(*world.resource::<u64>(), 18);
139 }
140
141 #[test]
142 fn forwards_name() {
143 let mut builder = WorldBuilder::new();
144 builder.register::<u64>(0);
145 let world = builder.build();
146
147 let handler = normal_handler.into_handler(world.registry());
148 let guarded = CatchAssertUnwindSafe::new(handler);
149
150 assert!(guarded.name().contains("normal_handler"));
151 }
152}