use crate::ecs::{
resources::Resources,
system::{IntoSystem, System},
system_set::IntoSystemSet,
};
pub trait RunCondition: 'static {
fn should_run(world: &hecs::World, resources: &Resources) -> bool;
}
pub struct ResourceExists<T>(std::marker::PhantomData<T>);
impl<T: 'static + Send + Sync> RunCondition for ResourceExists<T> {
fn should_run(world: &hecs::World, resources: &Resources) -> bool {
resources.has_resource::<T>(world)
}
}
pub struct And<A, B>(std::marker::PhantomData<(A, B)>);
impl<A: RunCondition, B: RunCondition> RunCondition for And<A, B> {
fn should_run(world: &hecs::World, resources: &Resources) -> bool {
A::should_run(world, resources) && B::should_run(world, resources)
}
}
pub struct Or<A, B>(std::marker::PhantomData<(A, B)>);
impl<A: RunCondition, B: RunCondition> RunCondition for Or<A, B> {
fn should_run(world: &hecs::World, resources: &Resources) -> bool {
A::should_run(world, resources) || B::should_run(world, resources)
}
}
pub struct Conditional<S, C> {
inner: S,
_marker: std::marker::PhantomData<C>,
}
impl<S: System, C: RunCondition> System for Conditional<S, C> {
fn run(&mut self, world: &hecs::World, resources: &Resources) {
if C::should_run(world, resources) {
self.inner.run(world, resources);
}
}
fn name(&self) -> &'static str {
self.inner.name()
}
}
pub trait RunIfExt<Marker>: IntoSystem<Marker> + Sized {
fn run_if<C: RunCondition>(self) -> RunIfSystem<Self, Marker, C> {
RunIfSystem {
inner: self,
_marker: std::marker::PhantomData,
}
}
}
impl<Marker, T: IntoSystem<Marker>> RunIfExt<Marker> for T {}
pub struct RunIfSystem<T, Marker, C> {
inner: T,
_marker: std::marker::PhantomData<(Marker, C)>,
}
impl<T, Marker, C> IntoSystem<Marker> for RunIfSystem<T, Marker, C>
where
T: IntoSystem<Marker>,
C: RunCondition,
{
type System = Conditional<T::System, C>;
fn into_system(self) -> Self::System {
Conditional {
inner: self.inner.into_system(),
_marker: std::marker::PhantomData,
}
}
}
pub trait RunIfFnExt<Marker>: IntoSystem<Marker> + Sized {
fn run_if_fn<F>(self, cond: F) -> RunIfFnSystem<Self, Marker, F>
where
F: Fn(&hecs::World, &Resources) -> bool + 'static,
{
RunIfFnSystem {
inner: self,
cond,
_marker: std::marker::PhantomData,
}
}
}
impl<Marker, T: IntoSystem<Marker>> RunIfFnExt<Marker> for T {}
pub struct RunIfFnSystem<T, Marker, F> {
inner: T,
cond: F,
_marker: std::marker::PhantomData<Marker>,
}
impl<T, Marker, F> IntoSystem<Marker> for RunIfFnSystem<T, Marker, F>
where
T: IntoSystem<Marker>,
F: Fn(&hecs::World, &Resources) -> bool + 'static,
Marker: 'static,
{
type System = ConditionalFn<T::System, F>;
fn into_system(self) -> Self::System {
ConditionalFn {
inner: self.inner.into_system(),
cond: self.cond,
}
}
}
pub struct ConditionalFn<S, F> {
inner: S,
cond: F,
}
impl<S: System, F: Fn(&hecs::World, &Resources) -> bool + 'static> System for ConditionalFn<S, F> {
fn run(&mut self, world: &hecs::World, resources: &Resources) {
if (self.cond)(world, resources) {
self.inner.run(world, resources);
}
}
fn name(&self) -> &'static str {
self.inner.name()
}
}
pub trait SystemSetRunIfExt<M>: IntoSystemSet<M> + Sized {
fn run_if<C: RunCondition>(self) -> ConditionalSet<Self, M, C> {
ConditionalSet {
inner: self,
_marker: std::marker::PhantomData,
}
}
}
impl<M, T: IntoSystemSet<M>> SystemSetRunIfExt<M> for T {}
pub struct ConditionalSet<T, M, C> {
inner: T,
_marker: std::marker::PhantomData<(M, C)>,
}
struct BoxedConditional<C> {
inner: Box<dyn System>,
_marker: std::marker::PhantomData<C>,
}
impl<C: RunCondition> System for BoxedConditional<C> {
fn run(&mut self, world: &hecs::World, resources: &Resources) {
if C::should_run(world, resources) {
self.inner.run(world, resources);
}
}
fn name(&self) -> &'static str {
self.inner.name()
}
}
impl<T, M, C> IntoSystemSet<M> for ConditionalSet<T, M, C>
where
T: IntoSystemSet<M>,
C: RunCondition,
{
fn into_system_set(self) -> Vec<Box<dyn System>> {
self.inner
.into_system_set()
.into_iter()
.map(|system| {
Box::new(BoxedConditional::<C> {
inner: system,
_marker: std::marker::PhantomData,
}) as Box<dyn System>
})
.collect()
}
}