use crate::{
buffer::ComponentBuffer,
component::{ComponentDesc, ComponentValue},
error::Result,
relation::RelationExt,
CommandBuffer, Component, Entity, World,
};
use alloc::{boxed::Box, vec::Vec};
type ModifyFunc = Box<dyn FnOnce(Entity, &mut EntityBuilder) + Send + Sync>;
struct Child {
builder: EntityBuilder,
modify: ModifyFunc,
}
impl core::fmt::Debug for Child {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.builder.fmt(f)
}
}
impl Child {
fn spawn(mut self, world: &mut World, parent: Entity) -> Entity {
(self.modify)(parent, &mut self.builder);
self.builder.spawn(world)
}
}
#[derive(Debug)]
pub struct EntityBuilder {
buffer: ComponentBuffer,
children: Vec<Child>,
}
impl EntityBuilder {
pub fn new() -> Self {
Self {
buffer: ComponentBuffer::new(),
children: Vec::new(),
}
}
pub fn set<T: ComponentValue>(&mut self, component: Component<T>, value: T) -> &mut Self {
self.buffer.set(component, value);
self
}
pub(crate) unsafe fn set_dyn(&mut self, desc: ComponentDesc, value: *mut u8) -> &mut Self {
self.buffer.set_dyn(desc, value);
self
}
pub fn tag<T: From<()> + ComponentValue>(&mut self, component: Component<T>) -> &mut Self {
self.set(component, ().into())
}
pub fn set_default<T: ComponentValue + Default>(
&mut self,
component: Component<T>,
) -> &mut Self {
self.set(component, Default::default())
}
pub fn set_opt<T: ComponentValue>(
&mut self,
component: Component<T>,
value: Option<T>,
) -> &mut Self {
if let Some(value) = value {
self.buffer.set(component, value);
}
self
}
pub fn get_mut<T: ComponentValue>(&mut self, component: Component<T>) -> Option<&mut T> {
self.buffer.get_mut(component)
}
pub fn get<T: ComponentValue>(&self, component: Component<T>) -> Option<&T> {
self.buffer.get(component)
}
pub fn has<T: ComponentValue>(&self, component: Component<T>) -> bool {
self.buffer.has(component)
}
pub fn remove<T: ComponentValue>(&mut self, component: Component<T>) -> Option<T> {
self.buffer.remove(component)
}
pub fn attach_with<T: ComponentValue>(
&mut self,
relation: impl RelationExt<T> + ComponentValue,
value: T,
other: impl Into<Self>,
) -> &mut Self {
self.children.push(Child {
builder: other.into(),
modify: Box::new(move |parent, builder| {
builder.set(relation.of(parent), value);
}),
});
self
}
pub fn attach<T: ComponentValue + Default>(
&mut self,
relation: impl RelationExt<T> + ComponentValue,
other: impl Into<Self>,
) -> &mut Self {
self.attach_with(relation, Default::default(), other)
}
pub fn spawn(&mut self, world: &mut World) -> Entity {
profile_function!();
let id = world.spawn_with(&mut self.buffer);
self.children.drain(..).for_each(|child| {
child.spawn(world, id);
});
id
}
pub fn spawn_at(&mut self, world: &mut World, id: Entity) -> Result<Entity> {
let (id, _) = world.spawn_at_with(id, &mut self.buffer)?;
self.children.drain(..).for_each(|child| {
child.spawn(world, id);
});
Ok(id)
}
pub fn append_to(&mut self, world: &mut World, id: Entity) -> Result<Entity> {
profile_function!();
world.set_with(id, &mut self.buffer)?;
self.children.drain(..).for_each(|child| {
child.spawn(world, id);
});
Ok(id)
}
pub fn spawn_into(&mut self, cmd: &mut CommandBuffer) {
cmd.spawn(core::mem::take(self));
}
pub fn component_count(&self) -> usize {
self.buffer.len()
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
}
impl Default for EntityBuilder {
fn default() -> Self {
Self::new()
}
}
impl From<&mut EntityBuilder> for EntityBuilder {
fn from(builder: &mut EntityBuilder) -> Self {
core::mem::take(builder)
}
}
#[cfg(test)]
mod test {
use crate::{component, components::name, error::MissingComponent, Entity, Error, World};
#[test]
fn builder() {
use glam::*;
component! {
health: f32,
position: Vec3,
is_player: (),
is_enemy: (),
}
let mut world = World::new();
let mut builder = Entity::builder();
builder
.set(name(), "Player".into())
.set(position(), vec3(0.0, 4.0, 2.0))
.set_opt(is_enemy(), None)
.set_opt(health(), Some(100.0))
.tag(is_player());
assert_eq!(builder.get(name()), Some(&"Player".into()));
assert_eq!(builder.get(health()), Some(&100.0));
builder.remove(health());
assert_eq!(builder.get(health()), None);
builder.set(health(), 50.0);
let id = builder.spawn(&mut world);
assert_eq!(world.get(id, name()).as_deref(), Ok(&"Player".into()));
assert_eq!(world.get(id, health()).as_deref(), Ok(&50.0));
assert_eq!(
world.get(id, is_enemy()).as_deref(),
Err(&Error::MissingComponent(MissingComponent {
id,
desc: is_enemy().desc()
}))
);
}
}