use crate::prelude::*;
use bevy::ecs::component::Immutable;
use bevy::ecs::component::StorageType;
use bevy::ecs::lifecycle::ComponentHook;
use bevy::ecs::lifecycle::HookContext;
use bevy::ecs::system::Command;
use bevy::ecs::world::DeferredWorld;
use core::marker::PhantomData;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Maybe<B: Bundle>(pub Option<B>);
impl<B: Bundle> Component for Maybe<B> {
type Mutability = Immutable;
const STORAGE_TYPE: StorageType = StorageType::SparseSet;
fn on_add() -> Option<ComponentHook> { Some(maybe_hook::<B>) }
}
impl<B: Bundle> Maybe<B> {
pub const NONE: Self = Self(None);
pub const fn new(bundle: B) -> Self { Self(Some(bundle)) }
pub fn into_inner(self) -> Option<B> { self.0 }
}
impl<B: Bundle> Default for Maybe<B> {
fn default() -> Self { Self::NONE }
}
fn maybe_hook<B: Bundle>(
mut world: DeferredWorld<'_>,
HookContext { entity, .. }: HookContext,
) {
world.commands().queue(MaybeCommand {
entity,
_phantom: PhantomData::<B>,
});
}
struct MaybeCommand<B> {
entity: Entity,
_phantom: PhantomData<B>,
}
impl<B: Bundle> Command for MaybeCommand<B> {
fn apply(self, world: &mut World) {
let Ok(mut entity_mut) = world.get_entity_mut(self.entity) else {
#[cfg(debug_assertions)]
panic!("Entity with Maybe component not found");
#[cfg(not(debug_assertions))]
return;
};
let Some(maybe_component) = entity_mut.take::<Maybe<B>>() else {
#[cfg(debug_assertions)]
panic!("Maybe component not found");
#[cfg(not(debug_assertions))]
return;
};
if let Some(bundle) = maybe_component.into_inner() {
entity_mut.insert(bundle);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use bevy::ecs::system::RunSystemOnce;
#[derive(Component)]
struct A;
#[derive(Bundle)]
struct TestBundle {
maybe_a: Maybe<A>,
}
#[test]
fn maybe_some() {
let mut world = World::new();
let entity = world
.spawn(TestBundle {
maybe_a: Maybe::new(A),
})
.id();
world.flush();
world.get::<A>(entity).is_some().xpect_true();
world.get::<Maybe<A>>(entity).is_none().xpect_true();
}
#[test]
fn maybe_none() {
let mut world = World::new();
let entity = world
.spawn(TestBundle {
maybe_a: Maybe::NONE,
})
.id();
world.flush();
world.get::<A>(entity).is_none().xpect_true();
world.get::<Maybe<A>>(entity).is_none().xpect_true();
}
#[test]
fn maybe_system() {
let mut world = World::new();
let entity_with_component = world
.run_system_once(|mut commands: Commands| -> Entity {
commands
.spawn(TestBundle {
maybe_a: Maybe::new(A),
})
.id()
})
.unwrap();
let entity_ref = world.get_entity(entity_with_component).unwrap();
entity_ref.contains::<A>().xpect_true();
(!entity_ref.contains::<Maybe<A>>()).xpect_true();
let entity_without_component = world
.run_system_once(|mut commands: Commands| -> Entity {
commands
.spawn(TestBundle {
maybe_a: Maybe::NONE,
})
.id()
})
.unwrap();
let entity_ref = world.get_entity(entity_without_component).unwrap();
(!entity_ref.contains::<A>()).xpect_true();
(!entity_ref.contains::<Maybe<A>>()).xpect_true();
}
}