use alloc::{boxed::Box, format};
use bevy_utils::prelude::DebugName;
use core::ops::Not;
use crate::system::{
Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, RunSystemError,
System, SystemIn, SystemInput,
};
pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
pub trait SystemCondition<Marker, In: SystemInput = ()>:
IntoSystem<In, bool, Marker, System: ReadOnlySystem>
{
fn and_then<M, C: SystemCondition<M, In>>(
self,
then_run: C,
) -> AndThen<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(then_run);
let name = format!("{} && {}", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
fn and_eager<M, C: SystemCondition<M, In>>(
self,
other: C,
) -> AndEager<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(other);
let name = format!("{} & {}", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
#[deprecated(
since = "0.19.0",
note = "use `.and_then(...)` instead, or `.and_eager(...)` to evaluate the conditions eagerly"
)]
fn and<M, C: SystemCondition<M, In>>(self, then_run: C) -> AndThen<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(then_run);
let name = format!("{} && {}", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
fn nand_then<M, C: SystemCondition<M, In>>(
self,
then_run: C,
) -> NandThen<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(then_run);
let name = format!("!({} && {})", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
fn nand_eager<M, C: SystemCondition<M, In>>(
self,
other: C,
) -> NandEager<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(other);
let name = format!("!({} & {})", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
#[deprecated(
since = "0.19.0",
note = "use `.nand_then(...) instead, or `.nand_eager(...)` to evaluate the conditions eagerly"
)]
fn nand<M, C: SystemCondition<M, In>>(self, nand: C) -> NandThen<Self::System, C::System> {
self.nand_then(nand)
}
fn nor_else<M, C: SystemCondition<M, In>>(
self,
else_run: C,
) -> NorElse<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(else_run);
let name = format!("!({} || {})", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
fn nor_eager<M, C: SystemCondition<M, In>>(
self,
other: C,
) -> NorEager<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(other);
let name = format!("!({} | {})", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
#[deprecated(
since = "0.19.0",
note = "use `.nor_else(...)` instead, or `.nor_eager(...)` to evaluate the conditions eagerly"
)]
fn nor<M, C: SystemCondition<M, In>>(self, else_run: C) -> NorElse<Self::System, C::System> {
self.nor_else(else_run)
}
fn or_else<M, C: SystemCondition<M, In>>(self, else_run: C) -> OrElse<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(else_run);
let name = format!("{} || {}", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
fn or_eager<M, C: SystemCondition<M, In>>(self, other: C) -> OrEager<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(other);
let name = format!("{} | {}", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
#[deprecated(
since = "0.19.0",
note = "use `.or_else(...)` instead, or `.or_eager(...)` to eagerly evaluate both conditions"
)]
fn or<M, C: SystemCondition<M, In>>(self, else_run: C) -> OrElse<Self::System, C::System> {
self.or_else(else_run)
}
fn xnor<M, C: SystemCondition<M, In>>(self, other: C) -> Xnor<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(other);
let name = format!("!({} ^ {})", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
fn xor<M, C: SystemCondition<M, In>>(self, other: C) -> Xor<Self::System, C::System> {
let a = IntoSystem::into_system(self);
let b = IntoSystem::into_system(other);
let name = format!("({} ^ {})", a.name(), b.name());
CombinatorSystem::new(a, b, DebugName::owned(name))
}
}
impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F where
F: IntoSystem<In, bool, Marker, System: ReadOnlySystem>
{
}
pub mod common_conditions {
use super::{NotSystem, SystemCondition};
use crate::{
change_detection::DetectChanges,
lifecycle::RemovedComponents,
message::{Message, MessageReader},
prelude::{Component, Query, With},
query::QueryFilter,
resource::Resource,
system::{In, IntoSystem, Local, Res, System, SystemInput},
};
use alloc::format;
pub fn run_once(mut has_run: Local<bool>) -> bool {
if !*has_run {
*has_run = true;
true
} else {
false
}
}
pub fn resource_exists<T>(res: Option<Res<T>>) -> bool
where
T: Resource,
{
res.is_some()
}
pub fn resource_equals<T>(value: T) -> impl FnMut(Res<T>) -> bool
where
T: Resource + PartialEq,
{
move |res: Res<T>| *res == value
}
pub fn resource_exists_and_equals<T>(value: T) -> impl FnMut(Option<Res<T>>) -> bool
where
T: Resource + PartialEq,
{
move |res: Option<Res<T>>| match res {
Some(res) => *res == value,
None => false,
}
}
pub fn resource_added<T>(res: Option<Res<T>>) -> bool
where
T: Resource,
{
match res {
Some(res) => res.is_added(),
None => false,
}
}
pub fn resource_changed<T>(res: Res<T>) -> bool
where
T: Resource,
{
res.is_changed()
}
pub fn resource_exists_and_changed<T>(res: Option<Res<T>>) -> bool
where
T: Resource,
{
match res {
Some(res) => res.is_changed(),
None => false,
}
}
pub fn resource_changed_or_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
where
T: Resource,
{
if let Some(value) = res {
*existed = true;
value.is_changed()
} else if *existed {
*existed = false;
true
} else {
false
}
}
pub fn resource_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
where
T: Resource,
{
if res.is_some() {
*existed = true;
false
} else if *existed {
*existed = false;
true
} else {
false
}
}
pub fn on_message<M: Message>(mut reader: MessageReader<M>) -> bool {
reader.read().count() > 0
}
pub fn any_with_component<T: Component>(query: Query<(), With<T>>) -> bool {
!query.is_empty()
}
pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool {
removals.read().count() > 0
}
pub fn any_match_filter<F: QueryFilter>(query: Query<(), F>) -> bool {
!query.is_empty()
}
pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System>
where
TOut: core::ops::Not,
T: IntoSystem<(), TOut, Marker>,
{
let condition = IntoSystem::into_system(condition);
let name = format!("!{}", condition.name());
NotSystem::new(super::NotMarker, condition, name.into())
}
pub fn condition_changed<Marker, CIn, C>(condition: C) -> impl SystemCondition<(), CIn>
where
CIn: SystemInput,
C: SystemCondition<Marker, CIn>,
{
IntoSystem::into_system(condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| {
let changed = *prev != new;
*prev = new;
changed
}))
}
pub fn condition_changed_to<Marker, CIn, C>(
to: bool,
condition: C,
) -> impl SystemCondition<(), CIn>
where
CIn: SystemInput,
C: SystemCondition<Marker, CIn>,
{
IntoSystem::into_system(condition.pipe(
move |In(new): In<bool>, mut prev: Local<bool>| -> bool {
let now_true = *prev != new && new == to;
*prev = new;
now_true
},
))
}
}
pub type NotSystem<S> = AdapterSystem<NotMarker, S>;
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct NotMarker;
impl<S: System<Out: Not>> Adapt<S> for NotMarker {
type In = S::In;
type Out = <S::Out as Not>::Output;
fn adapt(
&mut self,
input: <Self::In as SystemInput>::Inner<'_>,
run_system: impl FnOnce(SystemIn<'_, S>) -> Result<S::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
run_system(input).map(Not::not)
}
}
pub type AndThen<A, B> = CombinatorSystem<AndThenMarker, A, B>;
pub type AndEager<A, B> = CombinatorSystem<AndEagerMarker, A, B>;
pub type NandThen<A, B> = CombinatorSystem<NandThenMarker, A, B>;
pub type NandEager<A, B> = CombinatorSystem<NandEagerMarker, A, B>;
pub type NorElse<A, B> = CombinatorSystem<NorElseMarker, A, B>;
pub type NorEager<A, B> = CombinatorSystem<NorEagerMarker, A, B>;
pub type OrElse<A, B> = CombinatorSystem<OrElseMarker, A, B>;
pub type OrEager<A, B> = CombinatorSystem<OrEagerMarker, A, B>;
pub type Xnor<A, B> = CombinatorSystem<XnorMarker, A, B>;
pub type Xor<A, B> = CombinatorSystem<XorMarker, A, B>;
#[doc(hidden)]
pub struct AndThenMarker;
impl<In, A, B> Combine<A, B> for AndThenMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(a(input, data).unwrap_or(false) && b(input, data).unwrap_or(false))
}
}
#[doc(hidden)]
pub struct AndEagerMarker;
impl<In, A, B> Combine<A, B> for AndEagerMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(a(input, data).unwrap_or(false) & b(input, data).unwrap_or(false))
}
}
#[doc(hidden)]
pub struct NandThenMarker;
impl<In, A, B> Combine<A, B> for NandThenMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(!(a(input, data).unwrap_or(false) && b(input, data).unwrap_or(false)))
}
}
#[doc(hidden)]
pub struct NandEagerMarker;
impl<In, A, B> Combine<A, B> for NandEagerMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(!(a(input, data).unwrap_or(false) & b(input, data).unwrap_or(false)))
}
}
#[doc(hidden)]
pub struct NorElseMarker;
impl<In, A, B> Combine<A, B> for NorElseMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(!(a(input, data).unwrap_or(false) || b(input, data).unwrap_or(false)))
}
}
#[doc(hidden)]
pub struct NorEagerMarker;
impl<In, A, B> Combine<A, B> for NorEagerMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(!(a(input, data).unwrap_or(false) | b(input, data).unwrap_or(false)))
}
}
#[doc(hidden)]
pub struct OrElseMarker;
impl<In, A, B> Combine<A, B> for OrElseMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(a(input, data).unwrap_or(false) || b(input, data).unwrap_or(false))
}
}
#[doc(hidden)]
pub struct OrEagerMarker;
impl<In, A, B> Combine<A, B> for OrEagerMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(a(input, data).unwrap_or(false) | b(input, data).unwrap_or(false))
}
}
#[doc(hidden)]
pub struct XnorMarker;
impl<In, A, B> Combine<A, B> for XnorMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(!(a(input, data).unwrap_or(false) ^ b(input, data).unwrap_or(false)))
}
}
#[doc(hidden)]
pub struct XorMarker;
impl<In, A, B> Combine<A, B> for XorMarker
where
for<'a> In: SystemInput<Inner<'a>: Copy>,
A: System<In = In, Out = bool>,
B: System<In = In, Out = bool>,
{
type In = In;
type Out = bool;
fn combine<T>(
input: <Self::In as SystemInput>::Inner<'_>,
data: &mut T,
a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
) -> Result<Self::Out, RunSystemError> {
Ok(a(input, data).unwrap_or(false) ^ b(input, data).unwrap_or(false))
}
}
#[cfg(test)]
mod tests {
use super::{common_conditions::*, SystemCondition};
use crate::error::{BevyError, ErrorContext, FallbackErrorHandler};
use crate::{
change_detection::{Res, ResMut},
component::Component,
message::Message,
query::With,
schedule::{IntoScheduleConfigs, Schedule},
system::{IntoSystem, Local, System},
world::World,
};
use bevy_ecs_macros::{Resource, SystemSet};
#[derive(Resource, Default)]
struct Counter(usize);
fn increment_counter(mut counter: ResMut<Counter>) {
counter.0 += 1;
}
fn double_counter(mut counter: ResMut<Counter>) {
counter.0 *= 2;
}
fn every_other_time(mut has_ran: Local<bool>) -> bool {
*has_ran = !*has_ran;
*has_ran
}
#[test]
fn run_condition() {
let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();
schedule.add_systems(increment_counter.run_if(every_other_time));
schedule.run(&mut world);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 1);
schedule.run(&mut world);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 2);
schedule.add_systems(increment_counter.run_if(not(every_other_time)));
schedule.run(&mut world);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 4);
schedule.run(&mut world);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 6);
}
#[test]
fn combinators_with_maybe_failing_condition() {
#![allow(
clippy::nonminimal_bool,
clippy::overly_complex_bool_expr,
reason = "Trailing `|| false` and `&& true` are used in this test to visually remain consistent with the combinators"
)]
use crate::system::RunSystemOnce;
use alloc::sync::Arc;
use std::sync::Mutex;
#[derive(Component)]
struct Vacant;
#[derive(Resource, Default)]
struct Counter(Arc<Mutex<usize>>);
const FALSE: usize = 2;
const TRUE: usize = 3;
fn is_true_inc(counter: Res<Counter>) -> bool {
test_true(&counter)
}
fn is_false_inc(counter: Res<Counter>) -> bool {
test_false(&counter)
}
fn vacant(_: crate::system::Single<&Vacant>) -> bool {
true
}
fn test_true(counter: &Counter) -> bool {
*counter.0.lock().unwrap() *= TRUE;
true
}
fn test_false(counter: &Counter) -> bool {
*counter.0.lock().unwrap() *= FALSE;
false
}
fn logic_call_result(f: impl FnOnce(&Counter) -> bool) -> (usize, bool) {
let counter = Counter(Arc::new(Mutex::new(1)));
let result = f(&counter);
(*counter.0.lock().unwrap(), result)
}
assert_eq!(
logic_call_result(|c| test_true(c) || test_false(c)),
(TRUE.pow(1) * FALSE.pow(0), true)
);
let mut world = World::new();
world.init_resource::<Counter>();
assert!(world.query::<&Vacant>().iter(&world).next().is_none());
assert!(matches!(
world.run_system_once((|| true).or_else(vacant)),
Ok(true)
));
assert!(RunSystemOnce::run_system_once(&mut world, vacant).is_err());
#[track_caller]
fn assert_system<Marker>(
world: &mut World,
system: impl IntoSystem<(), bool, Marker>,
equivalent_to: impl FnOnce(&Counter) -> bool,
) {
use crate::system::System;
*world.resource::<Counter>().0.lock().unwrap() = 1;
let system = IntoSystem::into_system(system);
let name = system.name();
let out = RunSystemOnce::run_system_once(&mut *world, system).unwrap_or(false);
let (expected_counter, expected) = logic_call_result(equivalent_to);
let caller = core::panic::Location::caller();
let counter = *world.resource::<Counter>().0.lock().unwrap();
assert_eq!(
out,
expected,
"At {}:{} System `{name}` yielded unexpected value `{out}`, expected `{expected}`",
caller.file(),
caller.line(),
);
assert_eq!(
counter, expected_counter,
"At {}:{} System `{name}` did not increment counter as expected: expected `{expected_counter}`, got `{counter}`",
caller.file(),
caller.line(),
);
}
assert_system(&mut world, is_true_inc.or_else(vacant), |c| {
test_true(c) || false
});
assert_system(&mut world, is_true_inc.nor_else(vacant), |c| {
!(test_true(c) || false)
});
assert_system(&mut world, is_true_inc.xor(vacant), |c| {
test_true(c) ^ false
});
assert_system(&mut world, is_true_inc.xnor(vacant), |c| {
!(test_true(c) ^ false)
});
assert_system(&mut world, is_true_inc.and_then(vacant), |c| {
test_true(c) && false
});
assert_system(&mut world, is_true_inc.nand_then(vacant), |c| {
!(test_true(c) && false)
});
assert_system(&mut world, vacant.or_else(is_true_inc), |c| {
false || test_true(c)
});
assert_system(&mut world, vacant.nor_else(is_true_inc), |c| {
!(false || test_true(c))
});
assert_system(&mut world, vacant.xor(is_true_inc), |c| {
false ^ test_true(c)
});
assert_system(&mut world, vacant.xnor(is_true_inc), |c| {
!(false ^ test_true(c))
});
assert_system(&mut world, vacant.and_then(is_true_inc), |c| {
false && test_true(c)
});
assert_system(&mut world, vacant.nand_then(is_true_inc), |c| {
!(false && test_true(c))
});
assert_system(&mut world, is_true_inc.or_else(is_false_inc), |c| {
test_true(c) || test_false(c)
});
assert_system(&mut world, is_true_inc.nor_else(is_false_inc), |c| {
!(test_true(c) || test_false(c))
});
assert_system(&mut world, is_true_inc.xor(is_false_inc), |c| {
test_true(c) ^ test_false(c)
});
assert_system(&mut world, is_true_inc.xnor(is_false_inc), |c| {
!(test_true(c) ^ test_false(c))
});
assert_system(&mut world, is_true_inc.and_then(is_false_inc), |c| {
test_true(c) && test_false(c)
});
assert_system(&mut world, is_true_inc.nand_then(is_false_inc), |c| {
!(test_true(c) && test_false(c))
});
assert_system(&mut world, is_false_inc.or_else(vacant), |c| {
test_false(c) || false
});
assert_system(&mut world, is_false_inc.nor_else(vacant), |c| {
!(test_false(c) || false)
});
assert_system(&mut world, is_false_inc.xor(vacant), |c| {
test_false(c) ^ false
});
assert_system(&mut world, is_false_inc.xnor(vacant), |c| {
!(test_false(c) ^ false)
});
assert_system(&mut world, is_false_inc.and_then(vacant), |c| {
test_false(c) && false
});
assert_system(&mut world, is_false_inc.nand_then(vacant), |c| {
!(test_false(c) && false)
});
assert_system(&mut world, is_true_inc.or_else(is_true_inc), |c| {
test_true(c) || test_true(c)
});
assert_system(&mut world, is_true_inc.nor_else(is_true_inc), |c| {
!(test_true(c) || test_true(c))
});
assert_system(&mut world, is_true_inc.xor(is_true_inc), |c| {
test_true(c) ^ test_true(c)
});
assert_system(&mut world, is_true_inc.xnor(is_true_inc), |c| {
!(test_true(c) ^ test_true(c))
});
assert_system(&mut world, is_true_inc.and_then(is_true_inc), |c| {
test_true(c) && test_true(c)
});
assert_system(&mut world, is_true_inc.nand_then(is_true_inc), |c| {
!(test_true(c) && test_true(c))
});
assert_system(&mut world, is_false_inc.or_else(is_false_inc), |c| {
test_false(c) || test_false(c)
});
assert_system(&mut world, is_false_inc.nor_else(is_false_inc), |c| {
!(test_false(c) || test_false(c))
});
assert_system(&mut world, is_false_inc.xor(is_false_inc), |c| {
test_false(c) ^ test_false(c)
});
assert_system(&mut world, is_false_inc.xnor(is_false_inc), |c| {
!(test_false(c) ^ test_false(c))
});
assert_system(&mut world, is_false_inc.and_then(is_false_inc), |c| {
test_false(c) && test_false(c)
});
assert_system(&mut world, is_false_inc.nand_then(is_false_inc), |c| {
!(test_false(c) && test_false(c))
});
}
#[test]
fn run_condition_combinators() {
let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();
schedule.add_systems(
(
increment_counter.run_if(every_other_time.and_eager(|| true)), increment_counter.run_if(every_other_time.nand_eager(|| false)), double_counter.run_if(every_other_time.nor_eager(|| false)), increment_counter.run_if(every_other_time.or_eager(|| true)), increment_counter.run_if(every_other_time.xnor(|| true)), double_counter.run_if(every_other_time.xnor(|| false)), increment_counter.run_if(every_other_time.xor(|| false)), double_counter.run_if(every_other_time.xor(|| true)), )
.chain(),
);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 5);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 52);
}
#[test]
fn multiple_run_conditions() {
let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();
schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true));
schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false));
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 1);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 1);
}
#[test]
fn multiple_run_conditions_is_and_operation() {
let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();
schedule.add_systems(
increment_counter
.run_if(every_other_time)
.run_if(not(every_other_time)),
);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 0);
schedule.run(&mut world);
assert_eq!(world.resource::<Counter>().0, 0);
}
#[derive(Component)]
struct TestComponent;
#[derive(Message)]
struct TestMessage;
#[derive(Resource)]
struct TestResource(());
fn test_system() {}
#[test]
fn distributive_run_if_compiles() {
Schedule::default().add_systems(
(test_system, test_system)
.distributive_run_if(run_once)
.distributive_run_if(resource_exists::<TestResource>)
.distributive_run_if(resource_added::<TestResource>)
.distributive_run_if(resource_changed::<TestResource>)
.distributive_run_if(resource_exists_and_changed::<TestResource>)
.distributive_run_if(resource_changed_or_removed::<TestResource>)
.distributive_run_if(resource_removed::<TestResource>)
.distributive_run_if(on_message::<TestMessage>)
.distributive_run_if(any_with_component::<TestComponent>)
.distributive_run_if(any_match_filter::<With<TestComponent>>)
.distributive_run_if(not(run_once)),
);
}
#[test]
fn run_if_error_contains_system() {
let mut world = World::new();
world.insert_resource(FallbackErrorHandler(my_error_handler));
#[derive(Resource)]
struct MyResource;
fn condition(_res: Res<MyResource>) -> bool {
true
}
fn my_error_handler(_: BevyError, ctx: ErrorContext) {
let a = IntoSystem::into_system(system_a);
let b = IntoSystem::into_system(system_b);
assert!(
matches!(ctx, ErrorContext::RunCondition { system, on_set, .. } if (on_set && system == b.name()) || (!on_set && system == a.name()))
);
}
fn system_a() {}
fn system_b() {}
let mut schedule = Schedule::default();
schedule.add_systems(system_a.run_if(condition));
schedule.run(&mut world);
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
struct Set;
let mut schedule = Schedule::default();
schedule
.add_systems((system_b,).in_set(Set))
.configure_sets(Set.run_if(condition));
schedule.run(&mut world);
}
}