use std::ops::{Deref, DerefMut};
use crate::Resource;
use crate::callback::Callback;
use crate::resource::{Res, ResMut, Seq, SeqMut};
use crate::shutdown::Shutdown;
use crate::world::{Registry, ResourceId, World};
pub trait Param {
type State: Send;
type Item<'w>;
fn init(registry: &Registry) -> Self::State;
unsafe fn fetch<'w>(world: &'w World, state: &'w mut Self::State) -> Self::Item<'w>;
fn resource_id(state: &Self::State) -> Option<ResourceId> {
let _ = state;
None
}
}
impl<T: Resource> Param for Res<'_, T> {
type State = ResourceId;
type Item<'w> = Res<'w, T>;
fn init(registry: &Registry) -> ResourceId {
registry.id::<T>()
}
#[inline(always)]
unsafe fn fetch<'w>(world: &'w World, state: &'w mut ResourceId) -> Res<'w, T> {
let id = *state;
#[cfg(debug_assertions)]
world.track_borrow(id);
unsafe { Res::new(world.get::<T>(id)) }
}
fn resource_id(state: &ResourceId) -> Option<ResourceId> {
Some(*state)
}
}
impl<T: Resource> Param for ResMut<'_, T> {
type State = ResourceId;
type Item<'w> = ResMut<'w, T>;
fn init(registry: &Registry) -> ResourceId {
registry.id::<T>()
}
#[inline(always)]
unsafe fn fetch<'w>(world: &'w World, state: &'w mut ResourceId) -> ResMut<'w, T> {
let id = *state;
#[cfg(debug_assertions)]
world.track_borrow(id);
unsafe { ResMut::new(world.get_mut::<T>(id)) }
}
fn resource_id(state: &ResourceId) -> Option<ResourceId> {
Some(*state)
}
}
impl<T: Resource> Param for Option<Res<'_, T>> {
type State = Option<ResourceId>;
type Item<'w> = Option<Res<'w, T>>;
fn init(registry: &Registry) -> Option<ResourceId> {
registry.try_id::<T>()
}
#[inline(always)]
unsafe fn fetch<'w>(world: &'w World, state: &'w mut Option<ResourceId>) -> Option<Res<'w, T>> {
state.map(|id| {
#[cfg(debug_assertions)]
world.track_borrow(id);
unsafe { Res::new(world.get::<T>(id)) }
})
}
fn resource_id(state: &Option<ResourceId>) -> Option<ResourceId> {
*state
}
}
impl<T: Resource> Param for Option<ResMut<'_, T>> {
type State = Option<ResourceId>;
type Item<'w> = Option<ResMut<'w, T>>;
fn init(registry: &Registry) -> Option<ResourceId> {
registry.try_id::<T>()
}
#[inline(always)]
unsafe fn fetch<'w>(
world: &'w World,
state: &'w mut Option<ResourceId>,
) -> Option<ResMut<'w, T>> {
state.map(|id| {
#[cfg(debug_assertions)]
world.track_borrow(id);
unsafe { ResMut::new(world.get_mut::<T>(id)) }
})
}
fn resource_id(state: &Option<ResourceId>) -> Option<ResourceId> {
*state
}
}
impl Param for Seq {
type State = ();
type Item<'w> = Seq;
fn init(_registry: &Registry) {}
#[inline(always)]
unsafe fn fetch<'w>(world: &'w World, _state: &'w mut ()) -> Seq {
Seq(world.current_sequence())
}
}
impl Param for SeqMut<'_> {
type State = ();
type Item<'w> = SeqMut<'w>;
fn init(_registry: &Registry) {}
#[inline(always)]
unsafe fn fetch<'w>(world: &'w World, _state: &'w mut ()) -> SeqMut<'w> {
SeqMut(world.sequence_cell())
}
}
impl Param for Shutdown<'_> {
type State = ();
type Item<'w> = Shutdown<'w>;
fn init(_registry: &Registry) {}
#[inline(always)]
unsafe fn fetch<'w>(world: &'w World, _state: &'w mut ()) -> Shutdown<'w> {
Shutdown(world.shutdown_flag())
}
}
impl Param for () {
type State = ();
type Item<'w> = ();
fn init(_registry: &Registry) {}
#[inline(always)]
unsafe fn fetch<'w>(_world: &'w World, _state: &'w mut ()) {}
}
macro_rules! impl_param_tuple {
($($P:ident),+) => {
impl<$($P: Param),+> Param for ($($P,)+) {
type State = ($($P::State,)+);
type Item<'w> = ($($P::Item<'w>,)+);
fn init(registry: &Registry) -> Self::State {
($($P::init(registry),)+)
}
#[inline(always)]
#[allow(non_snake_case)]
unsafe fn fetch<'w>(world: &'w World, state: &'w mut Self::State) -> Self::Item<'w> {
let ($($P,)+) = state;
unsafe { ($($P::fetch(world, $P),)+) }
}
}
};
}
all_tuples!(impl_param_tuple);
pub struct Local<'s, T: Default + Send + 'static> {
value: &'s mut T,
}
impl<'s, T: Default + Send + 'static> Local<'s, T> {
pub(crate) fn new(value: &'s mut T) -> Self {
Self { value }
}
}
impl<T: Default + Send + std::fmt::Debug + 'static> std::fmt::Debug for Local<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
impl<T: Default + Send + 'static> Deref for Local<'_, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
self.value
}
}
impl<T: Default + Send + 'static> DerefMut for Local<'_, T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
self.value
}
}
impl<T: Default + Send + 'static> Param for Local<'_, T> {
type State = T;
type Item<'s> = Local<'s, T>;
fn init(_registry: &Registry) -> T {
T::default()
}
#[inline(always)]
unsafe fn fetch<'s>(_world: &'s World, state: &'s mut T) -> Local<'s, T> {
Local::new(state)
}
}
pub struct RegistryRef<'w> {
registry: &'w Registry,
}
impl Deref for RegistryRef<'_> {
type Target = Registry;
#[inline(always)]
fn deref(&self) -> &Registry {
self.registry
}
}
impl Param for RegistryRef<'_> {
type State = ();
type Item<'w> = RegistryRef<'w>;
fn init(_registry: &Registry) {}
#[inline(always)]
unsafe fn fetch<'w>(world: &'w World, _state: &'w mut ()) -> RegistryRef<'w> {
RegistryRef {
registry: world.registry(),
}
}
}
pub trait Handler<E>: Send {
fn run(&mut self, world: &mut World, event: E);
fn name(&self) -> &'static str {
"<unnamed>"
}
}
impl<E> Handler<E> for Box<dyn Handler<E>> {
fn run(&mut self, world: &mut World, event: E) {
(**self).run(world, event);
}
fn name(&self) -> &'static str {
(**self).name()
}
}
#[doc(hidden)]
pub struct CtxFree<F>(pub(crate) F);
pub type HandlerFn<F, Params> = Callback<(), CtxFree<F>, Params>;
#[diagnostic::on_unimplemented(
message = "this function cannot be converted into a handler",
note = "handler signature: `fn(Res<A>, ResMut<B>, ..., Event)` — resources first, event last",
note = "closures with resource parameters (Res<T>, ResMut<T>) are not supported — use a named `fn`",
note = "arity-0 closures (`fn(Event)` with no resources) ARE supported"
)]
pub trait IntoHandler<E, Params> {
type Handler: Handler<E> + 'static;
#[must_use = "the handler must be stored or dispatched — discarding it does nothing"]
fn into_handler(self, registry: &Registry) -> Self::Handler;
}
impl<E, F: FnMut(E) + Send + 'static> IntoHandler<E, ()> for F {
type Handler = Callback<(), CtxFree<F>, ()>;
fn into_handler(self, registry: &Registry) -> Self::Handler {
Callback {
ctx: (),
f: CtxFree(self),
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
impl<E, F: FnMut(E) + Send + 'static> Handler<E> for Callback<(), CtxFree<F>, ()> {
fn run(&mut self, _world: &mut World, event: E) {
(self.f.0)(event);
}
fn name(&self) -> &'static str {
self.name
}
}
macro_rules! impl_into_handler {
($($P:ident),+) => {
impl<E, F: Send + 'static, $($P: Param + 'static),+> IntoHandler<E, ($($P,)+)> for F
where
for<'a> &'a mut F: FnMut($($P,)+ E) + FnMut($($P::Item<'a>,)+ E),
{
type Handler = Callback<(), CtxFree<F>, ($($P,)+)>;
fn into_handler(self, registry: &Registry) -> Self::Handler {
let state = <($($P,)+) as Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$(
(<$P as Param>::resource_id($P),
std::any::type_name::<$P>()),
)+
]);
}
Callback {
ctx: (),
f: CtxFree(self),
state,
name: std::any::type_name::<F>(),
}
}
}
impl<E, F: Send + 'static, $($P: Param + 'static),+> Handler<E>
for Callback<(), CtxFree<F>, ($($P,)+)>
where
for<'a> &'a mut F: FnMut($($P,)+ E) + FnMut($($P::Item<'a>,)+ E),
{
#[allow(non_snake_case)]
fn run(&mut self, world: &mut World, event: E) {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ Ev>(
mut f: impl FnMut($($P,)+ Ev),
$($P: $P,)+
event: Ev,
) {
f($($P,)+ event);
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f.0, $($P,)+ event);
}
fn name(&self) -> &'static str {
self.name
}
}
};
}
all_tuples!(impl_into_handler);
pub struct Opaque;
pub struct Resolved;
impl<E, H: Handler<E> + 'static> IntoHandler<E, Resolved> for H {
type Handler = H;
fn into_handler(self, _registry: &Registry) -> H {
self
}
}
pub struct OpaqueHandler<F> {
f: F,
name: &'static str,
}
impl<E, F: FnMut(&mut World, E) + Send + 'static> Handler<E> for OpaqueHandler<F> {
fn run(&mut self, world: &mut World, event: E) {
(self.f)(world, event);
}
fn name(&self) -> &'static str {
self.name
}
}
impl<E, F: FnMut(&mut World, E) + Send + 'static> IntoHandler<E, Opaque> for F {
type Handler = OpaqueHandler<F>;
fn into_handler(self, _registry: &Registry) -> Self::Handler {
OpaqueHandler {
f: self,
name: std::any::type_name::<F>(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::WorldBuilder;
use crate::world::Sequence;
#[test]
fn res_param() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(42);
let mut world = builder.build();
let mut state = <Res<u64> as Param>::init(world.registry_mut());
let res = unsafe { <Res<u64> as Param>::fetch(&world, &mut state) };
assert_eq!(*res, 42);
}
#[test]
fn res_mut_param() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(1);
let mut world = builder.build();
let mut state = <ResMut<u64> as Param>::init(world.registry_mut());
unsafe {
let mut res = <ResMut<u64> as Param>::fetch(&world, &mut state);
*res = 99;
}
#[cfg(debug_assertions)]
world.clear_borrows();
unsafe {
let mut read_state = <Res<u64> as Param>::init(world.registry_mut());
let res = <Res<u64> as Param>::fetch(&world, &mut read_state);
assert_eq!(*res, 99);
}
}
#[test]
fn tuple_param() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(10);
builder.register::<bool>(true);
let mut world = builder.build();
let mut state = <(Res<u64>, ResMut<bool>) as Param>::init(world.registry_mut());
unsafe {
let (counter, mut flag) =
<(Res<u64>, ResMut<bool>) as Param>::fetch(&world, &mut state);
assert_eq!(*counter, 10);
assert!(*flag);
*flag = false;
}
#[cfg(debug_assertions)]
world.clear_borrows();
unsafe {
let mut read_state = <Res<bool> as Param>::init(world.registry_mut());
let res = <Res<bool> as Param>::fetch(&world, &mut read_state);
assert!(!*res);
}
}
#[test]
fn empty_tuple_param() {
let mut world = WorldBuilder::new().build();
<() as Param>::init(world.registry_mut());
unsafe { <() as Param>::fetch(&world, &mut ()) };
}
fn event_only_handler(event: u32) {
assert_eq!(event, 42);
}
#[test]
fn event_only_dispatch() {
let mut world = WorldBuilder::new().build();
let mut sys = event_only_handler.into_handler(world.registry_mut());
sys.run(&mut world, 42u32);
}
fn one_res_handler(counter: Res<u64>, event: u32) {
assert_eq!(*counter, 10);
assert_eq!(event, 5);
}
#[test]
fn one_res_and_event() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(10);
let mut world = builder.build();
let mut sys = one_res_handler.into_handler(world.registry_mut());
sys.run(&mut world, 5u32);
}
fn two_res_handler(counter: Res<u64>, flag: Res<bool>, event: u32) {
assert_eq!(*counter, 10);
assert!(*flag);
assert_eq!(event, 7);
}
#[test]
fn two_res_and_event() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(10);
builder.register::<bool>(true);
let mut world = builder.build();
let mut sys = two_res_handler.into_handler(world.registry_mut());
sys.run(&mut world, 7u32);
}
fn accumulate(mut counter: ResMut<u64>, event: u64) {
*counter += event;
}
#[test]
fn mutation_through_res_mut() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let mut sys = accumulate.into_handler(world.registry_mut());
sys.run(&mut world, 10u64);
sys.run(&mut world, 5u64);
assert_eq!(*world.resource::<u64>(), 15);
}
fn add_handler(mut counter: ResMut<u64>, event: u64) {
*counter += event;
}
fn mul_handler(mut counter: ResMut<u64>, event: u64) {
*counter *= event;
}
#[test]
fn box_dyn_type_erasure() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let sys_a = add_handler.into_handler(world.registry_mut());
let sys_b = mul_handler.into_handler(world.registry_mut());
let mut handlers: Vec<Box<dyn Handler<u64>>> = vec![Box::new(sys_a), Box::new(sys_b)];
for h in &mut handlers {
h.run(&mut world, 3u64);
}
assert_eq!(*world.resource::<u64>(), 9);
}
fn local_counter(mut count: Local<u64>, _event: u32) {
*count += 1;
}
#[test]
fn local_default_init() {
let mut world = WorldBuilder::new().build();
let mut sys = local_counter.into_handler(world.registry_mut());
sys.run(&mut world, 1u32);
}
#[test]
fn local_persists_across_runs() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
fn accumulate_local(mut count: Local<u64>, mut total: ResMut<u64>, _event: u32) {
*count += 1;
*total = *count;
}
let mut sys = accumulate_local.into_handler(world.registry_mut());
sys.run(&mut world, 0u32);
sys.run(&mut world, 0u32);
sys.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn local_independent_per_handler() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
fn inc_local(mut count: Local<u64>, mut total: ResMut<u64>, _event: u32) {
*count += 1;
*total += *count;
}
let mut sys_a = inc_local.into_handler(world.registry_mut());
let mut sys_b = inc_local.into_handler(world.registry_mut());
sys_a.run(&mut world, 0u32); sys_b.run(&mut world, 0u32); sys_a.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 4);
}
#[test]
fn option_res_none_when_missing() {
let mut world = WorldBuilder::new().build();
let mut state = <Option<Res<u64>> as Param>::init(world.registry_mut());
let opt = unsafe { <Option<Res<u64>> as Param>::fetch(&world, &mut state) };
assert!(opt.is_none());
}
#[test]
fn option_res_some_when_present() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(42);
let mut world = builder.build();
let mut state = <Option<Res<u64>> as Param>::init(world.registry_mut());
let opt = unsafe { <Option<Res<u64>> as Param>::fetch(&world, &mut state) };
assert_eq!(*opt.unwrap(), 42);
}
#[test]
fn option_res_mut_some_when_present() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(1);
let mut world = builder.build();
let mut state = <Option<ResMut<u64>> as Param>::init(world.registry_mut());
unsafe {
let opt = <Option<ResMut<u64>> as Param>::fetch(&world, &mut state);
*opt.unwrap() = 99;
}
#[cfg(debug_assertions)]
world.clear_borrows();
unsafe {
let mut read_state = <Res<u64> as Param>::init(world.registry_mut());
let res = <Res<u64> as Param>::fetch(&world, &mut read_state);
assert_eq!(*res, 99);
}
}
fn optional_handler(opt: Option<Res<String>>, _event: u32) {
assert!(opt.is_none());
}
#[test]
fn option_in_handler() {
let mut world = WorldBuilder::new().build();
let mut sys = optional_handler.into_handler(world.registry_mut());
sys.run(&mut world, 0u32);
}
#[test]
#[should_panic(expected = "conflicting access")]
fn duplicate_res_panics() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
fn bad(a: Res<u64>, b: Res<u64>, _e: ()) {
let _ = (*a, *b);
}
let _sys = bad.into_handler(world.registry_mut());
}
#[test]
#[should_panic(expected = "conflicting access")]
fn duplicate_res_mut_panics() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
fn bad(a: ResMut<u64>, b: ResMut<u64>, _e: ()) {
let _ = (&*a, &*b);
}
let _sys = bad.into_handler(world.registry_mut());
}
#[test]
#[should_panic(expected = "conflicting access")]
fn duplicate_mixed_panics() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
fn bad(a: Res<u64>, b: ResMut<u64>, _e: ()) {
let _ = (*a, &*b);
}
let _sys = bad.into_handler(world.registry_mut());
}
#[test]
fn different_types_no_conflict() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
builder.register::<u32>(0);
let mut world = builder.build();
fn ok(a: Res<u64>, b: ResMut<u32>, _e: ()) {
let _ = (*a, &*b);
}
let _sys = ok.into_handler(world.registry_mut());
}
#[test]
fn local_no_conflict() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
fn ok(local: Local<u64>, val: ResMut<u64>, _e: ()) {
let _ = (&*local, &*val);
}
let _sys = ok.into_handler(world.registry_mut());
}
#[test]
fn opaque_handler_dispatch() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let opaque_fn = |w: &mut World, event: u64| {
let current = *w.resource::<u64>();
*w.resource_mut::<u64>() = current + event;
};
let mut h = opaque_fn.into_handler(world.registry_mut());
h.run(&mut world, 10u64);
h.run(&mut world, 5u64);
assert_eq!(*world.resource::<u64>(), 15);
}
#[test]
fn opaque_handler_boxed() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let opaque_fn = |w: &mut World, event: u64| {
*w.resource_mut::<u64>() += event;
};
let mut h: Box<dyn Handler<u64>> = Box::new(opaque_fn.into_handler(world.registry_mut()));
h.run(&mut world, 7u64);
assert_eq!(*world.resource::<u64>(), 7);
}
#[test]
fn seq_reads_current() {
fn check(seq: Seq, mut out: ResMut<i64>, _event: ()) {
*out = seq.get();
}
let mut builder = WorldBuilder::new();
builder.register::<i64>(0);
let mut world = builder.build();
world.next_sequence();
let mut handler = check.into_handler(world.registry_mut());
handler.run(&mut world, ());
assert_eq!(*world.resource::<i64>(), 1);
}
#[test]
fn seq_mut_advances() {
fn stamp(mut seq: SeqMut, mut counter: ResMut<u64>, _event: ()) {
let a = seq.advance();
let b = seq.advance();
*counter = a.get() as u64 * 100 + b.get() as u64;
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut world = builder.build();
let mut handler = stamp.into_handler(world.registry_mut());
handler.run(&mut world, ());
assert_eq!(*world.resource::<u64>(), 100 + 2);
assert_eq!(world.current_sequence(), Sequence(2));
}
#[test]
fn seq_mut_persistent_across_dispatches() {
fn advance(mut seq: SeqMut, _event: ()) {
seq.advance();
}
let builder = WorldBuilder::new();
let mut world = builder.build();
let mut handler = advance.into_handler(world.registry_mut());
handler.run(&mut world, ());
handler.run(&mut world, ());
handler.run(&mut world, ());
assert_eq!(world.current_sequence(), Sequence(3));
}
#[test]
fn seq_only_param() {
fn handle(seq: Seq, _event: ()) {
assert!(seq.get() >= 0);
}
let builder = WorldBuilder::new();
let mut world = builder.build();
let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
}
#[test]
fn seq_first_with_res() {
fn handle(seq: Seq, config: Res<u64>, mut out: ResMut<i64>, _event: ()) {
*out = seq.get() + *config as i64;
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(100);
builder.register::<i64>(0);
let mut world = builder.build();
world.next_sequence();
let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
assert_eq!(*world.resource::<i64>(), 101);
}
#[test]
fn seq_middle_position() {
fn handle(config: Res<u64>, seq: Seq, mut out: ResMut<i64>, _event: ()) {
*out = *config as i64 + seq.get();
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(50);
builder.register::<i64>(0);
let mut world = builder.build();
world.next_sequence(); let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
assert_eq!(*world.resource::<i64>(), 51);
}
#[test]
fn seq_last_position() {
fn handle(mut out: ResMut<i64>, seq: Seq, _event: ()) {
*out = seq.get();
}
let mut builder = WorldBuilder::new();
builder.register::<i64>(0);
let mut world = builder.build();
world.next_sequence();
world.next_sequence(); let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
assert_eq!(*world.resource::<i64>(), 2);
}
#[test]
fn seq_mut_only_param() {
fn handle(mut seq: SeqMut, _event: ()) {
seq.advance();
}
let builder = WorldBuilder::new();
let mut world = builder.build();
let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
assert_eq!(world.current_sequence(), Sequence(1));
}
#[test]
fn seq_mut_first_with_res() {
fn handle(mut seq: SeqMut, mut out: ResMut<i64>, _event: ()) {
let s = seq.advance();
*out = s.0;
}
let mut builder = WorldBuilder::new();
builder.register::<i64>(0);
let mut world = builder.build();
let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
assert_eq!(*world.resource::<i64>(), 1);
}
#[test]
fn seq_mut_middle_position() {
fn handle(config: Res<u64>, mut seq: SeqMut, mut out: ResMut<i64>, _event: ()) {
let s = seq.advance();
*out = s.0 + *config as i64;
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(10);
builder.register::<i64>(0);
let mut world = builder.build();
let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
assert_eq!(*world.resource::<i64>(), 11);
}
#[test]
fn seq_mut_last_position() {
fn handle(mut out: ResMut<i64>, mut seq: SeqMut, _event: ()) {
let s = seq.advance();
*out = s.0;
}
let mut builder = WorldBuilder::new();
builder.register::<i64>(0);
let mut world = builder.build();
let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
assert_eq!(*world.resource::<i64>(), 1);
}
#[test]
fn seq_mut_multiple_advances_in_one_dispatch() {
fn handle(mut seq: SeqMut, mut out: ResMut<Vec<i64>>, _event: ()) {
out.push(seq.advance().0);
out.push(seq.advance().0);
out.push(seq.advance().0);
}
let mut builder = WorldBuilder::new();
builder.register::<Vec<i64>>(Vec::new());
let mut world = builder.build();
let mut h = handle.into_handler(world.registry_mut());
h.run(&mut world, ());
assert_eq!(*world.resource::<Vec<i64>>(), vec![1, 2, 3]);
assert_eq!(world.current_sequence(), Sequence(3));
}
#[test]
fn concrete_handler_satisfies_into_handler() {
fn accept_into_handler<E, P>(h: impl IntoHandler<E, P>, reg: &Registry) -> impl Handler<E> {
h.into_handler(reg)
}
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let world = builder.build();
fn bump(mut val: ResMut<u64>, event: u64) {
*val += event;
}
let handler = bump.into_handler(world.registry());
let _resolved = accept_into_handler(handler, world.registry());
}
#[test]
fn handler_impl_into_handler_dispatches() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
fn add_event(mut val: ResMut<u64>, event: u64) {
*val += event;
}
let handler = add_event.into_handler(builder.registry());
let mut resolved = handler.into_handler(builder.registry());
let mut world = builder.build();
resolved.run(&mut world, 42);
assert_eq!(*world.resource::<u64>(), 42);
}
}