1use crate::Handler;
32use crate::handler::Param;
33use crate::world::{Registry, World};
34
35pub struct Callback<C, F, Params: Param> {
72 pub ctx: C,
74 pub(crate) f: F,
75 pub(crate) state: Params::State,
76 pub(crate) name: &'static str,
77}
78
79pub trait IntoCallback<C, E, Params> {
130 type Callback: Handler<E>;
132
133 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback;
135}
136
137impl<C: Send + 'static, E, F: FnMut(&mut C, E) + Send + 'static> IntoCallback<C, E, ()> for F {
142 type Callback = Callback<C, F, ()>;
143
144 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback {
145 Callback {
146 ctx,
147 f: self,
148 state: <() as Param>::init(registry),
149 name: std::any::type_name::<F>(),
150 }
151 }
152}
153
154impl<C: Send + 'static, E, F: FnMut(&mut C, E) + Send + 'static> Handler<E> for Callback<C, F, ()> {
155 fn run(&mut self, _world: &mut World, event: E) {
156 (self.f)(&mut self.ctx, event);
157 }
158
159 fn name(&self) -> &'static str {
160 self.name
161 }
162}
163
164macro_rules! impl_into_callback {
169 ($($P:ident),+) => {
170 impl<C: Send + 'static, E, F: Send + 'static, $($P: Param + 'static),+>
171 IntoCallback<C, E, ($($P,)+)> for F
172 where
173 for<'a> &'a mut F:
174 FnMut(&mut C, $($P,)+ E) +
175 FnMut(&mut C, $($P::Item<'a>,)+ E),
176 {
177 type Callback = Callback<C, F, ($($P,)+)>;
178
179 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback {
180 let state = <($($P,)+) as Param>::init(registry);
181 {
182 #[allow(non_snake_case)]
183 let ($($P,)+) = &state;
184 registry.check_access(&[
185 $(
186 (<$P as Param>::resource_id($P),
187 std::any::type_name::<$P>()),
188 )+
189 ]);
190 }
191 Callback { ctx, f: self, state, name: std::any::type_name::<F>() }
192 }
193 }
194
195 impl<C: Send + 'static, E, F: Send + 'static, $($P: Param + 'static),+>
196 Handler<E> for Callback<C, F, ($($P,)+)>
197 where
198 for<'a> &'a mut F:
199 FnMut(&mut C, $($P,)+ E) +
200 FnMut(&mut C, $($P::Item<'a>,)+ E),
201 {
202 #[allow(non_snake_case)]
203 fn run(&mut self, world: &mut World, event: E) {
204 #[allow(clippy::too_many_arguments)]
205 fn call_inner<Ctx, $($P,)+ Ev>(
206 mut f: impl FnMut(&mut Ctx, $($P,)+ Ev),
207 ctx: &mut Ctx,
208 $($P: $P,)+
209 event: Ev,
210 ) {
211 f(ctx, $($P,)+ event);
212 }
213
214 #[cfg(debug_assertions)]
218 world.clear_borrows();
219 let ($($P,)+) = unsafe {
220 <($($P,)+) as Param>::fetch(world, &mut self.state)
221 };
222 call_inner(&mut self.f, &mut self.ctx, $($P,)+ event);
223 }
224
225 fn name(&self) -> &'static str {
226 self.name
227 }
228 }
229 };
230}
231
232macro_rules! all_tuples {
234 ($m:ident) => {
235 $m!(P0);
236 $m!(P0, P1);
237 $m!(P0, P1, P2);
238 $m!(P0, P1, P2, P3);
239 $m!(P0, P1, P2, P3, P4);
240 $m!(P0, P1, P2, P3, P4, P5);
241 $m!(P0, P1, P2, P3, P4, P5, P6);
242 $m!(P0, P1, P2, P3, P4, P5, P6, P7);
243 };
244}
245
246all_tuples!(impl_into_callback);
247
248#[cfg(test)]
253mod tests {
254 use super::*;
255 use crate::{Local, Res, ResMut, WorldBuilder};
256
257 struct TimerCtx {
260 order_id: u64,
261 call_count: u64,
262 }
263
264 struct OrderCache {
265 expired: Vec<u64>,
266 }
267
268 fn ctx_only_handler(ctx: &mut TimerCtx, _event: u32) {
271 ctx.call_count += 1;
272 }
273
274 #[test]
275 fn ctx_only_no_params() {
276 let mut world = WorldBuilder::new().build();
277 let mut cb = ctx_only_handler.into_callback(
278 TimerCtx {
279 order_id: 1,
280 call_count: 0,
281 },
282 world.registry_mut(),
283 );
284 cb.run(&mut world, 42u32);
285 assert_eq!(cb.ctx.call_count, 1);
286 }
287
288 fn ctx_one_res_handler(ctx: &mut TimerCtx, cache: Res<OrderCache>, _event: u32) {
289 ctx.call_count += cache.expired.len() as u64;
290 }
291
292 #[test]
293 fn ctx_one_res() {
294 let mut builder = WorldBuilder::new();
295 builder.register::<OrderCache>(OrderCache {
296 expired: vec![1, 2, 3],
297 });
298 let mut world = builder.build();
299
300 let mut cb = ctx_one_res_handler.into_callback(
301 TimerCtx {
302 order_id: 1,
303 call_count: 0,
304 },
305 world.registry_mut(),
306 );
307 cb.run(&mut world, 0u32);
308 assert_eq!(cb.ctx.call_count, 3);
309 }
310
311 fn ctx_one_res_mut_handler(ctx: &mut TimerCtx, mut cache: ResMut<OrderCache>, _event: u32) {
312 cache.expired.push(ctx.order_id);
313 ctx.call_count += 1;
314 }
315
316 #[test]
317 fn ctx_one_res_mut() {
318 let mut builder = WorldBuilder::new();
319 builder.register::<OrderCache>(OrderCache { expired: vec![] });
320 let mut world = builder.build();
321
322 let mut cb = ctx_one_res_mut_handler.into_callback(
323 TimerCtx {
324 order_id: 42,
325 call_count: 0,
326 },
327 world.registry_mut(),
328 );
329 cb.run(&mut world, 0u32);
330 assert_eq!(cb.ctx.call_count, 1);
331 assert_eq!(world.resource::<OrderCache>().expired, vec![42]);
332 }
333
334 fn ctx_two_params_handler(
335 ctx: &mut TimerCtx,
336 counter: Res<u64>,
337 mut cache: ResMut<OrderCache>,
338 _event: u32,
339 ) {
340 cache.expired.push(*counter);
341 ctx.call_count += 1;
342 }
343
344 #[test]
345 fn ctx_two_params() {
346 let mut builder = WorldBuilder::new();
347 builder.register::<u64>(99);
348 builder.register::<OrderCache>(OrderCache { expired: vec![] });
349 let mut world = builder.build();
350
351 let mut cb = ctx_two_params_handler.into_callback(
352 TimerCtx {
353 order_id: 0,
354 call_count: 0,
355 },
356 world.registry_mut(),
357 );
358 cb.run(&mut world, 0u32);
359 assert_eq!(cb.ctx.call_count, 1);
360 assert_eq!(world.resource::<OrderCache>().expired, vec![99]);
361 }
362
363 fn ctx_three_params_handler(
364 ctx: &mut TimerCtx,
365 a: Res<u64>,
366 b: Res<bool>,
367 mut c: ResMut<OrderCache>,
368 _event: u32,
369 ) {
370 if *b {
371 c.expired.push(*a);
372 }
373 ctx.call_count += 1;
374 }
375
376 #[test]
377 fn ctx_three_params() {
378 let mut builder = WorldBuilder::new();
379 builder.register::<u64>(7);
380 builder.register::<bool>(true);
381 builder.register::<OrderCache>(OrderCache { expired: vec![] });
382 let mut world = builder.build();
383
384 let mut cb = ctx_three_params_handler.into_callback(
385 TimerCtx {
386 order_id: 0,
387 call_count: 0,
388 },
389 world.registry_mut(),
390 );
391 cb.run(&mut world, 0u32);
392 assert_eq!(cb.ctx.call_count, 1);
393 assert_eq!(world.resource::<OrderCache>().expired, vec![7]);
394 }
395
396 #[test]
399 fn ctx_mutated_persists() {
400 let mut world = WorldBuilder::new().build();
401 let mut cb = ctx_only_handler.into_callback(
402 TimerCtx {
403 order_id: 1,
404 call_count: 0,
405 },
406 world.registry_mut(),
407 );
408 cb.run(&mut world, 0u32);
409 cb.run(&mut world, 0u32);
410 cb.run(&mut world, 0u32);
411 assert_eq!(cb.ctx.call_count, 3);
412 }
413
414 #[test]
415 fn ctx_accessible_outside_dispatch() {
416 let mut world = WorldBuilder::new().build();
417 let mut cb = ctx_only_handler.into_callback(
418 TimerCtx {
419 order_id: 42,
420 call_count: 0,
421 },
422 world.registry_mut(),
423 );
424 assert_eq!(cb.ctx.order_id, 42);
425 assert_eq!(cb.ctx.call_count, 0);
426 cb.run(&mut world, 0u32);
427 assert_eq!(cb.ctx.call_count, 1);
428 }
429
430 #[test]
431 fn ctx_mutated_outside_dispatch() {
432 let mut world = WorldBuilder::new().build();
433 let mut cb = ctx_only_handler.into_callback(
434 TimerCtx {
435 order_id: 1,
436 call_count: 0,
437 },
438 world.registry_mut(),
439 );
440 cb.ctx.order_id = 99;
441 cb.run(&mut world, 0u32);
442 assert_eq!(cb.ctx.order_id, 99);
443 assert_eq!(cb.ctx.call_count, 1);
444 }
445
446 #[test]
449 #[should_panic(expected = "not registered")]
450 fn panics_on_missing_resource() {
451 let mut world = WorldBuilder::new().build();
452
453 fn needs_cache(_ctx: &mut TimerCtx, _cache: Res<OrderCache>, _e: u32) {}
454
455 let _cb = needs_cache.into_callback(
456 TimerCtx {
457 order_id: 0,
458 call_count: 0,
459 },
460 world.registry_mut(),
461 );
462 }
463
464 #[test]
465 #[should_panic(expected = "not registered")]
466 fn panics_on_second_missing() {
467 let mut builder = WorldBuilder::new();
468 builder.register::<u64>(0);
469 let mut world = builder.build();
470
471 fn needs_two(_ctx: &mut TimerCtx, _a: Res<u64>, _b: Res<OrderCache>, _e: u32) {}
472
473 let _cb = needs_two.into_callback(
474 TimerCtx {
475 order_id: 0,
476 call_count: 0,
477 },
478 world.registry_mut(),
479 );
480 }
481
482 #[test]
485 #[should_panic(expected = "conflicting access")]
486 fn callback_duplicate_access_panics() {
487 let mut builder = WorldBuilder::new();
488 builder.register::<u64>(0);
489 let mut world = builder.build();
490
491 fn bad(_ctx: &mut u64, a: Res<u64>, b: ResMut<u64>, _e: ()) {
492 let _ = (*a, &*b);
493 }
494
495 let _cb = bad.into_callback(0u64, world.registry_mut());
496 }
497
498 fn stamps_writer(_ctx: &mut u64, mut val: ResMut<u64>, _e: ()) {
501 *val = 99;
502 }
503
504 #[test]
505 fn mut_stamps_changed_at() {
506 let mut builder = WorldBuilder::new();
507 builder.register::<u64>(0);
508 let mut world = builder.build();
509
510 let mut cb = stamps_writer.into_callback(0u64, world.registry_mut());
511
512 world.next_sequence(); let id = world.id::<u64>();
514 unsafe {
515 assert_eq!(world.changed_at(id), crate::Sequence(0));
516 }
517
518 cb.run(&mut world, ());
519 unsafe {
520 assert_eq!(world.changed_at(id), crate::Sequence(1));
521 }
522 }
523
524 #[test]
527 fn box_dyn_handler() {
528 let mut builder = WorldBuilder::new();
529 builder.register::<u64>(0);
530 let mut world = builder.build();
531
532 fn add_ctx(ctx: &mut u64, mut val: ResMut<u64>, event: u64) {
533 *val += event + *ctx;
534 }
535
536 let cb = add_ctx.into_callback(10u64, world.registry_mut());
537 let mut boxed: Box<dyn Handler<u64>> = Box::new(cb);
538 boxed.run(&mut world, 5u64);
539 assert_eq!(*world.resource::<u64>(), 15);
541 }
542
543 #[test]
544 fn callback_in_vec_dyn() {
545 let mut builder = WorldBuilder::new();
546 builder.register::<u64>(0);
547 let mut world = builder.build();
548
549 fn add(ctx: &mut u64, mut val: ResMut<u64>, _e: ()) {
550 *val += *ctx;
551 }
552 fn mul(ctx: &mut u64, mut val: ResMut<u64>, _e: ()) {
553 *val *= *ctx;
554 }
555
556 let cb_add = add.into_callback(3u64, world.registry_mut());
557 let cb_mul = mul.into_callback(2u64, world.registry_mut());
558
559 let mut handlers: Vec<Box<dyn Handler<()>>> = vec![Box::new(cb_add), Box::new(cb_mul)];
560
561 for h in handlers.iter_mut() {
562 h.run(&mut world, ());
563 }
564 assert_eq!(*world.resource::<u64>(), 6);
566 }
567
568 fn with_local(_ctx: &mut u64, mut local: Local<u64>, mut val: ResMut<u64>, _e: ()) {
569 *local += 1;
570 *val = *local;
571 }
572
573 #[test]
574 fn callback_with_local() {
575 let mut builder = WorldBuilder::new();
576 builder.register::<u64>(0);
577 let mut world = builder.build();
578
579 let mut cb = with_local.into_callback(0u64, world.registry_mut());
580 cb.run(&mut world, ());
581 cb.run(&mut world, ());
582 cb.run(&mut world, ());
583 assert_eq!(*world.resource::<u64>(), 3);
584 }
585
586 #[test]
589 fn callback_interop_with_handler() {
590 use crate::IntoHandler;
591
592 let mut builder = WorldBuilder::new();
593 builder.register::<u64>(0);
594 let mut world = builder.build();
595
596 fn sys_add(mut val: ResMut<u64>, event: u64) {
597 *val += event;
598 }
599 fn cb_mul(ctx: &mut u64, mut val: ResMut<u64>, _e: u64) {
600 *val *= *ctx;
601 }
602
603 let sys = sys_add.into_handler(world.registry_mut());
604 let cb = cb_mul.into_callback(3u64, world.registry_mut());
605
606 let mut handlers: Vec<Box<dyn Handler<u64>>> = vec![Box::new(sys), Box::new(cb)];
607
608 for h in handlers.iter_mut() {
609 h.run(&mut world, 5u64);
610 }
611 assert_eq!(*world.resource::<u64>(), 15);
613 }
614}