use bevy_ecs::{
component::ComponentId, intern::Interned, lifecycle::HookContext, prelude::*,
world::DeferredWorld,
};
pub use bevy_seedling_macros::PoolLabel;
bevy_ecs::define_label!(
PoolLabel,
POOL_LABEL_INTERNER
);
#[derive(PoolLabel, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
pub struct DefaultPool;
pub type InternedPoolLabel = Interned<dyn PoolLabel>;
#[derive(Component, Debug, Clone)]
#[component(on_remove = Self::on_remove)]
pub struct PoolLabelContainer {
pub(crate) label: InternedPoolLabel,
pub(crate) label_id: ComponentId,
}
impl PoolLabelContainer {
pub fn new<T: PoolLabel>(label: &T, id: ComponentId) -> Self {
Self {
label: label.intern(),
label_id: id,
}
}
fn on_remove(mut world: DeferredWorld, context: HookContext) {
let id = world
.get::<PoolLabelContainer>(context.entity)
.unwrap()
.label_id;
world.commands().queue(move |world: &mut World| {
let Ok(mut entity) = world.get_entity_mut(context.entity) else {
return;
};
entity.remove_by_id(id);
});
}
}
#[doc(hidden)]
pub fn insert_pool_label<L: PoolLabel + Component>(mut world: DeferredWorld, context: HookContext) {
let value = world.get::<L>(context.entity).unwrap();
let container = PoolLabelContainer::new(value, context.component_id);
world.commands().entity(context.entity).insert(container);
}
#[doc(hidden)]
pub fn remove_pool_label<L: PoolLabel + Component>(mut world: DeferredWorld, context: HookContext) {
world.commands().queue(move |world: &mut World| {
let Ok(mut entity) = world.get_entity_mut(context.entity) else {
return;
};
let Some(container) = entity.get::<PoolLabelContainer>() else {
return;
};
if container.label_id == context.component_id {
entity.remove::<PoolLabelContainer>();
}
});
}
#[cfg(test)]
mod test {
use super::*;
use crate::test::prepare_app;
#[derive(PoolLabel, Debug, PartialEq, Eq, Hash, Clone)]
struct TestLabel;
#[test]
fn test_label_removes_container() {
let mut app = prepare_app(|| ());
let world = app.world_mut();
let entity = world.spawn(TestLabel).id();
assert!(world.entity(entity).contains::<PoolLabelContainer>());
world.commands().entity(entity).remove::<TestLabel>();
world.flush();
assert!(!world.entity(entity).contains::<PoolLabelContainer>());
}
#[test]
fn test_no_spurious_label_remove() {
let mut app = prepare_app(|| ());
let world = app.world_mut();
let entity = world.spawn(TestLabel).id();
assert!(world.entity(entity).contains::<PoolLabelContainer>());
world
.commands()
.entity(entity)
.remove::<TestLabel>()
.insert(TestLabel);
world.flush();
assert!(world.entity(entity).contains::<PoolLabelContainer>());
}
#[test]
fn test_container_removes_label() {
let mut app = prepare_app(|| ());
let world = app.world_mut();
let entity = world.spawn(TestLabel).id();
assert!(world.entity(entity).contains::<PoolLabelContainer>());
world
.commands()
.entity(entity)
.remove::<PoolLabelContainer>();
world.flush();
assert!(!world.entity(entity).contains::<TestLabel>());
}
#[test]
fn test_no_spurious_container_remove() {
let mut app = prepare_app(|| ());
let world = app.world_mut();
let entity = world.spawn(TestLabel).id();
assert!(world.entity(entity).contains::<PoolLabelContainer>());
world
.commands()
.entity(entity)
.remove::<PoolLabelContainer>()
.insert(TestLabel);
world.flush();
assert!(world.entity(entity).contains::<PoolLabelContainer>());
}
}