use std::any::type_name; use std::marker::PhantomData; use paste::paste; use seq_macro::seq; use util_macros::sub; use crate::component::{ Component, Handle as ComponentHandle, HandleMut as ComponentHandleMut, }; use crate::entity::Handle as EntityHandle; use crate::pair::Wildcard; use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery}; use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::tuple::{Concat, Tuple}; use crate::uid::Uid; use crate::util::array_vec::ArrayVec; use crate::util::Array; use crate::{pair, World}; mod reexports { pub use crate::component::storage::{ SearchTerms as Terms, TermMetadata, Traversal, TraversalKind, TraversalResult, }; } pub use reexports::*; pub mod flexible; pub mod term; // A term tuple type can have a maximum of 17 elements pub const MAX_TERM_CNT: usize = 17; #[derive(Debug)] pub struct Query<'world, TermsT> { inner: FlexibleQuery<'world, MAX_TERM_CNT>, _pd: PhantomData, } impl<'world, TermsT> Query<'world, TermsT> { /// Iterates over the entities matching this query, the iterator item being the entity /// components. #[must_use] pub fn iter<'this>(&'this self) -> Iter<'this, TermsT> where TermsT: TermTuple<'this>, 'world: 'this, { tracing::trace!("Searching for {}", std::any::type_name::()); Iter { world: self.inner.world(), inner: self.inner.iter(), comps_pd: PhantomData, } } /// Iterates over the entities matching this query, the iterator item being the entity /// [`Uid`] and the matching entity components. #[must_use] pub fn iter_with_euids<'this>(&'this self) -> ComponentAndEuidIter<'this, TermsT> where TermsT: TermTuple<'this>, 'world: 'this, { tracing::trace!("Searching for {}", std::any::type_name::()); ComponentAndEuidIter { world: self.inner.world(), inner: self.inner.iter(), comps_pd: PhantomData, } } /// Returns the UID of the entity at the given query iteration index. #[must_use] pub fn get_entity_uid<'this>(&'this self, entity_index: usize) -> Option where TermsT: TermTuple<'this>, { Some(self.inner.iter().nth(entity_index)?.uid()) } /// Returns a new `Query` created from a [`FlexibleQuery`]. /// /// # Important notes /// The terms in `TermsT` must be compatible with the terms /// in the given [`FlexibleQuery`], otherwise any method call or iterating might /// panic. #[must_use] pub fn from_flexible_query( flexible_query: FlexibleQuery<'world, MAX_TERM_CNT>, ) -> Self { // TODO: Check compatability of terms Self { inner: flexible_query, _pd: PhantomData, } } pub fn into_flexible_query(self) -> FlexibleQuery<'world, MAX_TERM_CNT> { self.inner } pub(crate) fn new(world: &'world World) -> Self where TermsT: TermTuple<'world>, { let mut terms_builder = Terms::builder(); TermsT::apply_terms_to_builder(&mut terms_builder, 0); Self { inner: world.flexible_query(terms_builder.build()), _pd: PhantomData, } } } impl<'query, 'world, TermsT> IntoIterator for &'query Query<'world, TermsT> where TermsT: TermTuple<'query>, 'world: 'query, { type IntoIter = Iter<'query, TermsT>; type Item = TermsT::Fields; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'world, TermsT> SystemParam<'world> for Query<'world, TermsT> where TermsT: TermTuple<'world>, { type Input = (); fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { Self::new(world) } } impl Terms { pub fn builder() -> TermsBuilder { TermsBuilder::default() } } #[derive(Debug, Default)] #[must_use] pub struct TermsBuilder { present: ArrayVec, absent: ArrayVec, traverse: ArrayVec, TERM_CAP>, } #[allow(clippy::return_self_not_must_use)] pub trait TermsBuilderInterface { fn present(self, ids: impl Array) -> Self; fn absent(self, ids: impl Array) -> Self; fn traverse(self, traversals: impl Array> + Clone) -> Self; } macro_rules! impl_terms_builder { ($($impl_content: tt)*) => { impl TermsBuilderInterface for TermsBuilder { $($impl_content)* } impl TermsBuilderInterface for &mut TermsBuilder { $($impl_content)* } }; } impl_terms_builder! { #[allow(unused_mut)] fn present(mut self, mut ids: impl Array) -> Self { if !ids.as_ref().is_sorted() { ids.as_mut().sort(); } if self.present.is_empty() { self.present.extend(ids); return self; } let mut id_iter = ids.into_iter(); while let Some(id) = id_iter.next() { let insert_index = self.present .partition_point(|other_id| *other_id <= id); if insert_index == self.present.len() { self.present.extend([id].into_iter().chain(id_iter)); return self; } self.present .insert(insert_index, id); } self } #[allow(unused_mut)] fn absent(mut self, mut ids: impl Array) -> Self { if !ids.as_ref().is_sorted() { ids.as_mut().sort(); } if self.absent.is_empty() { self.absent.extend(ids); return self; } let mut id_iter = ids.into_iter(); while let Some(id) = id_iter.next() { let insert_index = self.absent .partition_point(|other_id| *other_id <= id); if insert_index == self.absent.len() { self.absent.extend([id].into_iter().chain(id_iter)); return self; } self.absent .insert(insert_index, id); } self } #[allow(unused_mut)] fn traverse(mut self, traversals: impl Array> + Clone) -> Self { self.traverse.extend(traversals.clone()); self.present( traversals .map(|traversal| pair!({ traversal.relation }, Wildcard).id()) ) } } impl TermsBuilder { #[must_use] pub fn build(self) -> Terms { debug_assert!(self.present.is_sorted()); debug_assert!(self.absent.is_sorted()); Terms { present: self.present, absent: self.absent, traverse: self.traverse, } } } pub trait Term<'query> { type Fields: Tuple; fn apply_to_terms_builder( terms_builder: &mut TermsBuilder, term_metadata: TermMetadata, ); fn fields( world: &'query World, search_result: &SearchResult<'query, '_>, term_metadata: TermMetadata, ) -> Self::Fields; } impl<'query, ComponentT: Component> Term<'query> for &ComponentT { type Fields = (ComponentHandle<'query, ComponentT>,); fn apply_to_terms_builder( terms_builder: &mut TermsBuilder, _term_metadata: TermMetadata, ) { terms_builder.present([ComponentT::id()]); } fn fields( _world: &'query World, search_result: &SearchResult<'query, '_>, _term_metadata: TermMetadata, ) -> Self::Fields { assert!(!ComponentT::id().is_pair()); let Some(component) = search_result .entity_handle .get_matching_components(ComponentT::id()) .next() else { panic!( concat!( "Component {} was not found in entity {}. There ", "is most likely a bug in the entity querying" ), type_name::(), search_result.entity_handle.uid() ); }; let component = match ComponentHandle::::from_entity_component_ref(&component) { Ok(component) => component, Err(err) => { panic!( "Creating handle to component {} failed: {err}", type_name::() ); } }; (component,) } } impl<'query, ComponentT: Component> Term<'query> for &mut ComponentT { type Fields = (ComponentHandleMut<'query, ComponentT>,); fn apply_to_terms_builder( terms_builder: &mut TermsBuilder, _term_metadata: TermMetadata, ) { terms_builder.present([ComponentT::id()]); } fn fields( world: &'query World, search_result: &SearchResult<'query, '_>, _term_metadata: TermMetadata, ) -> Self::Fields { assert!(!ComponentT::id().is_pair()); let Some(component) = search_result .entity_handle .get_matching_components(ComponentT::id()) .next() else { panic!( concat!( "Component {} was not found in entity {}. There ", "is most likely a bug in the entity querying" ), type_name::(), search_result.entity_handle.uid() ); }; let component = match ComponentHandleMut::::from_entity_component_ref( &component, world, ) { Ok(component) => component, Err(err) => { panic!( "Creating mut handle to component {} failed: {err}", type_name::() ); } }; (component,) } } impl<'query, TermT, FieldAcc> TermFieldSource<'query, FieldAcc> for TermT where TermT: Term<'query, Fields: Concat>, FieldAcc: Tuple, { type NewFieldAcc = >::Output; fn collect_field( world: &'query World, search_result: &SearchResult<'query, '_>, term_metadata: TermMetadata, fields: FieldAcc, ) -> Self::NewFieldAcc { >::concat( Self::fields(world, search_result, term_metadata), fields, ) } } pub trait TermTuple<'query> { type Fields: Tuple; fn apply_terms_to_builder( terms_builder: &mut TermsBuilder, term_level: u32, ); fn get_fields( world: &'query World, search_result: &SearchResult<'query, '_>, term_level: u32, ) -> Self::Fields; } #[derive(Debug)] #[non_exhaustive] pub struct SearchResult<'query, 'temp> { pub entity_handle: EntityHandle<'query>, pub traversal_results: &'temp [TraversalResult], } pub struct Iter<'query, TermsT> where TermsT: TermTuple<'query>, { world: &'query World, inner: FlexibleQueryIter<'query, MAX_TERM_CNT>, comps_pd: PhantomData, } impl<'query, TermsT> Iterator for Iter<'query, TermsT> where TermsT: TermTuple<'query>, { type Item = TermsT::Fields; fn next(&mut self) -> Option { let entity_handle = self.inner.next()?; Some(TermsT::get_fields( self.world, &SearchResult { entity_handle, traversal_results: self.inner.traversal_results().unwrap(), }, 0, )) } } pub struct ComponentAndEuidIter<'query, TermsT> where TermsT: TermTuple<'query>, { world: &'query World, inner: FlexibleQueryIter<'query, MAX_TERM_CNT>, comps_pd: PhantomData, } impl<'query, TermsT> Iterator for ComponentAndEuidIter<'query, TermsT> where TermsT: TermTuple<'query>, { type Item = (Uid, TermsT::Fields); fn next(&mut self) -> Option { let entity_handle = self.inner.next()?; Some(( entity_handle.uid(), TermsT::get_fields( self.world, &SearchResult { entity_handle, traversal_results: self.inner.traversal_results().unwrap(), }, 0, ), )) } } pub trait TermFieldSource<'query, FieldAcc> { type NewFieldAcc; fn collect_field( world: &'query World, search_result: &SearchResult<'query, '_>, term_metadata: TermMetadata, fields: FieldAcc, ) -> Self::NewFieldAcc; } macro_rules! term_field_source_new_field_acc { (overflow) => { () }; ($index: tt) => { paste! { []::NewFieldAcc } }; } macro_rules! gen_term_tuple_impls { ($c: tt) => { seq!(I in 0..$c { impl<'query, #(Term~I,)*> TermTuple<'query> for (#(Term~I,)*) where #( Term~I: Term<'query> + TermFieldSource< 'query, sub!(I - 1, term_field_source_new_field_acc), NewFieldAcc: Tuple >, )* { type Fields = sub!($c - 1, term_field_source_new_field_acc); #[allow(unused)] fn apply_terms_to_builder( terms_builder: &mut TermsBuilder, term_level: u32, ) { #( Term~I::apply_to_terms_builder( terms_builder, TermMetadata { level: term_level, index: I }, ); )* } #[allow(unused)] fn get_fields( world: &'query World, search_result: &SearchResult<'query, '_>, term_level: u32, ) -> Self::Fields { let fields = (); #( let fields = Term~I::collect_field( world, search_result, TermMetadata { level: term_level, index: I }, fields ); )* fields } } }); }; } seq!(C in 0..17 { gen_term_tuple_impls!(C); });