1#![allow(clippy::too_many_arguments)]
67
68use std::marker::PhantomData;
69
70use crate::Handler;
71use crate::handler::Param;
72use crate::world::{Registry, World};
73
74pub trait Blueprint {
104 type Event;
106 type Params: Param + 'static;
108}
109
110pub trait CallbackBlueprint: Blueprint {
115 type Context: Send + 'static;
117}
118
119#[doc(hidden)]
129#[diagnostic::on_unimplemented(
130 message = "function signature doesn't match the blueprint's Event and Params types",
131 note = "the function must accept the blueprint's Params then Event, in that order"
132)]
133pub trait TemplateDispatch<P: Param, E> {
134 fn run_fn_ptr() -> unsafe fn(&mut P::State, &mut World, E);
136 fn validate(state: &P::State, registry: &Registry);
138}
139
140#[doc(hidden)]
142#[diagnostic::on_unimplemented(
143 message = "function signature doesn't match the callback blueprint's Context, Event, and Params types",
144 note = "the function must accept &mut Context first, then Params, then Event"
145)]
146pub trait CallbackTemplateDispatch<C, P: Param, E> {
147 fn run_fn_ptr() -> unsafe fn(&mut C, &mut P::State, &mut World, E);
149 fn validate(state: &P::State, registry: &Registry);
151}
152
153impl<E, F: FnMut(E) + Send + 'static> TemplateDispatch<(), E> for F {
158 fn run_fn_ptr() -> unsafe fn(&mut (), &mut World, E) {
159 unsafe fn run<E, F: FnMut(E) + Send>(_state: &mut (), _world: &mut World, event: E) {
164 let mut f: F = unsafe { std::mem::zeroed() };
167 f(event);
168 }
169 run::<E, F>
170 }
171
172 fn validate(_state: &(), _registry: &Registry) {}
173}
174
175impl<C: Send + 'static, E, F: FnMut(&mut C, E) + Send + 'static> CallbackTemplateDispatch<C, (), E>
176 for F
177{
178 fn run_fn_ptr() -> unsafe fn(&mut C, &mut (), &mut World, E) {
179 unsafe fn run<C: Send, E, F: FnMut(&mut C, E) + Send>(
182 ctx: &mut C,
183 _state: &mut (),
184 _world: &mut World,
185 event: E,
186 ) {
187 let mut f: F = unsafe { std::mem::zeroed() };
189 f(ctx, event);
190 }
191 run::<C, E, F>
192 }
193
194 fn validate(_state: &(), _registry: &Registry) {}
195}
196
197macro_rules! impl_template_dispatch {
202 ($($P:ident),+) => {
203 impl<E, F: Send + 'static, $($P: Param + 'static),+> TemplateDispatch<($($P,)+), E> for F
204 where
205 for<'a> &'a mut F: FnMut($($P,)+ E) + FnMut($($P::Item<'a>,)+ E),
206 {
207 fn run_fn_ptr() -> unsafe fn(&mut ($($P::State,)+), &mut World, E) {
208 #[allow(non_snake_case)]
215 unsafe fn run<E, F: Send + 'static, $($P: Param + 'static),+>(
216 state: &mut ($($P::State,)+),
217 world: &mut World,
218 event: E,
219 ) where
220 for<'a> &'a mut F: FnMut($($P,)+ E) + FnMut($($P::Item<'a>,)+ E),
221 {
222 fn call_inner<$($P,)+ Ev>(
223 mut f: impl FnMut($($P,)+ Ev),
224 $($P: $P,)+
225 event: Ev,
226 ) {
227 f($($P,)+ event);
228 }
229
230 #[cfg(debug_assertions)]
233 world.clear_borrows();
234 let ($($P,)+) = unsafe {
235 <($($P,)+) as Param>::fetch(world, state)
236 };
237 let mut f: F = unsafe { std::mem::zeroed() };
239 call_inner(&mut f, $($P,)+ event);
240 }
241 run::<E, F, $($P),+>
242 }
243
244 #[allow(non_snake_case)]
245 fn validate(state: &($($P::State,)+), registry: &Registry) {
246 let ($($P,)+) = state;
247 registry.check_access(&[
248 $((<$P as Param>::resource_id($P), std::any::type_name::<$P>()),)+
249 ]);
250 }
251 }
252
253 impl<C: Send + 'static, E, F: Send + 'static, $($P: Param + 'static),+>
254 CallbackTemplateDispatch<C, ($($P,)+), E> for F
255 where
256 for<'a> &'a mut F:
257 FnMut(&mut C, $($P,)+ E) +
258 FnMut(&mut C, $($P::Item<'a>,)+ E),
259 {
260 fn run_fn_ptr() -> unsafe fn(&mut C, &mut ($($P::State,)+), &mut World, E) {
261 #[allow(non_snake_case)]
264 unsafe fn run<C: Send, E, F: Send + 'static, $($P: Param + 'static),+>(
265 ctx: &mut C,
266 state: &mut ($($P::State,)+),
267 world: &mut World,
268 event: E,
269 ) where
270 for<'a> &'a mut F:
271 FnMut(&mut C, $($P,)+ E) +
272 FnMut(&mut C, $($P::Item<'a>,)+ E),
273 {
274 fn call_inner<Ctx, $($P,)+ Ev>(
275 mut f: impl FnMut(&mut Ctx, $($P,)+ Ev),
276 ctx: &mut Ctx,
277 $($P: $P,)+
278 event: Ev,
279 ) {
280 f(ctx, $($P,)+ event);
281 }
282
283 #[cfg(debug_assertions)]
287 world.clear_borrows();
288 let ($($P,)+) = unsafe {
289 <($($P,)+) as Param>::fetch(world, state)
290 };
291 let mut f: F = unsafe { std::mem::zeroed() };
293 call_inner(&mut f, ctx, $($P,)+ event);
294 }
295 run::<C, E, F, $($P),+>
296 }
297
298 #[allow(non_snake_case)]
299 fn validate(state: &($($P::State,)+), registry: &Registry) {
300 let ($($P,)+) = state;
301 registry.check_access(&[
302 $((<$P as Param>::resource_id($P), std::any::type_name::<$P>()),)+
303 ]);
304 }
305 }
306 };
307}
308
309all_tuples!(impl_template_dispatch);
310
311use crate::handler::NoEvent;
316
317impl<F: FnMut() + Send + 'static> TemplateDispatch<(), ()> for NoEvent<F> {
319 fn run_fn_ptr() -> unsafe fn(&mut (), &mut World, ()) {
320 unsafe fn run<F: FnMut() + Send>(_state: &mut (), _world: &mut World, _event: ()) {
324 let mut f: F = unsafe { std::mem::zeroed() };
326 f();
327 }
328 run::<F>
329 }
330
331 fn validate(_state: &(), _registry: &Registry) {}
332}
333
334impl<C: Send + 'static, F: FnMut(&mut C) + Send + 'static> CallbackTemplateDispatch<C, (), ()>
335 for NoEvent<F>
336{
337 fn run_fn_ptr() -> unsafe fn(&mut C, &mut (), &mut World, ()) {
338 unsafe fn run<C: Send, F: FnMut(&mut C) + Send>(
342 ctx: &mut C,
343 _state: &mut (),
344 _world: &mut World,
345 _event: (),
346 ) {
347 let mut f: F = unsafe { std::mem::zeroed() };
349 f(ctx);
350 }
351 run::<C, F>
352 }
353
354 fn validate(_state: &(), _registry: &Registry) {}
355}
356
357macro_rules! impl_template_dispatch_no_event {
359 ($($P:ident),+) => {
360 impl<F: Send + 'static, $($P: Param + 'static),+> TemplateDispatch<($($P,)+), ()>
361 for NoEvent<F>
362 where
363 for<'a> &'a mut F: FnMut($($P,)+) + FnMut($($P::Item<'a>,)+),
364 {
365 fn run_fn_ptr() -> unsafe fn(&mut ($($P::State,)+), &mut World, ()) {
366 #[allow(non_snake_case)]
372 unsafe fn run<F: Send + 'static, $($P: Param + 'static),+>(
373 state: &mut ($($P::State,)+),
374 world: &mut World,
375 _event: (),
376 ) where
377 for<'a> &'a mut F: FnMut($($P,)+) + FnMut($($P::Item<'a>,)+),
378 {
379 fn call_inner<$($P),+>(
380 mut f: impl FnMut($($P),+),
381 $($P: $P,)+
382 ) {
383 f($($P),+);
384 }
385
386 #[cfg(debug_assertions)]
387 world.clear_borrows();
388 let ($($P,)+) = unsafe {
389 <($($P,)+) as Param>::fetch(world, state)
390 };
391 let mut f: F = unsafe { std::mem::zeroed() };
393 call_inner(&mut f, $($P,)+);
394 }
395 run::<F, $($P),+>
396 }
397
398 #[allow(non_snake_case)]
399 fn validate(state: &($($P::State,)+), registry: &Registry) {
400 let ($($P,)+) = state;
401 registry.check_access(&[
402 $((<$P as Param>::resource_id($P), std::any::type_name::<$P>()),)+
403 ]);
404 }
405 }
406
407 impl<C: Send + 'static, F: Send + 'static, $($P: Param + 'static),+>
408 CallbackTemplateDispatch<C, ($($P,)+), ()> for NoEvent<F>
409 where
410 for<'a> &'a mut F:
411 FnMut(&mut C, $($P,)+) +
412 FnMut(&mut C, $($P::Item<'a>,)+),
413 {
414 fn run_fn_ptr() -> unsafe fn(&mut C, &mut ($($P::State,)+), &mut World, ()) {
415 #[allow(non_snake_case)]
416 unsafe fn run<C: Send, F: Send + 'static, $($P: Param + 'static),+>(
417 ctx: &mut C,
418 state: &mut ($($P::State,)+),
419 world: &mut World,
420 _event: (),
421 ) where
422 for<'a> &'a mut F:
423 FnMut(&mut C, $($P,)+) +
424 FnMut(&mut C, $($P::Item<'a>,)+),
425 {
426 fn call_inner<Ctx, $($P),+>(
427 mut f: impl FnMut(&mut Ctx, $($P),+),
428 ctx: &mut Ctx,
429 $($P: $P,)+
430 ) {
431 f(ctx, $($P),+);
432 }
433
434 #[cfg(debug_assertions)]
435 world.clear_borrows();
436 let ($($P,)+) = unsafe {
437 <($($P,)+) as Param>::fetch(world, state)
438 };
439 let mut f: F = unsafe { std::mem::zeroed() };
441 call_inner(&mut f, ctx, $($P,)+);
442 }
443 run::<C, F, $($P),+>
444 }
445
446 #[allow(non_snake_case)]
447 fn validate(state: &($($P::State,)+), registry: &Registry) {
448 let ($($P,)+) = state;
449 registry.check_access(&[
450 $((<$P as Param>::resource_id($P), std::any::type_name::<$P>()),)+
451 ]);
452 }
453 }
454 };
455}
456
457all_tuples!(impl_template_dispatch_no_event);
458
459pub struct HandlerTemplate<K: Blueprint>
500where
501 <K::Params as Param>::State: Copy,
502{
503 prototype: <K::Params as Param>::State,
504 run_fn: unsafe fn(&mut <K::Params as Param>::State, &mut World, K::Event),
505 name: &'static str,
506 _key: PhantomData<fn() -> K>,
508}
509
510impl<K: Blueprint> HandlerTemplate<K>
511where
512 <K::Params as Param>::State: Copy,
513{
514 #[allow(clippy::needless_pass_by_value)]
525 pub fn new<F>(f: F, registry: &Registry) -> Self
526 where
527 F: TemplateDispatch<K::Params, K::Event>,
528 {
529 const {
530 assert!(
531 std::mem::size_of::<F>() == 0,
532 "F must be a ZST (named function item, not a closure or fn pointer)"
533 );
534 }
535 let _ = f;
536 let prototype = K::Params::init(registry);
537 F::validate(&prototype, registry);
538 Self {
539 prototype,
540 run_fn: F::run_fn_ptr(),
541 name: std::any::type_name::<F>(),
542 _key: PhantomData,
543 }
544 }
545
546 #[must_use = "the generated handler must be stored or dispatched"]
548 pub fn generate(&self) -> TemplatedHandler<K> {
549 TemplatedHandler {
550 state: self.prototype,
551 run_fn: self.run_fn,
552 name: self.name,
553 _key: PhantomData,
554 }
555 }
556}
557
558pub struct TemplatedHandler<K: Blueprint>
567where
568 <K::Params as Param>::State: Copy,
569{
570 state: <K::Params as Param>::State,
571 run_fn: unsafe fn(&mut <K::Params as Param>::State, &mut World, K::Event),
572 name: &'static str,
573 _key: PhantomData<fn() -> K>,
574}
575
576impl<K: Blueprint> Handler<K::Event> for TemplatedHandler<K>
577where
578 <K::Params as Param>::State: Copy,
579{
580 fn run(&mut self, world: &mut World, event: K::Event) {
581 unsafe { (self.run_fn)(&mut self.state, world, event) }
585 }
586
587 fn name(&self) -> &'static str {
588 self.name
589 }
590}
591
592type CallbackRunFn<K> = unsafe fn(
598 &mut <K as CallbackBlueprint>::Context,
599 &mut <<K as Blueprint>::Params as Param>::State,
600 &mut World,
601 <K as Blueprint>::Event,
602);
603
604pub struct CallbackTemplate<K: CallbackBlueprint>
646where
647 <K::Params as Param>::State: Copy,
648{
649 prototype: <K::Params as Param>::State,
650 run_fn: CallbackRunFn<K>,
651 name: &'static str,
652 _key: PhantomData<fn() -> K>,
653}
654
655impl<K: CallbackBlueprint> CallbackTemplate<K>
656where
657 <K::Params as Param>::State: Copy,
658{
659 #[allow(clippy::needless_pass_by_value)]
669 pub fn new<F>(f: F, registry: &Registry) -> Self
670 where
671 F: CallbackTemplateDispatch<K::Context, K::Params, K::Event>,
672 {
673 const {
674 assert!(
675 std::mem::size_of::<F>() == 0,
676 "F must be a ZST (named function item, not a closure or fn pointer)"
677 );
678 }
679 let _ = f;
680 let prototype = K::Params::init(registry);
681 F::validate(&prototype, registry);
682 Self {
683 prototype,
684 run_fn: F::run_fn_ptr(),
685 name: std::any::type_name::<F>(),
686 _key: PhantomData,
687 }
688 }
689
690 #[must_use = "the generated callback must be stored or dispatched"]
692 pub fn generate(&self, ctx: K::Context) -> TemplatedCallback<K> {
693 TemplatedCallback {
694 ctx,
695 state: self.prototype,
696 run_fn: self.run_fn,
697 name: self.name,
698 _key: PhantomData,
699 }
700 }
701}
702
703pub struct TemplatedCallback<K: CallbackBlueprint>
711where
712 <K::Params as Param>::State: Copy,
713{
714 ctx: K::Context,
715 state: <K::Params as Param>::State,
716 run_fn: CallbackRunFn<K>,
717 name: &'static str,
718 _key: PhantomData<fn() -> K>,
719}
720
721impl<K: CallbackBlueprint> TemplatedCallback<K>
722where
723 <K::Params as Param>::State: Copy,
724{
725 pub fn ctx(&self) -> &K::Context {
727 &self.ctx
728 }
729
730 pub fn ctx_mut(&mut self) -> &mut K::Context {
732 &mut self.ctx
733 }
734}
735
736impl<K: CallbackBlueprint> Handler<K::Event> for TemplatedCallback<K>
737where
738 <K::Params as Param>::State: Copy,
739{
740 fn run(&mut self, world: &mut World, event: K::Event) {
741 unsafe { (self.run_fn)(&mut self.ctx, &mut self.state, world, event) }
744 }
745
746 fn name(&self) -> &'static str {
747 self.name
748 }
749}
750
751#[macro_export]
767macro_rules! handler_blueprint {
768 ($name:ident, Event = $event:ty, Params = $params:ty) => {
769 struct $name;
770 impl $crate::template::Blueprint for $name {
771 type Event = $event;
772 type Params = $params;
773 }
774 };
775}
776
777#[macro_export]
785macro_rules! callback_blueprint {
786 ($name:ident, Context = $ctx:ty, Event = $event:ty, Params = $params:ty) => {
787 struct $name;
788 impl $crate::template::Blueprint for $name {
789 type Event = $event;
790 type Params = $params;
791 }
792 impl $crate::template::CallbackBlueprint for $name {
793 type Context = $ctx;
794 }
795 };
796}
797
798#[cfg(test)]
803mod tests {
804 use super::*;
805 use crate::{Res, ResMut, WorldBuilder, no_event};
806
807 struct OnTick;
810 impl Blueprint for OnTick {
811 type Event = u32;
812 type Params = (ResMut<'static, u64>,);
813 }
814
815 struct EventOnly;
816 impl Blueprint for EventOnly {
817 type Event = u32;
818 type Params = ();
819 }
820
821 struct TwoParams;
822 impl Blueprint for TwoParams {
823 type Event = ();
824 type Params = (Res<'static, u64>, ResMut<'static, bool>);
825 }
826
827 fn tick(mut counter: ResMut<u64>, event: u32) {
830 *counter += event as u64;
831 }
832
833 #[test]
834 fn handler_template_basic() {
835 let mut builder = WorldBuilder::new();
836 builder.register::<u64>(0);
837 let mut world = builder.build();
838
839 let template = HandlerTemplate::<OnTick>::new(tick, world.registry());
840 let mut h = template.generate();
841 h.run(&mut world, 10);
842 assert_eq!(*world.resource::<u64>(), 10);
843 }
844
845 #[test]
846 fn handler_template_stamps_independent() {
847 let mut builder = WorldBuilder::new();
848 builder.register::<u64>(0);
849 let mut world = builder.build();
850
851 let template = HandlerTemplate::<OnTick>::new(tick, world.registry());
852 let mut h1 = template.generate();
853 let mut h2 = template.generate();
854
855 h1.run(&mut world, 10);
856 h2.run(&mut world, 5);
857 assert_eq!(*world.resource::<u64>(), 15);
858 }
859
860 fn event_only_fn(event: u32) {
861 assert!(event > 0);
862 }
863
864 #[test]
865 fn handler_template_event_only() {
866 let mut world = WorldBuilder::new().build();
867 let template = HandlerTemplate::<EventOnly>::new(event_only_fn, world.registry());
868 let mut h = template.generate();
869 h.run(&mut world, 42);
870 }
871
872 fn two_params_fn(counter: Res<u64>, mut flag: ResMut<bool>) {
873 if *counter > 0 {
874 *flag = true;
875 }
876 }
877
878 #[test]
879 fn handler_template_two_params() {
880 let mut builder = WorldBuilder::new();
881 builder.register::<u64>(1);
882 builder.register::<bool>(false);
883 let mut world = builder.build();
884
885 let template = HandlerTemplate::<TwoParams>::new(no_event(two_params_fn), world.registry());
886 let mut h = template.generate();
887 h.run(&mut world, ());
888 assert!(*world.resource::<bool>());
889 }
890
891 #[test]
894 fn templated_handler_boxable() {
895 let mut builder = WorldBuilder::new();
896 builder.register::<u64>(0);
897 let mut world = builder.build();
898
899 let template = HandlerTemplate::<OnTick>::new(tick, world.registry());
900 let h = template.generate();
901 let mut boxed: Box<dyn Handler<u32>> = Box::new(h);
902 boxed.run(&mut world, 7);
903 assert_eq!(*world.resource::<u64>(), 7);
904 }
905
906 #[test]
909 fn templated_handler_name() {
910 let mut builder = WorldBuilder::new();
911 builder.register::<u64>(0);
912 let world = builder.build();
913
914 let template = HandlerTemplate::<OnTick>::new(tick, world.registry());
915 let h = template.generate();
916 assert!(h.name().contains("tick"));
917 }
918
919 #[test]
922 #[should_panic(expected = "not registered")]
923 fn handler_template_missing_resource() {
924 let world = WorldBuilder::new().build();
925 let _template = HandlerTemplate::<OnTick>::new(tick, world.registry());
926 }
927
928 #[test]
929 #[should_panic(expected = "conflicting access")]
930 fn handler_template_duplicate_access() {
931 struct BadBlueprint;
932 impl Blueprint for BadBlueprint {
933 type Event = ();
934 type Params = (Res<'static, u64>, ResMut<'static, u64>);
935 }
936
937 fn bad(a: Res<u64>, b: ResMut<u64>) {
938 let _ = (*a, &*b);
939 }
940
941 let mut builder = WorldBuilder::new();
942 builder.register::<u64>(0);
943 let world = builder.build();
944 let _template = HandlerTemplate::<BadBlueprint>::new(no_event(bad), world.registry());
945 }
946
947 struct TimerCtx {
950 order_id: u64,
951 fires: u64,
952 }
953
954 struct OnTimeout;
955 impl Blueprint for OnTimeout {
956 type Event = ();
957 type Params = (ResMut<'static, u64>,);
958 }
959 impl CallbackBlueprint for OnTimeout {
960 type Context = TimerCtx;
961 }
962
963 fn on_timeout(ctx: &mut TimerCtx, mut counter: ResMut<u64>) {
964 ctx.fires += 1;
965 *counter += ctx.order_id;
966 }
967
968 #[test]
969 fn callback_template_basic() {
970 let mut builder = WorldBuilder::new();
971 builder.register::<u64>(0);
972 let mut world = builder.build();
973
974 let template = CallbackTemplate::<OnTimeout>::new(no_event(on_timeout), world.registry());
975 let mut cb = template.generate(TimerCtx {
976 order_id: 42,
977 fires: 0,
978 });
979 cb.run(&mut world, ());
980 assert_eq!(cb.ctx().fires, 1);
981 assert_eq!(*world.resource::<u64>(), 42);
982 }
983
984 #[test]
985 fn callback_template_independent_contexts() {
986 let mut builder = WorldBuilder::new();
987 builder.register::<u64>(0);
988 let mut world = builder.build();
989
990 let template = CallbackTemplate::<OnTimeout>::new(no_event(on_timeout), world.registry());
991 let mut cb1 = template.generate(TimerCtx {
992 order_id: 10,
993 fires: 0,
994 });
995 let mut cb2 = template.generate(TimerCtx {
996 order_id: 20,
997 fires: 0,
998 });
999
1000 cb1.run(&mut world, ());
1001 cb2.run(&mut world, ());
1002 assert_eq!(cb1.ctx().fires, 1);
1003 assert_eq!(cb2.ctx().fires, 1);
1004 assert_eq!(*world.resource::<u64>(), 30);
1005 }
1006
1007 struct CtxOnlyKey;
1008 impl Blueprint for CtxOnlyKey {
1009 type Event = u32;
1010 type Params = ();
1011 }
1012 impl CallbackBlueprint for CtxOnlyKey {
1013 type Context = u64;
1014 }
1015
1016 fn ctx_only(ctx: &mut u64, event: u32) {
1017 *ctx += event as u64;
1018 }
1019
1020 #[test]
1021 fn callback_template_event_only() {
1022 let mut world = WorldBuilder::new().build();
1023 let template = CallbackTemplate::<CtxOnlyKey>::new(ctx_only, world.registry());
1024 let mut cb = template.generate(0u64);
1025 cb.run(&mut world, 5);
1026 assert_eq!(*cb.ctx(), 5);
1027 }
1028
1029 #[test]
1030 fn callback_template_boxable() {
1031 let mut builder = WorldBuilder::new();
1032 builder.register::<u64>(0);
1033 let mut world = builder.build();
1034
1035 let template = CallbackTemplate::<OnTimeout>::new(no_event(on_timeout), world.registry());
1036 let cb = template.generate(TimerCtx {
1037 order_id: 7,
1038 fires: 0,
1039 });
1040 let mut boxed: Box<dyn Handler<()>> = Box::new(cb);
1041 boxed.run(&mut world, ());
1042 assert_eq!(*world.resource::<u64>(), 7);
1043 }
1044
1045 #[test]
1046 fn callback_template_ctx_accessible() {
1047 let mut builder = WorldBuilder::new();
1048 builder.register::<u64>(0);
1049 let mut world = builder.build();
1050
1051 let template = CallbackTemplate::<OnTimeout>::new(no_event(on_timeout), world.registry());
1052 let mut cb = template.generate(TimerCtx {
1053 order_id: 42,
1054 fires: 0,
1055 });
1056 assert_eq!(cb.ctx().order_id, 42);
1057 cb.run(&mut world, ());
1058 assert_eq!(cb.ctx().fires, 1);
1059 cb.ctx_mut().order_id = 99;
1060 cb.run(&mut world, ());
1061 assert_eq!(*world.resource::<u64>(), 42 + 99);
1062 }
1063
1064 handler_blueprint!(MacroOnTick, Event = u32, Params = (ResMut<'static, u64>,));
1067
1068 #[test]
1069 fn macro_handler_blueprint() {
1070 let mut builder = WorldBuilder::new();
1071 builder.register::<u64>(0);
1072 let mut world = builder.build();
1073
1074 let template = HandlerTemplate::<MacroOnTick>::new(tick, world.registry());
1075 let mut h = template.generate();
1076 h.run(&mut world, 3);
1077 assert_eq!(*world.resource::<u64>(), 3);
1078 }
1079
1080 struct Offset(i64);
1083 impl crate::world::Resource for Offset {}
1084 struct Scale(u32);
1085 impl crate::world::Resource for Scale {}
1086 struct Tag(u32);
1087 impl crate::world::Resource for Tag {}
1088
1089 struct FiveParamBlueprint;
1090 impl Blueprint for FiveParamBlueprint {
1091 type Event = u32;
1092 type Params = (
1093 ResMut<'static, u64>,
1094 Res<'static, bool>,
1095 ResMut<'static, Offset>,
1096 Res<'static, Scale>,
1097 ResMut<'static, Tag>,
1098 );
1099 }
1100
1101 fn five_param_fn(
1102 mut counter: ResMut<u64>,
1103 flag: Res<bool>,
1104 mut offset: ResMut<Offset>,
1105 scale: Res<Scale>,
1106 mut tag: ResMut<Tag>,
1107 event: u32,
1108 ) {
1109 if *flag {
1110 *counter += event as u64;
1111 }
1112 offset.0 += (scale.0 as i64) * (event as i64);
1113 tag.0 = event;
1114 }
1115
1116 #[test]
1117 fn handler_template_five_params() {
1118 let mut builder = WorldBuilder::new();
1119 builder.register::<u64>(0);
1120 builder.register::<bool>(true);
1121 builder.register(Offset(0));
1122 builder.register(Scale(2));
1123 builder.register(Tag(0));
1124 let mut world = builder.build();
1125
1126 let template = HandlerTemplate::<FiveParamBlueprint>::new(five_param_fn, world.registry());
1127
1128 let mut h1 = template.generate();
1129 let mut h2 = template.generate();
1130
1131 h1.run(&mut world, 10);
1132 assert_eq!(*world.resource::<u64>(), 10);
1133 assert_eq!(world.resource::<Offset>().0, 20);
1134 assert_eq!(world.resource::<Tag>().0, 10);
1135
1136 h2.run(&mut world, 5);
1137 assert_eq!(*world.resource::<u64>(), 15);
1138 assert_eq!(world.resource::<Offset>().0, 30);
1139 assert_eq!(world.resource::<Tag>().0, 5);
1140 }
1141
1142 callback_blueprint!(MacroOnTimeout, Context = TimerCtx, Event = (), Params = (ResMut<'static, u64>,));
1143
1144 #[test]
1145 fn macro_callback_blueprint() {
1146 let mut builder = WorldBuilder::new();
1147 builder.register::<u64>(0);
1148 let mut world = builder.build();
1149
1150 let template =
1151 CallbackTemplate::<MacroOnTimeout>::new(no_event(on_timeout), world.registry());
1152 let mut cb = template.generate(TimerCtx {
1153 order_id: 5,
1154 fires: 0,
1155 });
1156 cb.run(&mut world, ());
1157 assert_eq!(cb.ctx().fires, 1);
1158 assert_eq!(*world.resource::<u64>(), 5);
1159 }
1160}