Skip to main content

pebble/ecs/
system_condition.rs

1use crate::ecs::{
2    resources::Resources,
3    system::{IntoSystem, System},
4};
5
6/// A predicate checked before a system runs. If `should_run` returns
7/// `false`, the wrapped system's body is skipped entirely for that tick —
8/// its `SystemParam`s are never fetched.
9///
10/// Re-checked every tick. This is deliberate: a resource that exists now
11/// is not guaranteed to exist forever (e.g. if something explicitly removes
12/// it later), so conditions should not assume "ready once" means "ready
13/// forever".
14pub trait RunCondition: 'static {
15    fn should_run(world: &hecs::World, resources: &Resources) -> bool;
16}
17
18/// Runs only while resource `T` exists.
19pub struct ResourceExists<T>(std::marker::PhantomData<T>);
20impl<T: 'static + Send + Sync> RunCondition for ResourceExists<T> {
21    fn should_run(world: &hecs::World, resources: &Resources) -> bool {
22        resources.has_resource::<T>(world)
23    }
24}
25
26/// Wraps a [`System`], skipping it for a tick whenever `C::should_run`
27/// returns `false`.
28pub struct Conditional<S, C> {
29    inner: S,
30    _marker: std::marker::PhantomData<C>,
31}
32
33impl<S: System, C: RunCondition> System for Conditional<S, C> {
34    fn run(&mut self, world: &hecs::World, resources: &Resources) {
35        if C::should_run(world, resources) {
36            self.inner.run(world, resources);
37        }
38    }
39
40    // Deliberately does NOT forward `requires()`: wrapping a system in
41    // `.run_if()` means its author has taken over the "is it safe to run"
42    // question themselves (often via `ResourceExists<T>` for the very
43    // resource the body needs) — App shouldn't second-guess that with its
44    // own pre-flight check on top.
45    fn name(&self) -> &'static str {
46        self.inner.name()
47    }
48}
49
50/// Adds [`.run_if`](RunIfExt::run_if) to anything convertible into a
51/// [`System`], gating it on a [`RunCondition`].
52pub trait RunIfExt<Marker>: IntoSystem<Marker> + Sized {
53    fn run_if<C: RunCondition>(self) -> RunIfSystem<Self, Marker, C> {
54        RunIfSystem {
55            inner: self,
56            _marker: std::marker::PhantomData,
57        }
58    }
59}
60
61impl<Marker, T: IntoSystem<Marker>> RunIfExt<Marker> for T {}
62
63pub struct RunIfSystem<T, Marker, C> {
64    inner: T,
65    _marker: std::marker::PhantomData<(Marker, C)>,
66}
67
68impl<T, Marker, C> IntoSystem<Marker> for RunIfSystem<T, Marker, C>
69where
70    T: IntoSystem<Marker>,
71    C: RunCondition,
72{
73    type System = Conditional<T::System, C>;
74
75    fn into_system(self) -> Self::System {
76        Conditional {
77            inner: self.inner.into_system(),
78            _marker: std::marker::PhantomData,
79        }
80    }
81}