use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use crate::{Effect, EffectOut};
pub type BoxedLocalSetAndFn<T, E> = Box<dyn FnOnce(&T) -> EffectOut<E, T>>;
#[derive(derive_more::Debug)]
pub struct LocalSetAnd<T, E>
where
T: FromWorld + Send + 'static,
E: Effect,
{
#[debug("{0} -> {1}", std::any::type_name::<&T>(), std::any::type_name::<EffectOut<E, T>>())]
pub f: BoxedLocalSetAndFn<T, E>,
}
pub fn local_set_and<T, E, F>(f: F) -> LocalSetAnd<T, E>
where
T: FromWorld + Send + 'static,
E: Effect,
F: FnOnce(&T) -> EffectOut<E, T> + 'static,
{
LocalSetAnd { f: Box::new(f) }
}
impl<T, E> Default for LocalSetAnd<T, E>
where
T: FromWorld + Send + Clone + 'static,
E: Effect + Default,
{
fn default() -> Self {
local_set_and(|t: &T| EffectOut::from_out(t.clone()))
}
}
impl<T, E> Effect for LocalSetAnd<T, E>
where
T: FromWorld + Send + 'static,
E: Effect,
{
type MutParam = (Local<'static, T>, <E as Effect>::MutParam);
fn affect(self, param: &mut <Self::MutParam as SystemParam>::Item<'_, '_>) {
let EffectOut {
effect,
out: new_local,
} = (self.f)(¶m.0);
*param.0 = new_local;
effect.affect(&mut param.1);
}
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::*;
use crate::effect_out;
use crate::effects::number_data::NumberResource;
use crate::effects::one_way_fn::OneWayFn;
use crate::effects::resource::res_set;
use crate::prelude::affect;
proptest! {
#[test]
fn local_set_and_sets_value_and_produces_effect(one_way_fn: OneWayFn) {
let mut app = App::new();
app.init_resource::<NumberResource>().add_systems(
Update,
(move |non_effect_local: Local<u128>| {
assert_eq!(*non_effect_local, 0);
local_set_and(move |x: &u128| {
let new_value = x + 1;
let resource_value = one_way_fn.call(new_value);
effect_out(res_set(NumberResource(resource_value)), new_value)
})
})
.pipe(affect),
);
assert_eq!(app.world().resource::<NumberResource>().0, 0);
for i in 1..10u128 {
app.update();
assert_eq!(
app.world().resource::<NumberResource>().0,
one_way_fn.call(i)
);
}
}
}
}