1use crate::Handler;
49use crate::handler::Param;
50use crate::world::{Registry, World};
51
52pub struct Callback<C, F, Params: Param> {
92 pub ctx: C,
94 pub(crate) f: F,
95 pub(crate) state: Params::State,
96 pub(crate) name: &'static str,
97}
98
99#[diagnostic::on_unimplemented(
153 message = "this function cannot be converted into a callback",
154 note = "callback signature: `fn(&mut Context, Res<A>, ..., Event)` — context first, then resources, event last",
155 note = "closures with resource parameters are not supported — use a named `fn` when using Param resources"
156)]
157pub trait IntoCallback<C, E, Params> {
158 type Callback: Handler<E>;
160
161 #[must_use = "the callback must be stored or dispatched — discarding it does nothing"]
163 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback;
164}
165
166impl<C: Send + 'static, E, F: FnMut(&mut C, E) + Send + 'static> IntoCallback<C, E, ()> for F {
171 type Callback = Callback<C, F, ()>;
172
173 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback {
174 Callback {
175 ctx,
176 f: self,
177 state: <() as Param>::init(registry),
178 name: std::any::type_name::<F>(),
179 }
180 }
181}
182
183impl<C: Send + 'static, E, F: FnMut(&mut C, E) + Send + 'static> Handler<E> for Callback<C, F, ()> {
184 fn run(&mut self, _world: &mut World, event: E) {
185 (self.f)(&mut self.ctx, event);
186 }
187
188 fn name(&self) -> &'static str {
189 self.name
190 }
191}
192
193macro_rules! impl_into_callback {
198 ($($P:ident),+) => {
199 impl<C: Send + 'static, E, F: Send + 'static, $($P: Param + 'static),+>
200 IntoCallback<C, E, ($($P,)+)> for F
201 where
202 for<'a> &'a mut F:
203 FnMut(&mut C, $($P,)+ E) +
204 FnMut(&mut C, $($P::Item<'a>,)+ E),
205 {
206 type Callback = Callback<C, F, ($($P,)+)>;
207
208 fn into_callback(self, ctx: C, registry: &Registry) -> Self::Callback {
209 let state = <($($P,)+) as Param>::init(registry);
210 {
211 #[allow(non_snake_case)]
212 let ($($P,)+) = &state;
213 registry.check_access(&[
214 $(
215 (<$P as Param>::resource_id($P),
216 std::any::type_name::<$P>()),
217 )+
218 ]);
219 }
220 Callback { ctx, f: self, state, name: std::any::type_name::<F>() }
221 }
222 }
223
224 impl<C: Send + 'static, E, F: Send + 'static, $($P: Param + 'static),+>
225 Handler<E> for Callback<C, F, ($($P,)+)>
226 where
227 for<'a> &'a mut F:
228 FnMut(&mut C, $($P,)+ E) +
229 FnMut(&mut C, $($P::Item<'a>,)+ E),
230 {
231 #[allow(non_snake_case)]
232 fn run(&mut self, world: &mut World, event: E) {
233 #[allow(clippy::too_many_arguments)]
234 fn call_inner<Ctx, $($P,)+ Ev>(
235 mut f: impl FnMut(&mut Ctx, $($P,)+ Ev),
236 ctx: &mut Ctx,
237 $($P: $P,)+
238 event: Ev,
239 ) {
240 f(ctx, $($P,)+ event);
241 }
242
243 #[cfg(debug_assertions)]
247 world.clear_borrows();
248 let ($($P,)+) = unsafe {
249 <($($P,)+) as Param>::fetch(world, &mut self.state)
250 };
251 call_inner(&mut self.f, &mut self.ctx, $($P,)+ event);
252 }
253
254 fn name(&self) -> &'static str {
255 self.name
256 }
257 }
258 };
259}
260
261all_tuples!(impl_into_callback);
262
263#[cfg(test)]
268mod tests {
269 use super::*;
270 use crate::{Local, Res, ResMut, WorldBuilder};
271
272 struct TimerCtx {
275 order_id: u64,
276 call_count: u64,
277 }
278
279 struct OrderCache {
280 expired: Vec<u64>,
281 }
282 impl crate::world::Resource for OrderCache {}
283
284 fn ctx_only_handler(ctx: &mut TimerCtx, _event: u32) {
287 ctx.call_count += 1;
288 }
289
290 #[test]
291 fn ctx_only_no_params() {
292 let mut world = WorldBuilder::new().build();
293 let mut cb = ctx_only_handler.into_callback(
294 TimerCtx {
295 order_id: 1,
296 call_count: 0,
297 },
298 world.registry_mut(),
299 );
300 cb.run(&mut world, 42u32);
301 assert_eq!(cb.ctx.call_count, 1);
302 }
303
304 fn ctx_one_res_handler(ctx: &mut TimerCtx, cache: Res<OrderCache>, _event: u32) {
305 ctx.call_count += cache.expired.len() as u64;
306 }
307
308 #[test]
309 fn ctx_one_res() {
310 let mut builder = WorldBuilder::new();
311 builder.register::<OrderCache>(OrderCache {
312 expired: vec![1, 2, 3],
313 });
314 let mut world = builder.build();
315
316 let mut cb = ctx_one_res_handler.into_callback(
317 TimerCtx {
318 order_id: 1,
319 call_count: 0,
320 },
321 world.registry_mut(),
322 );
323 cb.run(&mut world, 0u32);
324 assert_eq!(cb.ctx.call_count, 3);
325 }
326
327 fn ctx_one_res_mut_handler(ctx: &mut TimerCtx, mut cache: ResMut<OrderCache>, _event: u32) {
328 cache.expired.push(ctx.order_id);
329 ctx.call_count += 1;
330 }
331
332 #[test]
333 fn ctx_one_res_mut() {
334 let mut builder = WorldBuilder::new();
335 builder.register::<OrderCache>(OrderCache { expired: vec![] });
336 let mut world = builder.build();
337
338 let mut cb = ctx_one_res_mut_handler.into_callback(
339 TimerCtx {
340 order_id: 42,
341 call_count: 0,
342 },
343 world.registry_mut(),
344 );
345 cb.run(&mut world, 0u32);
346 assert_eq!(cb.ctx.call_count, 1);
347 assert_eq!(world.resource::<OrderCache>().expired, vec![42]);
348 }
349
350 fn ctx_two_params_handler(
351 ctx: &mut TimerCtx,
352 counter: Res<u64>,
353 mut cache: ResMut<OrderCache>,
354 _event: u32,
355 ) {
356 cache.expired.push(*counter);
357 ctx.call_count += 1;
358 }
359
360 #[test]
361 fn ctx_two_params() {
362 let mut builder = WorldBuilder::new();
363 builder.register::<u64>(99);
364 builder.register::<OrderCache>(OrderCache { expired: vec![] });
365 let mut world = builder.build();
366
367 let mut cb = ctx_two_params_handler.into_callback(
368 TimerCtx {
369 order_id: 0,
370 call_count: 0,
371 },
372 world.registry_mut(),
373 );
374 cb.run(&mut world, 0u32);
375 assert_eq!(cb.ctx.call_count, 1);
376 assert_eq!(world.resource::<OrderCache>().expired, vec![99]);
377 }
378
379 fn ctx_three_params_handler(
380 ctx: &mut TimerCtx,
381 a: Res<u64>,
382 b: Res<bool>,
383 mut c: ResMut<OrderCache>,
384 _event: u32,
385 ) {
386 if *b {
387 c.expired.push(*a);
388 }
389 ctx.call_count += 1;
390 }
391
392 #[test]
393 fn ctx_three_params() {
394 let mut builder = WorldBuilder::new();
395 builder.register::<u64>(7);
396 builder.register::<bool>(true);
397 builder.register::<OrderCache>(OrderCache { expired: vec![] });
398 let mut world = builder.build();
399
400 let mut cb = ctx_three_params_handler.into_callback(
401 TimerCtx {
402 order_id: 0,
403 call_count: 0,
404 },
405 world.registry_mut(),
406 );
407 cb.run(&mut world, 0u32);
408 assert_eq!(cb.ctx.call_count, 1);
409 assert_eq!(world.resource::<OrderCache>().expired, vec![7]);
410 }
411
412 #[test]
415 fn ctx_mutated_persists() {
416 let mut world = WorldBuilder::new().build();
417 let mut cb = ctx_only_handler.into_callback(
418 TimerCtx {
419 order_id: 1,
420 call_count: 0,
421 },
422 world.registry_mut(),
423 );
424 cb.run(&mut world, 0u32);
425 cb.run(&mut world, 0u32);
426 cb.run(&mut world, 0u32);
427 assert_eq!(cb.ctx.call_count, 3);
428 }
429
430 #[test]
431 fn ctx_accessible_outside_dispatch() {
432 let mut world = WorldBuilder::new().build();
433 let mut cb = ctx_only_handler.into_callback(
434 TimerCtx {
435 order_id: 42,
436 call_count: 0,
437 },
438 world.registry_mut(),
439 );
440 assert_eq!(cb.ctx.order_id, 42);
441 assert_eq!(cb.ctx.call_count, 0);
442 cb.run(&mut world, 0u32);
443 assert_eq!(cb.ctx.call_count, 1);
444 }
445
446 #[test]
447 fn ctx_mutated_outside_dispatch() {
448 let mut world = WorldBuilder::new().build();
449 let mut cb = ctx_only_handler.into_callback(
450 TimerCtx {
451 order_id: 1,
452 call_count: 0,
453 },
454 world.registry_mut(),
455 );
456 cb.ctx.order_id = 99;
457 cb.run(&mut world, 0u32);
458 assert_eq!(cb.ctx.order_id, 99);
459 assert_eq!(cb.ctx.call_count, 1);
460 }
461
462 #[test]
465 #[should_panic(expected = "not registered")]
466 fn panics_on_missing_resource() {
467 let mut world = WorldBuilder::new().build();
468
469 fn needs_cache(_ctx: &mut TimerCtx, _cache: Res<OrderCache>, _e: u32) {}
470
471 let _cb = needs_cache.into_callback(
472 TimerCtx {
473 order_id: 0,
474 call_count: 0,
475 },
476 world.registry_mut(),
477 );
478 }
479
480 #[test]
481 #[should_panic(expected = "not registered")]
482 fn panics_on_second_missing() {
483 let mut builder = WorldBuilder::new();
484 builder.register::<u64>(0);
485 let mut world = builder.build();
486
487 fn needs_two(_ctx: &mut TimerCtx, _a: Res<u64>, _b: Res<OrderCache>, _e: u32) {}
488
489 let _cb = needs_two.into_callback(
490 TimerCtx {
491 order_id: 0,
492 call_count: 0,
493 },
494 world.registry_mut(),
495 );
496 }
497
498 #[test]
501 #[should_panic(expected = "conflicting access")]
502 fn callback_duplicate_access_panics() {
503 let mut builder = WorldBuilder::new();
504 builder.register::<u64>(0);
505 let mut world = builder.build();
506
507 fn bad(_ctx: &mut u64, a: Res<u64>, b: ResMut<u64>, _e: ()) {
508 let _ = (*a, &*b);
509 }
510
511 let _cb = bad.into_callback(0u64, world.registry_mut());
512 }
513
514 #[test]
517 fn box_dyn_handler() {
518 let mut builder = WorldBuilder::new();
519 builder.register::<u64>(0);
520 let mut world = builder.build();
521
522 fn add_ctx(ctx: &mut u64, mut val: ResMut<u64>, event: u64) {
523 *val += event + *ctx;
524 }
525
526 let cb = add_ctx.into_callback(10u64, world.registry_mut());
527 let mut boxed: Box<dyn Handler<u64>> = Box::new(cb);
528 boxed.run(&mut world, 5u64);
529 assert_eq!(*world.resource::<u64>(), 15);
531 }
532
533 #[test]
534 fn callback_in_vec_dyn() {
535 let mut builder = WorldBuilder::new();
536 builder.register::<u64>(0);
537 let mut world = builder.build();
538
539 fn add(ctx: &mut u64, mut val: ResMut<u64>, _e: ()) {
540 *val += *ctx;
541 }
542 fn mul(ctx: &mut u64, mut val: ResMut<u64>, _e: ()) {
543 *val *= *ctx;
544 }
545
546 let cb_add = add.into_callback(3u64, world.registry_mut());
547 let cb_mul = mul.into_callback(2u64, world.registry_mut());
548
549 let mut handlers: Vec<Box<dyn Handler<()>>> = vec![Box::new(cb_add), Box::new(cb_mul)];
550
551 for h in &mut handlers {
552 h.run(&mut world, ());
553 }
554 assert_eq!(*world.resource::<u64>(), 6);
556 }
557
558 fn with_local(_ctx: &mut u64, mut local: Local<u64>, mut val: ResMut<u64>, _e: ()) {
559 *local += 1;
560 *val = *local;
561 }
562
563 #[test]
564 fn callback_with_local() {
565 let mut builder = WorldBuilder::new();
566 builder.register::<u64>(0);
567 let mut world = builder.build();
568
569 let mut cb = with_local.into_callback(0u64, world.registry_mut());
570 cb.run(&mut world, ());
571 cb.run(&mut world, ());
572 cb.run(&mut world, ());
573 assert_eq!(*world.resource::<u64>(), 3);
574 }
575
576 #[test]
579 fn callback_interop_with_handler() {
580 use crate::IntoHandler;
581
582 let mut builder = WorldBuilder::new();
583 builder.register::<u64>(0);
584 let mut world = builder.build();
585
586 fn sys_add(mut val: ResMut<u64>, event: u64) {
587 *val += event;
588 }
589 fn cb_mul(ctx: &mut u64, mut val: ResMut<u64>, _e: u64) {
590 *val *= *ctx;
591 }
592
593 let sys = sys_add.into_handler(world.registry_mut());
594 let cb = cb_mul.into_callback(3u64, world.registry_mut());
595
596 let mut handlers: Vec<Box<dyn Handler<u64>>> = vec![Box::new(sys), Box::new(cb)];
597
598 for h in &mut handlers {
599 h.run(&mut world, 5u64);
600 }
601 assert_eq!(*world.resource::<u64>(), 15);
603 }
604}