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