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