use core::{default, ffi::c_void};
use crate::core::internals::*;
use crate::core::private::internal_SystemAPI;
use crate::core::*;
use crate::sys;
pub struct ObserverBuilder<'a, P = (), T: QueryTuple = ()> {
desc: sys::ecs_observer_desc_t,
term_builder: TermBuilder,
world: WorldRef<'a>,
event_count: usize,
_phantom: core::marker::PhantomData<&'a (T, P)>,
}
impl<'a, P: ComponentId, T: QueryTuple> ObserverBuilder<'a, P, T> {
pub(crate) fn new(world: impl WorldProvider<'a>) -> Self {
let desc = Default::default();
let mut obj = Self {
desc,
term_builder: TermBuilder::default(),
event_count: 1,
world: world.world(),
_phantom: core::marker::PhantomData,
};
obj.desc.events[0] = P::UnderlyingType::entity_id(world.world());
obj.desc.entity =
unsafe { sys::ecs_entity_init(world.world_ptr_mut(), &Default::default()) };
T::populate(&mut obj);
obj
}
pub fn new_named(world: impl WorldProvider<'a>, name: &str) -> Self {
let name = compact_str::format_compact!("{}\0", name);
let desc = Default::default();
let mut obj = Self {
desc,
term_builder: TermBuilder::default(),
event_count: 1,
world: world.world(),
_phantom: core::marker::PhantomData,
};
let entity_desc: sys::ecs_entity_desc_t = sys::ecs_entity_desc_t {
name: name.as_ptr() as *const _,
sep: SEPARATOR.as_ptr(),
root_sep: SEPARATOR.as_ptr(),
..default::Default::default()
};
obj.desc.events[0] = P::entity_id(world.world());
obj.desc.entity = unsafe { sys::ecs_entity_init(obj.world_ptr_mut(), &entity_desc) };
T::populate(&mut obj);
obj
}
}
impl<'a, P, T: QueryTuple> ObserverBuilder<'a, P, T> {
pub(crate) fn new_untyped(world: impl WorldProvider<'a>) -> ObserverBuilder<'a, (), T> {
let desc = Default::default();
let mut obj = ObserverBuilder {
desc,
term_builder: TermBuilder::default(),
event_count: 0,
world: world.world(),
_phantom: core::marker::PhantomData,
};
obj.desc.entity =
unsafe { sys::ecs_entity_init(world.world_ptr_mut(), &Default::default()) };
T::populate(&mut obj);
obj
}
#[expect(dead_code)]
pub(crate) fn new_from_desc(
world: impl WorldProvider<'a>,
desc: sys::ecs_observer_desc_t,
) -> Self {
let mut obj = Self {
desc,
term_builder: TermBuilder::default(),
event_count: 0,
world: world.world(),
_phantom: core::marker::PhantomData,
};
if obj.desc.entity == 0 {
obj.desc.entity =
unsafe { sys::ecs_entity_init(world.world_ptr_mut(), &Default::default()) };
}
T::populate(&mut obj);
obj
}
}
impl<P, T: QueryTuple> ObserverBuilder<'_, P, T> {
pub fn set_observer_flags(&mut self, flags: ObserverFlags) -> &mut Self {
self.desc.flags_ |= flags.bits();
self
}
pub fn add_event(&mut self, event: impl IntoEntity) -> &mut ObserverBuilder<'_, (), T> {
let event = *event.into_entity(self.world);
self.desc.events[self.event_count] = event;
self.event_count += 1;
unsafe { core::mem::transmute(self) }
}
pub fn yield_existing(&mut self) -> &mut Self {
self.desc.yield_existing = true;
self
}
}
#[doc(hidden)]
impl<'a, P, T: QueryTuple> internals::QueryConfig<'a> for ObserverBuilder<'a, P, T> {
#[inline(always)]
fn term_builder(&self) -> &TermBuilder {
&self.term_builder
}
#[inline(always)]
fn term_builder_mut(&mut self) -> &mut TermBuilder {
&mut self.term_builder
}
#[inline(always)]
fn query_desc(&self) -> &sys::ecs_query_desc_t {
&self.desc.query
}
#[inline(always)]
fn query_desc_mut(&mut self) -> &mut sys::ecs_query_desc_t {
&mut self.desc.query
}
#[inline(always)]
fn count_generic_terms(&self) -> i32 {
T::COUNT
}
}
impl<'a, P, T: QueryTuple> TermBuilderImpl<'a> for ObserverBuilder<'a, P, T> {}
impl<'a, P, T: QueryTuple> QueryBuilderImpl<'a> for ObserverBuilder<'a, P, T> {}
impl<'a, P, T> Builder<'a> for ObserverBuilder<'a, P, T>
where
T: QueryTuple,
{
type BuiltType = Observer<'a>;
#[doc(hidden)]
fn build(&mut self) -> Self::BuiltType {
if self.desc.callback.is_none() && self.desc.run.is_none() {
panic!("you should not call this fn manually. Use `.each` , `.run` instead")
}
if self.desc.events[0] == flecs::OnAdd::ID {
for term in self.desc.query.terms.iter_mut() {
if (term.first.id | term.id | term.second.id | term.src.id) == 0 {
break;
}
if term.inout == sys::ecs_inout_kind_t_EcsInOutDefault as i16 {
term.inout = sys::ecs_inout_kind_t_EcsInOutNone as i16;
}
}
}
let observer = Observer::new(self.world(), self.desc);
for s in self.term_builder.str_ptrs_to_free.iter_mut() {
unsafe { core::mem::ManuallyDrop::drop(s) };
}
self.term_builder.str_ptrs_to_free.clear();
observer
}
}
impl<'a, P, T: QueryTuple> WorldProvider<'a> for ObserverBuilder<'a, P, T> {
fn world(&self) -> WorldRef<'a> {
self.world
}
}
implement_reactor_api!(ObserverBuilder<'a, P, T>);