use core::marker::PhantomData;
use bevy_ecs::{
schedule::{IntoScheduleConfigs, ScheduleConfigs, SystemCondition},
system::ScheduleSystem,
};
use crate::{
access::{CurrentRef, FlushRef, NextRef},
schedule::ResolveStateSystems,
state::State,
};
pub trait StatePattern<S: State>: 'static + Send + Sync + Sized {
fn matches(&self, state: &S) -> bool;
fn will_update(self) -> impl 'static + Send + Sync + Fn(CurrentRef<S>) -> bool {
self.will_exit()
}
fn on_update<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems.run_if(self.will_update())
}
fn will_exit(self) -> impl 'static + Send + Sync + Fn(CurrentRef<S>) -> bool {
move |state| state.is_in(&self)
}
fn on_exit<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems
.run_if(self.will_exit())
.in_set(ResolveStateSystems::<S>::AnyFlush)
.in_set(ResolveStateSystems::<S>::Exit)
}
fn will_disable(self) -> impl 'static + Send + Sync + Fn(FlushRef<S>) -> bool {
move |state| state.will_disable(&self)
}
fn on_disable<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems
.run_if(self.will_disable())
.in_set(ResolveStateSystems::<S>::AnyFlush)
.in_set(ResolveStateSystems::<S>::Exit)
}
fn will_enter(self) -> impl 'static + Send + Sync + Fn(NextRef<S>) -> bool {
move |state| state.will_be_in(&self)
}
fn on_enter<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems
.run_if(self.will_enter())
.in_set(ResolveStateSystems::<S>::AnyFlush)
.in_set(ResolveStateSystems::<S>::Enter)
}
fn will_enable(self) -> impl 'static + Send + Sync + Fn(FlushRef<S>) -> bool {
move |state| state.will_enable(&self)
}
fn on_enable<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems
.run_if(S::is_triggered.and(self.will_enable()))
.in_set(ResolveStateSystems::<S>::AnyFlush)
.in_set(ResolveStateSystems::<S>::Enter)
}
}
pub trait StatePatternExtClone<S: State>: StatePattern<S> + Clone {
fn on_edge<M1, M2>(
self,
exit_systems: impl IntoScheduleConfigs<ScheduleSystem, M1>,
enter_systems: impl IntoScheduleConfigs<ScheduleSystem, M2>,
) -> ScheduleConfigs<ScheduleSystem> {
(
self.clone().on_exit(exit_systems),
self.on_enter(enter_systems),
)
.into_configs()
}
}
impl<S: State, P: StatePattern<S> + Clone> StatePatternExtClone<S> for P {}
pub trait StatePatternExtEq<S: State + Eq>: StatePattern<S> {
fn will_refresh(self) -> impl 'static + Send + Sync + Fn(FlushRef<S>) -> bool {
move |state| state.will_refresh(&self)
}
fn on_refresh<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems
.run_if(self.will_refresh())
.in_set(ResolveStateSystems::<S>::AnyFlush)
.in_set(ResolveStateSystems::<S>::Trans)
}
}
impl<S: State + Eq, P: StatePattern<S>> StatePatternExtEq<S> for P {}
impl<S: State + Eq> StatePattern<S> for S {
fn matches(&self, state: &S) -> bool {
self == state
}
}
pub struct AnyStatePattern<S: State>(pub(crate) PhantomData<S>);
impl<S: State> Clone for AnyStatePattern<S> {
fn clone(&self) -> Self {
Self(PhantomData)
}
}
impl<S: State> StatePattern<S> for AnyStatePattern<S> {
fn matches(&self, _state: &S) -> bool {
true
}
}
#[derive(Clone)]
pub struct FnStatePattern<S: State, F>(F, PhantomData<S>)
where
F: 'static + Send + Sync + Fn(&S) -> bool;
impl<S: State, F> StatePattern<S> for FnStatePattern<S, F>
where
F: 'static + Send + Sync + Fn(&S) -> bool,
{
fn matches(&self, state: &S) -> bool {
self.0(state)
}
}
impl<S: State, F> FnStatePattern<S, F>
where
F: 'static + Send + Sync + Fn(&S) -> bool,
{
pub fn new(f: F) -> Self {
Self(f, PhantomData)
}
}
pub trait StateTransPattern<S: State>: 'static + Send + Sync + Sized {
fn matches(&self, old: &S, new: &S) -> bool;
fn will_trans(self) -> impl 'static + Send + Sync + Fn(FlushRef<S>) -> bool {
move |state| state.will_trans(&self)
}
fn on_exit<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems
.run_if(self.will_trans())
.in_set(ResolveStateSystems::<S>::AnyFlush)
.in_set(ResolveStateSystems::<S>::Exit)
}
fn on_trans<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems
.run_if(self.will_trans())
.in_set(ResolveStateSystems::<S>::AnyFlush)
.in_set(ResolveStateSystems::<S>::Trans)
}
fn on_enter<M>(
self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> ScheduleConfigs<ScheduleSystem> {
systems
.run_if(self.will_trans())
.in_set(ResolveStateSystems::<S>::AnyFlush)
.in_set(ResolveStateSystems::<S>::Enter)
}
}
pub trait StateTransPatternExtClone<S: State>: StateTransPattern<S> + Clone {
fn on_edge<M1, M2>(
self,
exit_systems: impl IntoScheduleConfigs<ScheduleSystem, M1>,
enter_systems: impl IntoScheduleConfigs<ScheduleSystem, M2>,
) -> ScheduleConfigs<ScheduleSystem> {
(
self.clone().on_exit(exit_systems),
self.on_enter(enter_systems),
)
.into_configs()
}
}
impl<S: State, P: StateTransPattern<S> + Clone> StateTransPatternExtClone<S> for P {}
impl<S: State, P1: StatePattern<S>, P2: StatePattern<S>> StateTransPattern<S> for (P1, P2) {
fn matches(&self, old: &S, new: &S) -> bool {
self.0.matches(old) && self.1.matches(new)
}
}
#[derive(Clone)]
pub struct AnyStateTransPattern<S: State>(pub(crate) PhantomData<S>);
impl<S: State> StateTransPattern<S> for AnyStateTransPattern<S> {
fn matches(&self, _old: &S, _new: &S) -> bool {
true
}
}
#[derive(Clone)]
pub struct FnStateTransPattern<S: State, F>(F, PhantomData<S>)
where
F: 'static + Send + Sync + Fn(&S, &S) -> bool;
impl<S: State, F> StateTransPattern<S> for FnStateTransPattern<S, F>
where
F: 'static + Send + Sync + Fn(&S, &S) -> bool,
{
fn matches(&self, old: &S, new: &S) -> bool {
self.0(old, new)
}
}
impl<S: State, F> FnStateTransPattern<S, F>
where
F: 'static + Send + Sync + Fn(&S, &S) -> bool,
{
pub fn new(f: F) -> Self {
Self(f, PhantomData)
}
}
#[macro_export]
macro_rules! state {
($state:pat $(if $guard:expr)? $(,)?) => {
pyri_state::pattern::FnStatePattern::new(
|state| matches!(state, $state $(if $guard)?),
)
};
($old:pat => $new:pat $(if $guard:expr)? $(,)?) => {
pyri_state::pattern::FnStateTransPattern::new(
|old, new| matches!((old, new), ($old, $new) $(if $guard)?),
)
};
}