1#![allow(clippy::too_many_arguments)]
53
54use crate::Handler;
55use crate::handler::Param;
56use crate::world::{Registry, World};
57
58pub struct Callback<C, F, Params: Param> {
98 pub ctx: C,
100 pub(crate) f: F,
101 pub(crate) state: Params::State,
102 pub(crate) name: &'static str,
103}
104
105#[diagnostic::on_unimplemented(
159 message = "this function cannot be converted into a callback",
160 note = "callback signature: `fn(&mut Context, Res<A>, ..., Event)` — context first, then resources, event last",
161 note = "for Handler<()> with params, wrap with `no_event(fn_name)` to omit the event parameter",
162 note = "closures with resource parameters are not supported — use a named `fn` when using Param resources"
163)]
164pub trait IntoCallback<C, E, Params> {
165 type Callback: Handler<E>;
167
168 #[must_use = "the callback must be stored or dispatched — discarding it does nothing"]
170 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback;
171}
172
173impl<C: Send + 'static, E, F: FnMut(&mut C, E) + Send + 'static> IntoCallback<C, E, ()> for F {
178 type Callback = Callback<C, F, ()>;
179
180 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback {
181 Callback {
182 ctx,
183 f: self,
184 state: <() as Param>::init(registry),
185 name: std::any::type_name::<F>(),
186 }
187 }
188}
189
190impl<C: Send + 'static, E, F: FnMut(&mut C, E) + Send + 'static> Handler<E> for Callback<C, F, ()> {
191 fn run(&mut self, _world: &mut World, event: E) {
192 (self.f)(&mut self.ctx, event);
193 }
194
195 fn name(&self) -> &'static str {
196 self.name
197 }
198}
199
200macro_rules! impl_into_callback {
205 ($($P:ident),+) => {
206 impl<C: Send + 'static, E, F: Send + 'static, $($P: Param + 'static),+>
207 IntoCallback<C, E, ($($P,)+)> for F
208 where
209 for<'a> &'a mut F:
210 FnMut(&mut C, $($P,)+ E) +
211 FnMut(&mut C, $($P::Item<'a>,)+ E),
212 {
213 type Callback = Callback<C, F, ($($P,)+)>;
214
215 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback {
216 let state = <($($P,)+) as Param>::init(registry);
217 {
218 #[allow(non_snake_case)]
219 let ($($P,)+) = &state;
220 registry.check_access(&[
221 $(
222 (<$P as Param>::resource_id($P),
223 std::any::type_name::<$P>()),
224 )+
225 ]);
226 }
227 Callback { ctx, f: self, state, name: std::any::type_name::<F>() }
228 }
229 }
230
231 impl<C: Send + 'static, E, F: Send + 'static, $($P: Param + 'static),+>
232 Handler<E> for Callback<C, F, ($($P,)+)>
233 where
234 for<'a> &'a mut F:
235 FnMut(&mut C, $($P,)+ E) +
236 FnMut(&mut C, $($P::Item<'a>,)+ E),
237 {
238 #[allow(non_snake_case)]
239 fn run(&mut self, world: &mut World, event: E) {
240 fn call_inner<Ctx, $($P,)+ Ev>(
241 mut f: impl FnMut(&mut Ctx, $($P,)+ Ev),
242 ctx: &mut Ctx,
243 $($P: $P,)+
244 event: Ev,
245 ) {
246 f(ctx, $($P,)+ event);
247 }
248
249 #[cfg(debug_assertions)]
253 world.clear_borrows();
254 let ($($P,)+) = unsafe {
255 <($($P,)+) as Param>::fetch(world, &mut self.state)
256 };
257 call_inner(&mut self.f, &mut self.ctx, $($P,)+ event);
258 }
259
260 fn name(&self) -> &'static str {
261 self.name
262 }
263 }
264 };
265}
266
267all_tuples!(impl_into_callback);
268
269use crate::handler::NoEvent;
274
275impl<C: Send + 'static, F: FnMut(&mut C) + Send + 'static> IntoCallback<C, (), NoEvent<F>> for F {
277 type Callback = Callback<C, NoEvent<F>, ()>;
278
279 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback {
280 Callback {
281 ctx,
282 f: NoEvent(self),
283 state: <() as Param>::init(registry),
284 name: std::any::type_name::<F>(),
285 }
286 }
287}
288
289impl<C: Send + 'static, F: FnMut(&mut C) + Send + 'static> Handler<()>
290 for Callback<C, NoEvent<F>, ()>
291{
292 fn run(&mut self, _world: &mut World, _event: ()) {
293 (self.f.0)(&mut self.ctx);
294 }
295
296 fn name(&self) -> &'static str {
297 self.name
298 }
299}
300
301macro_rules! impl_into_callback_no_event {
302 ($($P:ident),+) => {
303 impl<C: Send + 'static, F: Send + 'static, $($P: Param + 'static),+>
304 IntoCallback<C, (), ($($P,)+)> for NoEvent<F>
305 where
306 for<'a> &'a mut F:
307 FnMut(&mut C, $($P,)+) +
308 FnMut(&mut C, $($P::Item<'a>,)+),
309 {
310 type Callback = Callback<C, NoEvent<F>, ($($P,)+)>;
311
312 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback {
313 let state = <($($P,)+) as Param>::init(registry);
314 {
315 #[allow(non_snake_case)]
316 let ($($P,)+) = &state;
317 registry.check_access(&[
318 $(
319 (<$P as Param>::resource_id($P),
320 std::any::type_name::<$P>()),
321 )+
322 ]);
323 }
324 Callback { ctx, f: self, state, name: std::any::type_name::<F>() }
325 }
326 }
327
328 impl<C: Send + 'static, F: Send + 'static, $($P: Param + 'static),+>
329 Handler<()> for Callback<C, NoEvent<F>, ($($P,)+)>
330 where
331 for<'a> &'a mut F:
332 FnMut(&mut C, $($P,)+) +
333 FnMut(&mut C, $($P::Item<'a>,)+),
334 {
335 #[allow(non_snake_case)]
336 fn run(&mut self, world: &mut World, _event: ()) {
337 fn call_inner<Ctx, $($P),+>(
338 mut f: impl FnMut(&mut Ctx, $($P),+),
339 ctx: &mut Ctx,
340 $($P: $P,)+
341 ) {
342 f(ctx, $($P),+);
343 }
344
345 #[cfg(debug_assertions)]
346 world.clear_borrows();
347 let ($($P,)+) = unsafe {
348 <($($P,)+) as Param>::fetch(world, &mut self.state)
349 };
350 call_inner(&mut self.f.0, &mut self.ctx, $($P,)+);
351 }
352
353 fn name(&self) -> &'static str {
354 self.name
355 }
356 }
357 };
358}
359
360all_tuples!(impl_into_callback_no_event);
361
362#[cfg(test)]
367mod tests {
368 use super::*;
369 use crate::{Local, Res, ResMut, WorldBuilder, no_event};
370
371 struct TimerCtx {
374 order_id: u64,
375 call_count: u64,
376 }
377
378 struct OrderCache {
379 expired: Vec<u64>,
380 }
381 impl crate::world::Resource for OrderCache {}
382
383 fn ctx_only_handler(ctx: &mut TimerCtx, _event: u32) {
386 ctx.call_count += 1;
387 }
388
389 #[test]
390 fn ctx_only_no_params() {
391 let mut world = WorldBuilder::new().build();
392 let mut cb = ctx_only_handler.into_callback(
393 TimerCtx {
394 order_id: 1,
395 call_count: 0,
396 },
397 world.registry_mut(),
398 );
399 cb.run(&mut world, 42u32);
400 assert_eq!(cb.ctx.call_count, 1);
401 }
402
403 fn ctx_one_res_handler(ctx: &mut TimerCtx, cache: Res<OrderCache>, _event: u32) {
404 ctx.call_count += cache.expired.len() as u64;
405 }
406
407 #[test]
408 fn ctx_one_res() {
409 let mut builder = WorldBuilder::new();
410 builder.register::<OrderCache>(OrderCache {
411 expired: vec![1, 2, 3],
412 });
413 let mut world = builder.build();
414
415 let mut cb = ctx_one_res_handler.into_callback(
416 TimerCtx {
417 order_id: 1,
418 call_count: 0,
419 },
420 world.registry_mut(),
421 );
422 cb.run(&mut world, 0u32);
423 assert_eq!(cb.ctx.call_count, 3);
424 }
425
426 fn ctx_one_res_mut_handler(ctx: &mut TimerCtx, mut cache: ResMut<OrderCache>, _event: u32) {
427 cache.expired.push(ctx.order_id);
428 ctx.call_count += 1;
429 }
430
431 #[test]
432 fn ctx_one_res_mut() {
433 let mut builder = WorldBuilder::new();
434 builder.register::<OrderCache>(OrderCache { expired: vec![] });
435 let mut world = builder.build();
436
437 let mut cb = ctx_one_res_mut_handler.into_callback(
438 TimerCtx {
439 order_id: 42,
440 call_count: 0,
441 },
442 world.registry_mut(),
443 );
444 cb.run(&mut world, 0u32);
445 assert_eq!(cb.ctx.call_count, 1);
446 assert_eq!(world.resource::<OrderCache>().expired, vec![42]);
447 }
448
449 fn ctx_two_params_handler(
450 ctx: &mut TimerCtx,
451 counter: Res<u64>,
452 mut cache: ResMut<OrderCache>,
453 _event: u32,
454 ) {
455 cache.expired.push(*counter);
456 ctx.call_count += 1;
457 }
458
459 #[test]
460 fn ctx_two_params() {
461 let mut builder = WorldBuilder::new();
462 builder.register::<u64>(99);
463 builder.register::<OrderCache>(OrderCache { expired: vec![] });
464 let mut world = builder.build();
465
466 let mut cb = ctx_two_params_handler.into_callback(
467 TimerCtx {
468 order_id: 0,
469 call_count: 0,
470 },
471 world.registry_mut(),
472 );
473 cb.run(&mut world, 0u32);
474 assert_eq!(cb.ctx.call_count, 1);
475 assert_eq!(world.resource::<OrderCache>().expired, vec![99]);
476 }
477
478 fn ctx_three_params_handler(
479 ctx: &mut TimerCtx,
480 a: Res<u64>,
481 b: Res<bool>,
482 mut c: ResMut<OrderCache>,
483 _event: u32,
484 ) {
485 if *b {
486 c.expired.push(*a);
487 }
488 ctx.call_count += 1;
489 }
490
491 #[test]
492 fn ctx_three_params() {
493 let mut builder = WorldBuilder::new();
494 builder.register::<u64>(7);
495 builder.register::<bool>(true);
496 builder.register::<OrderCache>(OrderCache { expired: vec![] });
497 let mut world = builder.build();
498
499 let mut cb = ctx_three_params_handler.into_callback(
500 TimerCtx {
501 order_id: 0,
502 call_count: 0,
503 },
504 world.registry_mut(),
505 );
506 cb.run(&mut world, 0u32);
507 assert_eq!(cb.ctx.call_count, 1);
508 assert_eq!(world.resource::<OrderCache>().expired, vec![7]);
509 }
510
511 #[test]
514 fn ctx_mutated_persists() {
515 let mut world = WorldBuilder::new().build();
516 let mut cb = ctx_only_handler.into_callback(
517 TimerCtx {
518 order_id: 1,
519 call_count: 0,
520 },
521 world.registry_mut(),
522 );
523 cb.run(&mut world, 0u32);
524 cb.run(&mut world, 0u32);
525 cb.run(&mut world, 0u32);
526 assert_eq!(cb.ctx.call_count, 3);
527 }
528
529 #[test]
530 fn ctx_accessible_outside_dispatch() {
531 let mut world = WorldBuilder::new().build();
532 let mut cb = ctx_only_handler.into_callback(
533 TimerCtx {
534 order_id: 42,
535 call_count: 0,
536 },
537 world.registry_mut(),
538 );
539 assert_eq!(cb.ctx.order_id, 42);
540 assert_eq!(cb.ctx.call_count, 0);
541 cb.run(&mut world, 0u32);
542 assert_eq!(cb.ctx.call_count, 1);
543 }
544
545 #[test]
546 fn ctx_mutated_outside_dispatch() {
547 let mut world = WorldBuilder::new().build();
548 let mut cb = ctx_only_handler.into_callback(
549 TimerCtx {
550 order_id: 1,
551 call_count: 0,
552 },
553 world.registry_mut(),
554 );
555 cb.ctx.order_id = 99;
556 cb.run(&mut world, 0u32);
557 assert_eq!(cb.ctx.order_id, 99);
558 assert_eq!(cb.ctx.call_count, 1);
559 }
560
561 #[test]
564 #[should_panic(expected = "not registered")]
565 fn panics_on_missing_resource() {
566 let mut world = WorldBuilder::new().build();
567
568 fn needs_cache(_ctx: &mut TimerCtx, _cache: Res<OrderCache>, _e: u32) {}
569
570 let _cb = needs_cache.into_callback(
571 TimerCtx {
572 order_id: 0,
573 call_count: 0,
574 },
575 world.registry_mut(),
576 );
577 }
578
579 #[test]
580 #[should_panic(expected = "not registered")]
581 fn panics_on_second_missing() {
582 let mut builder = WorldBuilder::new();
583 builder.register::<u64>(0);
584 let mut world = builder.build();
585
586 fn needs_two(_ctx: &mut TimerCtx, _a: Res<u64>, _b: Res<OrderCache>, _e: u32) {}
587
588 let _cb = needs_two.into_callback(
589 TimerCtx {
590 order_id: 0,
591 call_count: 0,
592 },
593 world.registry_mut(),
594 );
595 }
596
597 #[test]
600 #[should_panic(expected = "conflicting access")]
601 fn callback_duplicate_access_panics() {
602 let mut builder = WorldBuilder::new();
603 builder.register::<u64>(0);
604 let mut world = builder.build();
605
606 fn bad(_ctx: &mut u64, a: Res<u64>, b: ResMut<u64>) {
607 let _ = (*a, &*b);
608 }
609
610 let _cb = no_event(bad).into_callback(0u64, world.registry_mut());
611 }
612
613 #[test]
616 fn box_dyn_handler() {
617 let mut builder = WorldBuilder::new();
618 builder.register::<u64>(0);
619 let mut world = builder.build();
620
621 fn add_ctx(ctx: &mut u64, mut val: ResMut<u64>, event: u64) {
622 *val += event + *ctx;
623 }
624
625 let cb = add_ctx.into_callback(10u64, world.registry_mut());
626 let mut boxed: Box<dyn Handler<u64>> = Box::new(cb);
627 boxed.run(&mut world, 5u64);
628 assert_eq!(*world.resource::<u64>(), 15);
630 }
631
632 #[test]
633 fn callback_in_vec_dyn() {
634 let mut builder = WorldBuilder::new();
635 builder.register::<u64>(0);
636 let mut world = builder.build();
637
638 fn add(ctx: &mut u64, mut val: ResMut<u64>) {
639 *val += *ctx;
640 }
641 fn mul(ctx: &mut u64, mut val: ResMut<u64>) {
642 *val *= *ctx;
643 }
644
645 let cb_add = no_event(add).into_callback(3u64, world.registry_mut());
646 let cb_mul = no_event(mul).into_callback(2u64, world.registry_mut());
647
648 let mut handlers: Vec<Box<dyn Handler<()>>> = vec![Box::new(cb_add), Box::new(cb_mul)];
649
650 for h in &mut handlers {
651 h.run(&mut world, ());
652 }
653 assert_eq!(*world.resource::<u64>(), 6);
655 }
656
657 fn with_local(_ctx: &mut u64, mut local: Local<u64>, mut val: ResMut<u64>) {
658 *local += 1;
659 *val = *local;
660 }
661
662 #[test]
663 fn callback_with_local() {
664 let mut builder = WorldBuilder::new();
665 builder.register::<u64>(0);
666 let mut world = builder.build();
667
668 let mut cb = no_event(with_local).into_callback(0u64, world.registry_mut());
669 cb.run(&mut world, ());
670 cb.run(&mut world, ());
671 cb.run(&mut world, ());
672 assert_eq!(*world.resource::<u64>(), 3);
673 }
674
675 #[test]
678 fn callback_interop_with_handler() {
679 use crate::IntoHandler;
680
681 let mut builder = WorldBuilder::new();
682 builder.register::<u64>(0);
683 let mut world = builder.build();
684
685 fn sys_add(mut val: ResMut<u64>, event: u64) {
686 *val += event;
687 }
688 fn cb_mul(ctx: &mut u64, mut val: ResMut<u64>, _e: u64) {
689 *val *= *ctx;
690 }
691
692 let sys = sys_add.into_handler(world.registry_mut());
693 let cb = cb_mul.into_callback(3u64, world.registry_mut());
694
695 let mut handlers: Vec<Box<dyn Handler<u64>>> = vec![Box::new(sys), Box::new(cb)];
696
697 for h in &mut handlers {
698 h.run(&mut world, 5u64);
699 }
700 assert_eq!(*world.resource::<u64>(), 15);
702 }
703
704 fn no_event_ctx_only(ctx: &mut TimerCtx) {
709 ctx.call_count += 1;
710 }
711
712 #[test]
713 fn no_event_callback_arity_0() {
714 let mut world = WorldBuilder::new().build();
715 let mut cb = no_event_ctx_only.into_callback(
716 TimerCtx {
717 order_id: 1,
718 call_count: 0,
719 },
720 world.registry_mut(),
721 );
722 cb.run(&mut world, ());
723 cb.run(&mut world, ());
724 assert_eq!(cb.ctx.call_count, 2);
725 }
726
727 fn no_event_ctx_one_res(ctx: &mut TimerCtx, mut val: ResMut<u64>) {
728 *val += ctx.order_id;
729 ctx.call_count += 1;
730 }
731
732 #[test]
733 fn no_event_callback_arity_1() {
734 use crate::no_event;
735
736 let mut builder = WorldBuilder::new();
737 builder.register::<u64>(0);
738 let mut world = builder.build();
739
740 let mut cb = no_event(no_event_ctx_one_res).into_callback(
741 TimerCtx {
742 order_id: 42,
743 call_count: 0,
744 },
745 world.registry_mut(),
746 );
747 cb.run(&mut world, ());
748 assert_eq!(cb.ctx.call_count, 1);
749 assert_eq!(*world.resource::<u64>(), 42);
750 }
751
752 fn no_event_ctx_two_params(ctx: &mut TimerCtx, src: Res<u32>, mut dst: ResMut<u64>) {
753 *dst += *src as u64 + ctx.order_id;
754 ctx.call_count += 1;
755 }
756
757 #[test]
758 fn no_event_callback_arity_2() {
759 use crate::no_event;
760
761 let mut builder = WorldBuilder::new();
762 builder.register::<u32>(10);
763 builder.register::<u64>(0);
764 let mut world = builder.build();
765
766 let mut cb = no_event(no_event_ctx_two_params).into_callback(
767 TimerCtx {
768 order_id: 5,
769 call_count: 0,
770 },
771 world.registry_mut(),
772 );
773 cb.run(&mut world, ());
774 assert_eq!(cb.ctx.call_count, 1);
775 assert_eq!(*world.resource::<u64>(), 15); }
777
778 #[test]
779 fn no_event_callback_boxed() {
780 use crate::no_event;
781
782 let mut builder = WorldBuilder::new();
783 builder.register::<u64>(0);
784 let mut world = builder.build();
785
786 let cb = no_event(no_event_ctx_one_res).into_callback(
787 TimerCtx {
788 order_id: 7,
789 call_count: 0,
790 },
791 world.registry_mut(),
792 );
793 let mut boxed: Box<dyn Handler<()>> = Box::new(cb);
794 boxed.run(&mut world, ());
795 assert_eq!(*world.resource::<u64>(), 7);
796 }
797}