#![warn(clippy::all)]
#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
use bevy::prelude::*;
#[cfg(feature = "derive")]
pub use bevy_cleanup_derive::Cleanup;
pub trait Cleanup: Component {}
pub trait AddStateCleanup {
fn add_state_cleanup<S: States, C: Cleanup>(&mut self, variant: S) -> &mut Self;
}
impl AddStateCleanup for App {
fn add_state_cleanup<S: States, C: Cleanup>(&mut self, variant: S) -> &mut Self {
let cleanup = move |mut commands: Commands, query: Query<Entity, With<C>>| {
for entity in &query {
commands.entity(entity).despawn_recursive();
}
};
self.add_systems(OnExit(variant), cleanup)
}
}
#[cfg(test)]
mod tests {
use bevy::prelude::*;
use super::{Cleanup, AddStateCleanup};
use crate as bevy_cleanup;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, States)]
enum AppState {
#[default]
Menu,
Game,
}
#[derive(Component, Cleanup)]
struct CleanupMenu;
#[derive(Component, Cleanup)]
struct CleanupGame;
fn setup_menu(mut commands: Commands) {
commands.spawn(CleanupMenu);
}
fn setup_game(mut commands: Commands) {
commands.spawn(CleanupGame);
commands.spawn(CleanupGame);
}
fn app() -> App {
let mut app = App::new();
app
.add_state::<AppState>()
.add_state_cleanup::<_, CleanupMenu>(AppState::Menu)
.add_state_cleanup::<_, CleanupGame>(AppState::Game)
.add_systems(OnEnter(AppState::Menu), setup_menu)
.add_systems(OnEnter(AppState::Game), setup_game);
app
}
#[test]
fn remove_on_exit() {
let mut app = app();
assert_eq!(0, app.world.entities().len());
app.update();
assert_eq!(1, app.world.entities().len());
app.insert_resource(NextState(Some(AppState::Game)));
app.update();
assert_eq!(2, app.world.entities().len());
app.insert_resource(NextState(Some(AppState::Menu)));
app.update();
assert_eq!(1, app.world.entities().len());
}
#[test]
fn remove_on_reenter() {
let mut app = app();
assert_eq!(0, app.world.entities().len());
app.update();
assert_eq!(1, app.world.entities().len());
app.insert_resource(NextState(Some(AppState::Menu)));
app.update();
assert_eq!(1, app.world.entities().len());
}
}