summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-07-23 17:38:51 +0200
committerHampusM <hampus@hampusmat.com>2026-07-23 17:38:51 +0200
commit624e6dd450944e67d84ad7afda54ae2abd277aba (patch)
tree908834c4d666eaf6b5f1fd3670e4951d98af1efe
parent66cbb5d207094799740228c4c913218ddca3a19d (diff)
refactor(engine-ecs): remove distinction between query terms with & without a field
-rw-r--r--engine-ecs/examples/component_changed_event.rs2
-rw-r--r--engine-ecs/src/lib.rs12
-rw-r--r--engine-ecs/src/pair.rs103
-rw-r--r--engine-ecs/src/phase.rs2
-rw-r--r--engine-ecs/src/query.rs302
-rw-r--r--engine-ecs/src/query/flexible.rs24
-rw-r--r--engine-ecs/src/query/term.rs121
-rw-r--r--engine-ecs/src/uid.rs2
-rw-r--r--engine-ecs/tests/query.rs43
9 files changed, 339 insertions, 272 deletions
diff --git a/engine-ecs/examples/component_changed_event.rs b/engine-ecs/examples/component_changed_event.rs
index a963b41..3f6b4a8 100644
--- a/engine-ecs/examples/component_changed_event.rs
+++ b/engine-ecs/examples/component_changed_event.rs
@@ -65,7 +65,7 @@ fn main()
world.step();
- for (mut greeting,) in &world.query::<(&mut Greeting,), ()>() {
+ for (mut greeting,) in &world.query::<(&mut Greeting,)>() {
if greeting.greeting == "Good afternoon" {
greeting.greeting = "Yo yo".to_string();
greeting.set_changed();
diff --git a/engine-ecs/src/lib.rs b/engine-ecs/src/lib.rs
index 3a867ba..97316f9 100644
--- a/engine-ecs/src/lib.rs
+++ b/engine-ecs/src/lib.rs
@@ -45,8 +45,7 @@ use crate::phase::{
};
use crate::query::flexible::Query as FlexibleQuery;
use crate::query::{
- TermWithFieldTuple as QueryTermWithFieldTuple,
- TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
+ TermTuple as QueryTermTuple,
Terms as QueryTerms,
TermsBuilderInterface,
MAX_TERM_CNT as QUERY_MAX_TERM_CNT,
@@ -286,12 +285,11 @@ impl World
extension.collect(extension_collector);
}
- pub fn query<FieldTerms, FieldlessTerms>(
- &self,
- ) -> Query<'_, FieldTerms, FieldlessTerms>
+ pub fn query<'this, TermsT>(
+ &'this self,
+ ) -> Query<'this, TermsT>
where
- FieldTerms: QueryTermWithFieldTuple,
- FieldlessTerms: QueryTermWithoutFieldTuple,
+ TermsT: QueryTermTuple<'this>,
{
Query::new(self)
}
diff --git a/engine-ecs/src/pair.rs b/engine-ecs/src/pair.rs
index 3668f1b..1168f67 100644
--- a/engine-ecs/src/pair.rs
+++ b/engine-ecs/src/pair.rs
@@ -14,10 +14,11 @@ use crate::entity::{
MatchingComponentIter as EntityMatchingComponentIter,
};
use crate::query::{
- TermWithField as QueryTermWithField,
+ Term as QueryTerm,
TermsBuilder as QueryTermsBuilder,
TermsBuilderInterface,
};
+use crate::tuple::Tuple;
use crate::uid::{PairParams as UidPairParams, Uid, With as WithUid};
use crate::util::impl_multiple;
use crate::{Component, EntityComponentRef, World};
@@ -191,12 +192,13 @@ where
}
}
-impl<Relation, Target> QueryTermWithField for Pair<Relation, &Target>
+impl<'world, Relation, Target> QueryTerm<'world> for Pair<Relation, &Target>
where
Relation: Component,
Target: Component,
{
- type Field<'a> = ComponentHandle<'a, Target>;
+ type AddField<Fields: Tuple> =
+ Fields::WithElementAtEnd<ComponentHandle<'world, Target>>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
@@ -205,31 +207,38 @@ where
terms_builder.present([Pair::<Relation, Target>::uid()]);
}
- fn get_field<'world>(
+ fn add_field<Fields: Tuple>(
entity_handle: &EntityHandle<'world>,
_world: &'world World,
- ) -> Self::Field<'world>
+ fields: Fields,
+ ) -> Self::AddField<Fields>
{
let target_component = entity_handle
.get_matching_components(Pair::<Relation, Target>::uid())
.next()
.expect("Not possible");
- Self::Field::from_entity_component_ref(&target_component).unwrap_or_else(|err| {
- panic!(
- "Creating handle to target component {} failed: {err}",
- type_name::<Target>()
- );
- })
+ let target_component = match ComponentHandle::<Target>::from_entity_component_ref(&target_component) {
+ Ok(target_component) => target_component,
+ Err(err) => {
+ panic!(
+ "Creating handle to target component {} failed: {err}",
+ type_name::<Target>()
+ );
+ }
+ };
+
+ fields.with_elem(target_component)
}
}
-impl<Relation, Target> QueryTermWithField for Pair<Relation, &mut Target>
+impl<'world, Relation, Target> QueryTerm<'world> for Pair<Relation, &mut Target>
where
Relation: Component,
Target: Component,
{
- type Field<'a> = ComponentHandleMut<'a, Target>;
+ type AddField<Fields: Tuple> =
+ Fields::WithElementAtEnd<ComponentHandleMut<'world, Target>>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
@@ -238,35 +247,40 @@ where
terms_builder.present([Pair::<Relation, Target>::uid()]);
}
- fn get_field<'world>(
+ fn add_field<Fields: Tuple>(
entity_handle: &EntityHandle<'world>,
world: &'world World,
- ) -> Self::Field<'world>
+ fields: Fields,
+ ) -> Self::AddField<Fields>
{
let target_component = entity_handle
.get_matching_components(Pair::<Relation, Target>::uid())
.next()
.expect("Not possible");
- Self::Field::from_entity_component_ref(&target_component, world).unwrap_or_else(
- |err| {
+ let target_component = match ComponentHandleMut::<Target>::from_entity_component_ref(&target_component, world) {
+ Ok(target_component) => target_component,
+ Err(err) => {
panic!(
- "Creating handle to target component {} failed: {err}",
+ "Creating mut handle to target component {} failed: {err}",
type_name::<Target>()
);
- },
- )
+ }
+ };
+
+ fields.with_elem(target_component)
}
}
-// TODO: implement QueryTermWithField for Pair<&Relation, Target> (or equivalent)
-// TODO: implement QueryTermWithField for Pair<&mut Relation, Target> (or equivalent)
+// TODO: implement QueryTerm for Pair<&Relation, Target> (or equivalent)
+// TODO: implement QueryTerm for Pair<&mut Relation, Target> (or equivalent)
-impl<Relation> QueryTermWithField for Pair<Relation, Wildcard>
+impl<'world, Relation> QueryTerm<'world> for Pair<Relation, Wildcard>
where
Relation: Component,
{
- type Field<'a> = WithWildcard<'a, Relation, Wildcard>;
+ type AddField<Fields: Tuple> =
+ Fields::WithElementAtEnd<WithWildcard<'world, Relation, Wildcard>>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
@@ -275,21 +289,24 @@ where
terms_builder.present([Self::uid()]);
}
- fn get_field<'world>(
+ fn add_field<Fields: Tuple>(
entity_handle: &EntityHandle<'world>,
world: &'world World,
- ) -> Self::Field<'world>
+ fields: Fields,
+ ) -> Self::AddField<Fields>
{
let first_matching_comp = entity_handle
.get_matching_components(Self::uid())
.next()
.expect("Not possible");
- WithWildcard {
- world,
- component_ref: first_matching_comp,
- _pd: PhantomData,
- }
+ fields.with_elem(
+ WithWildcard {
+ world,
+ component_ref: first_matching_comp,
+ _pd: PhantomData,
+ }
+ )
}
}
@@ -320,11 +337,12 @@ where
}
}
-impl<Relation> QueryTermWithField for &'_ [Pair<Relation, Wildcard>]
+impl<'world, Relation> QueryTerm<'world> for &[Pair<Relation, Wildcard>]
where
Relation: Component,
{
- type Field<'a> = MultipleWithWildcard<'a, Relation, Wildcard>;
+ type AddField<Fields: Tuple> =
+ Fields::WithElementAtEnd<MultipleWithWildcard<'world, Relation, Wildcard>>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
_terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
@@ -332,16 +350,19 @@ where
{
}
- fn get_field<'world>(
+ fn add_field<Fields: Tuple>(
entity_handle: &EntityHandle<'world>,
world: &'world World,
- ) -> Self::Field<'world>
- {
- MultipleWithWildcard {
- entity_handle: entity_handle.clone(),
- world,
- _pd: PhantomData,
- }
+ fields: Fields,
+ ) -> Self::AddField<Fields>
+ {
+ fields.with_elem(
+ MultipleWithWildcard {
+ entity_handle: entity_handle.clone(),
+ world,
+ _pd: PhantomData,
+ }
+ )
}
}
diff --git a/engine-ecs/src/phase.rs b/engine-ecs/src/phase.rs
index cccf978..cd8ab56 100644
--- a/engine-ecs/src/phase.rs
+++ b/engine-ecs/src/phase.rs
@@ -38,7 +38,7 @@ pub(crate) struct HasSystem;
#[cfg(debug_assertions)]
-pub(crate) fn check_parents(phase_query: Query<(Pair<ChildOf, Wildcard>, Option<&EntityName>), (With<Phase>,)>)
+pub(crate) fn check_parents(phase_query: Query<(Pair<ChildOf, Wildcard>, Option<&EntityName>, With<Phase>,)>)
{
for (child_of, ent_name) in &phase_query {
let Some(parent_ent) = child_of.get_target_ent() else {
diff --git a/engine-ecs/src/query.rs b/engine-ecs/src/query.rs
index 1b7bf43..8549411 100644
--- a/engine-ecs/src/query.rs
+++ b/engine-ecs/src/query.rs
@@ -2,6 +2,8 @@ use std::any::type_name;
use std::marker::PhantomData;
use seq_macro::seq;
+use paste::paste;
+use util_macros::sub;
use crate::component::{
Component,
@@ -11,6 +13,7 @@ use crate::component::{
use crate::entity::Handle as EntityHandle;
use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery};
use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
+use crate::tuple::Tuple;
use crate::uid::Uid;
use crate::util::array_vec::ArrayVec;
use crate::util::Array;
@@ -23,28 +26,26 @@ pub mod term;
pub const MAX_TERM_CNT: usize = 17;
#[derive(Debug)]
-pub struct Query<'world, FieldTerms, FieldlessTerms = ()>
+pub struct Query<'world, TermsT>
where
- FieldTerms: TermWithFieldTuple,
- FieldlessTerms: TermWithoutFieldTuple,
+ TermsT: TermTuple<'world>,
{
inner: FlexibleQuery<'world, MAX_TERM_CNT>,
- _pd: PhantomData<(FieldTerms, FieldlessTerms)>,
+ _pd: PhantomData<TermsT>,
}
-impl<'world, FieldTerms, FieldlessTerms> Query<'world, FieldTerms, FieldlessTerms>
+impl<'world, TermsT> Query<'world, TermsT>
where
- FieldTerms: TermWithFieldTuple,
- FieldlessTerms: TermWithoutFieldTuple,
+ TermsT: TermTuple<'world>,
{
/// Iterates over the entities matching this query, the iterator item being the entity
/// components.
#[must_use]
- pub fn iter<'query>(
- &'query self,
- ) -> Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>
+ pub fn iter<'this>(
+ &'this self,
+ ) -> Iter<'this, 'world, TermsT, FlexibleQueryIter<'this, 'world>>
{
- tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
+ tracing::trace!("Searching for {}", std::any::type_name::<TermsT>());
Iter {
world: self.inner.world(),
@@ -56,11 +57,11 @@ where
/// 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<'query>(
- &'query self,
- ) -> ComponentAndEuidIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>
+ pub fn iter_with_euids<'this>(
+ &'this self,
+ ) -> ComponentAndEuidIter<'this, 'world, TermsT, FlexibleQueryIter<'this, 'world>>
{
- tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
+ tracing::trace!("Searching for {}", std::any::type_name::<TermsT>());
ComponentAndEuidIter {
world: self.inner.world(),
@@ -75,14 +76,14 @@ where
/// This function exists so that a custom [`EntityHandle`] iterator can be given to
/// [`Iter`] without giving the user access to a reference to the [`World`].
#[must_use]
- pub fn iter_with<'query, OutIter>(
- &'query self,
- func: impl FnOnce(FlexibleQueryIter<'query>) -> OutIter,
- ) -> Iter<'query, 'world, FieldTerms, OutIter>
+ pub fn iter_with<'this, OutIter>(
+ &'this self,
+ func: impl FnOnce(FlexibleQueryIter<'this, 'world>) -> OutIter,
+ ) -> Iter<'this, 'world, TermsT, OutIter>
where
- OutIter: Iterator<Item = EntityHandle<'query>>,
+ OutIter: Iterator<Item = EntityHandle<'world>>,
{
- tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
+ tracing::trace!("Searching for {}", std::any::type_name::<TermsT>());
Iter {
world: self.inner.world(),
@@ -101,7 +102,7 @@ where
/// Returns a new `Query` created from a [`FlexibleQuery`].
///
/// # Important notes
- /// The terms in `FieldTerms` and `FieldlessTerms` must be compatible with the terms
+ /// The terms in `TermsT` must be compatible with the terms
/// in the given [`FlexibleQuery`], otherwise any method call or iterating might
/// panic.
#[must_use]
@@ -126,8 +127,7 @@ where
{
let mut terms_builder = Terms::builder();
- FieldTerms::apply_terms_to_builder(&mut terms_builder);
- FieldlessTerms::apply_terms_to_builder(&mut terms_builder);
+ TermsT::apply_terms_to_builder(&mut terms_builder);
Self {
inner: world.flexible_query(terms_builder.build()),
@@ -136,14 +136,13 @@ where
}
}
-impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator
- for &'query Query<'world, FieldTerms, FieldlessTerms>
+impl<'query, 'world, TermsT> IntoIterator
+ for &'query Query<'world, TermsT>
where
- FieldTerms: TermWithFieldTuple,
- FieldlessTerms: TermWithoutFieldTuple,
+ TermsT: TermTuple<'world>,
{
- type IntoIter = Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>;
- type Item = FieldTerms::Fields<'query>;
+ type IntoIter = Iter<'query, 'world, TermsT, FlexibleQueryIter<'query, 'world>>;
+ type Item = TermsT::Fields;
fn into_iter(self) -> Self::IntoIter
{
@@ -151,11 +150,10 @@ where
}
}
-impl<'world, FieldTerms, FieldlessTerms> SystemParam<'world>
- for Query<'world, FieldTerms, FieldlessTerms>
+impl<'world, TermsT> SystemParam<'world>
+ for Query<'world, TermsT>
where
- FieldTerms: TermWithFieldTuple,
- FieldlessTerms: TermWithoutFieldTuple,
+ TermsT: TermTuple<'world>,
{
type Input = ();
@@ -293,30 +291,24 @@ impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT>
}
}
-pub trait TermWithoutField
+pub trait Term<'world>
{
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- );
-}
-
-pub trait TermWithField
-{
- type Field<'a>;
+ type AddField<Fields: Tuple>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
);
- fn get_field<'world>(
+ fn add_field<Fields: Tuple>(
entity_handle: &EntityHandle<'world>,
world: &'world World,
- ) -> Self::Field<'world>;
+ fields: Fields,
+ ) -> Self::AddField<Fields>;
}
-impl<ComponentT: Component> TermWithField for &ComponentT
+impl<'world, ComponentT: Component> Term<'world> for &ComponentT
{
- type Field<'a> = ComponentHandle<'a, ComponentT>;
+ type AddField<Fields: Tuple> = Fields::WithElementAtEnd<ComponentHandle<'world, ComponentT>>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
@@ -325,10 +317,11 @@ impl<ComponentT: Component> TermWithField for &ComponentT
terms_builder.present([ComponentT::id()]);
}
- fn get_field<'world>(
+ fn add_field<Fields: Tuple>(
entity_handle: &EntityHandle<'world>,
_world: &'world World,
- ) -> Self::Field<'world>
+ fields: Fields,
+ ) -> Self::AddField<Fields>
{
assert!(!ComponentT::id().is_pair());
@@ -346,18 +339,23 @@ impl<ComponentT: Component> TermWithField for &ComponentT
);
};
- Self::Field::from_entity_component_ref(&component).unwrap_or_else(|err| {
- panic!(
- "Creating handle to component {} failed: {err}",
- type_name::<ComponentT>()
- );
- })
+ let component = match ComponentHandle::<ComponentT>::from_entity_component_ref(&component) {
+ Ok(component) => component,
+ Err(err) => {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }
+ };
+
+ fields.with_elem(component)
}
}
-impl<ComponentT: Component> TermWithField for &mut ComponentT
+impl<'world, ComponentT: Component> Term<'world> for &mut ComponentT
{
- type Field<'a> = ComponentHandleMut<'a, ComponentT>;
+ type AddField<Fields: Tuple> = Fields::WithElementAtEnd<ComponentHandleMut<'world, ComponentT>>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
@@ -366,10 +364,11 @@ impl<ComponentT: Component> TermWithField for &mut ComponentT
terms_builder.present([ComponentT::id()]);
}
- fn get_field<'world>(
+ fn add_field<Fields: Tuple>(
entity_handle: &EntityHandle<'world>,
world: &'world World,
- ) -> Self::Field<'world>
+ fields: Fields,
+ ) -> Self::AddField<Fields>
{
assert!(!ComponentT::id().is_pair());
@@ -387,81 +386,95 @@ impl<ComponentT: Component> TermWithField for &mut ComponentT
);
};
- Self::Field::from_entity_component_ref(&component, world).unwrap_or_else(|err| {
- panic!(
- "Creating handle to component {} failed: {err}",
- type_name::<ComponentT>()
- );
- })
+ let component = match ComponentHandleMut::<ComponentT>::from_entity_component_ref(&component, world) {
+ Ok(component) => component,
+ Err(err) => {
+ panic!(
+ "Creating mut handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }
+ };
+
+ fields.with_elem(component)
}
}
-pub trait TermWithoutFieldTuple
+impl<'world, TermT, FieldAcc> TermFieldSource<'world, FieldAcc> for TermT
+where
+ TermT: Term<'world>,
+ TermT::AddField<FieldAcc>: Tuple,
+ FieldAcc: Tuple
{
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- );
+ type NewFieldAcc = TermT::AddField<FieldAcc>;
+
+ fn collect_field(
+ entity_handle: &EntityHandle<'world>,
+ world: &'world World,
+ fields: FieldAcc,
+ ) -> Self::NewFieldAcc
+ {
+ Self::add_field(entity_handle, world, fields)
+ }
}
-pub trait TermWithFieldTuple
+pub trait TermTuple<'world>
{
- type Fields<'component>;
+ type Fields;
fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
);
- fn get_fields<'component>(
- entity_handle: &EntityHandle<'component>,
- world: &'component World,
- ) -> Self::Fields<'component>;
+ fn get_fields(
+ entity_handle: &EntityHandle<'world>,
+ world: &'world World,
+ ) -> Self::Fields;
}
-pub struct Iter<'query, 'world, FieldTerms, EntityHandleIter>
+pub struct Iter<'query, 'world, TermsT, EntityHandleIter>
where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
+ TermsT: TermTuple<'world>,
+ EntityHandleIter: Iterator<Item = EntityHandle<'world>>,
{
world: &'world World,
inner: EntityHandleIter,
- comps_pd: PhantomData<FieldTerms>,
+ comps_pd: PhantomData<(TermsT, &'query ())>,
}
-impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator
- for Iter<'query, 'world, FieldTerms, EntityHandleIter>
+impl<'query, 'world, TermsT, EntityHandleIter> Iterator
+ for Iter<'query, 'world, TermsT, EntityHandleIter>
where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
- 'world: 'query,
+ TermsT: TermTuple<'world>,
+ EntityHandleIter: Iterator<Item = EntityHandle<'world>>,
{
- type Item = FieldTerms::Fields<'query>;
+ type Item = TermsT::Fields;
fn next(&mut self) -> Option<Self::Item>
{
let entity_handle = self.inner.next()?;
- Some(FieldTerms::get_fields(&entity_handle, self.world))
+ Some(TermsT::get_fields(&entity_handle, self.world))
}
}
-pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter>
+pub struct ComponentAndEuidIter<'query, 'world, TermsT, EntityHandleIter>
where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
+ TermsT: TermTuple<'world>,
+ EntityHandleIter: Iterator<Item = EntityHandle<'world>>,
{
world: &'world World,
iter: EntityHandleIter,
- comps_pd: PhantomData<FieldTerms>,
+ comps_pd: PhantomData<(TermsT, &'query ())>,
}
-impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator
- for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter>
+impl<'query, 'world, TermsT, EntityHandleIter> Iterator
+ for ComponentAndEuidIter<'query, 'world, TermsT, EntityHandleIter>
where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
- 'world: 'query,
+ TermsT: TermTuple<'world>,
+ EntityHandleIter: Iterator<Item = EntityHandle<'world>>,
{
- type Item = (Uid, FieldTerms::Fields<'query>);
+ type Item = (Uid, TermsT::Fields);
fn next(&mut self) -> Option<Self::Item>
{
@@ -469,16 +482,50 @@ where
Some((
entity_handle.uid(),
- FieldTerms::get_fields(&entity_handle, self.world),
+ TermsT::get_fields(&entity_handle, self.world),
))
}
}
-macro_rules! impl_term_sequence {
+pub trait TermFieldSource<'world, FieldAcc>
+{
+ type NewFieldAcc;
+
+ fn collect_field(
+ entity_handle: &EntityHandle<'world>,
+ world: &'world World,
+ fields: FieldAcc,
+ ) -> Self::NewFieldAcc;
+}
+
+macro_rules! term_field_source_new_field_acc {
+ (overflow) => {
+ ()
+ };
+
+ ($index: tt) => {
+ paste! {
+ [<Term $index>]::NewFieldAcc
+ }
+ };
+}
+
+macro_rules! gen_term_tuple_impls {
($c: tt) => {
- seq!(I in 0..=$c {
- impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*)
+ seq!(I in 0..$c {
+ impl<'world, #(Term~I,)*> TermTuple<'world> for (#(Term~I,)*)
+ where
+ #(
+ Term~I: Term<'world> + TermFieldSource<
+ 'world,
+ 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<const MAX_TERM_CNT: usize>(
terms_builder: &mut TermsBuilder<MAX_TERM_CNT>
)
@@ -487,60 +534,27 @@ macro_rules! impl_term_sequence {
Term~I::apply_to_terms_builder(terms_builder);
)*
}
- }
-
- impl<#(Term~I: TermWithField,)*> TermWithFieldTuple for (#(Term~I,)*)
- {
- type Fields<'component> = (#(Term~I::Field<'component>,)*);
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>
- )
+ #[allow(unused)]
+ fn get_fields(
+ entity_handle: &EntityHandle<'world>,
+ world: &'world World,
+ ) -> Self::Fields
{
+ let fields = ();
+
#(
- Term~I::apply_to_terms_builder(terms_builder);
+ let fields = Term~I::collect_field(entity_handle, world, fields);
)*
- }
- fn get_fields<'component>(
- entity_handle: &EntityHandle<'component>,
- world: &'component World,
- ) -> Self::Fields<'component>
- {
- (#(Term~I::get_field(entity_handle, world),)*)
+ fields
}
}
});
};
}
-seq!(C in 0..=16 {
- impl_term_sequence!(C);
+seq!(C in 0..17 {
+ gen_term_tuple_impls!(C);
});
-impl TermWithoutFieldTuple for ()
-{
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- }
-}
-
-impl TermWithFieldTuple for ()
-{
- type Fields<'component> = ();
-
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- }
-
- fn get_fields<'component>(
- _entity_handle: &EntityHandle<'_>,
- _world: &'component World,
- ) -> Self::Fields<'component>
- {
- }
-}
diff --git a/engine-ecs/src/query/flexible.rs b/engine-ecs/src/query/flexible.rs
index 66d62c6..fd1e30e 100644
--- a/engine-ecs/src/query/flexible.rs
+++ b/engine-ecs/src/query/flexible.rs
@@ -19,7 +19,7 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT>
{
/// Iterates over the entities matching this query.
#[must_use]
- pub fn iter(&self) -> Iter<'_>
+ pub fn iter(&self) -> Iter<'_, 'world>
{
Iter {
iter: self
@@ -52,10 +52,10 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT>
}
}
-impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_TERM_CNT>
+impl<'query, 'world, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'world, MAX_TERM_CNT>
{
- type IntoIter = Iter<'query>;
- type Item = EntityHandle<'query>;
+ type IntoIter = Iter<'query, 'world>;
+ type Item = EntityHandle<'world>;
fn into_iter(self) -> Self::IntoIter
{
@@ -63,15 +63,15 @@ impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_T
}
}
-pub struct Iter<'query>
+pub struct Iter<'query, 'world>
{
- iter: QueryEntityIter<'query>,
- world: &'query World,
+ iter: QueryEntityIter<'query, 'world>,
+ world: &'world World,
}
-impl<'query> Iterator for Iter<'query>
+impl<'query, 'world> Iterator for Iter<'query, 'world>
{
- type Item = EntityHandle<'query>;
+ type Item = EntityHandle<'world>;
fn next(&mut self) -> Option<Self::Item>
{
@@ -85,8 +85,8 @@ type ComponentIterMapFnOutput<'a> = Zip<RepeatN<&'a Archetype>, EntityIter<'a>>;
type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> ComponentIterMapFnOutput<'a>;
-type QueryEntityIter<'query> = FlatMap<
- ArchetypeRefIter<'query, 'query>,
- ComponentIterMapFnOutput<'query>,
+type QueryEntityIter<'query, 'world> = FlatMap<
+ ArchetypeRefIter<'world, 'query>,
+ ComponentIterMapFnOutput<'world>,
ComponentIterMapFn,
>;
diff --git a/engine-ecs/src/query/term.rs b/engine-ecs/src/query/term.rs
index 7c4503a..04bca1d 100644
--- a/engine-ecs/src/query/term.rs
+++ b/engine-ecs/src/query/term.rs
@@ -1,17 +1,19 @@
use std::any::type_name;
use std::marker::PhantomData;
+use crate::World;
use crate::component::{
Component,
Handle as ComponentHandle,
HandleMut as ComponentHandleMut,
};
+use crate::entity::Handle as EntityHandle;
use crate::query::{
- TermWithField,
- TermWithoutField,
+ Term,
TermsBuilder,
TermsBuilderInterface,
};
+use crate::tuple::Tuple;
use crate::uid::With as WithUid;
pub struct With<WithUidT>
@@ -21,16 +23,27 @@ where
_pd: PhantomData<WithUidT>,
}
-impl<WithUidT> TermWithoutField for With<WithUidT>
+impl<'world, WithUidT> Term<'world> for With<WithUidT>
where
WithUidT: WithUid,
{
+ type AddField<Fields: Tuple> = Fields;
+
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
)
{
terms_builder.present([WithUidT::uid()]);
}
+
+ fn add_field<Fields: Tuple>(
+ _entity_handle: &EntityHandle<'world>,
+ _world: &'world World,
+ fields: Fields,
+ ) -> Self::AddField<Fields>
+ {
+ fields
+ }
}
pub struct Without<WithUidT>
@@ -40,21 +53,33 @@ where
_pd: PhantomData<WithUidT>,
}
-impl<WithUidT> TermWithoutField for Without<WithUidT>
+impl<'world, WithUidT> Term<'world> for Without<WithUidT>
where
WithUidT: WithUid,
{
+ type AddField<Fields: Tuple> = Fields;
+
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
)
{
terms_builder.absent([WithUidT::uid()]);
}
+
+ fn add_field<Fields: Tuple>(
+ _entity_handle: &EntityHandle<'world>,
+ _world: &'world World,
+ fields: Fields,
+ ) -> Self::AddField<Fields>
+ {
+ fields
+ }
}
-impl<ComponentT: Component> TermWithField for Option<&ComponentT>
+impl<'world, ComponentT: Component> Term<'world> for Option<&ComponentT>
{
- type Field<'a> = Option<ComponentHandle<'a, ComponentT>>;
+ type AddField<Fields: Tuple> =
+ Fields::WithElementAtEnd<Option<ComponentHandle<'world, ComponentT>>>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
_terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
@@ -62,30 +87,36 @@ impl<ComponentT: Component> TermWithField for Option<&ComponentT>
{
}
- fn get_field<'world>(
- entity_handle: &crate::entity::Handle<'world>,
- _world: &'world crate::World,
- ) -> Self::Field<'world>
+ fn add_field<Fields: Tuple>(
+ entity_handle: &EntityHandle<'world>,
+ _world: &'world World,
+ fields: Fields,
+ ) -> Self::AddField<Fields>
{
- Some(
- ComponentHandle::<'world, ComponentT>::from_entity_component_ref(
- &entity_handle
- .get_matching_components(ComponentT::id())
- .next()?,
- )
- .unwrap_or_else(|err| {
- panic!(
- "Creating handle to component {} failed: {err}",
- type_name::<ComponentT>()
- );
- }),
- )
+ let component = (|| {
+ let comp_ref = &entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()?;
+
+ match ComponentHandle::<ComponentT>::from_entity_component_ref(comp_ref) {
+ Ok(component) => Some(component),
+ Err(err) => {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }
+ }
+ })();
+
+ fields.with_elem(component)
}
}
-impl<ComponentT: Component> TermWithField for Option<&mut ComponentT>
+impl<'world, ComponentT: Component> Term<'world> for Option<&mut ComponentT>
{
- type Field<'a> = Option<ComponentHandleMut<'a, ComponentT>>;
+ type AddField<Fields: Tuple> =
+ Fields::WithElementAtEnd<Option<ComponentHandleMut<'world, ComponentT>>>;
fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
_terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
@@ -93,24 +124,28 @@ impl<ComponentT: Component> TermWithField for Option<&mut ComponentT>
{
}
- fn get_field<'world>(
- entity_handle: &crate::entity::Handle<'world>,
- world: &'world crate::World,
- ) -> Self::Field<'world>
+ fn add_field<Fields: Tuple>(
+ entity_handle: &EntityHandle<'world>,
+ world: &'world World,
+ fields: Fields,
+ ) -> Self::AddField<Fields>
{
- Some(
- ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref(
- &entity_handle
- .get_matching_components(ComponentT::id())
- .next()?,
- world,
- )
- .unwrap_or_else(|err| {
- panic!(
- "Creating handle to component {} failed: {err}",
- type_name::<ComponentT>()
- );
- }),
- )
+ let component = (|| {
+ let comp_ref = &entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()?;
+
+ match ComponentHandleMut::<ComponentT>::from_entity_component_ref(comp_ref, world) {
+ Ok(component) => Some(component),
+ Err(err) => {
+ panic!(
+ "Creating mut handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }
+ }
+ })();
+
+ fields.with_elem(component)
}
}
diff --git a/engine-ecs/src/uid.rs b/engine-ecs/src/uid.rs
index 26fbaee..c148dbc 100644
--- a/engine-ecs/src/uid.rs
+++ b/engine-ecs/src/uid.rs
@@ -166,7 +166,7 @@ pub struct PairParams
pub target: Uid,
}
-pub trait With
+pub trait With: 'static
{
fn uid() -> Uid;
}
diff --git a/engine-ecs/tests/query.rs b/engine-ecs/tests/query.rs
index af47083..870ff42 100644
--- a/engine-ecs/tests/query.rs
+++ b/engine-ecs/tests/query.rs
@@ -2,11 +2,11 @@ use engine_ecs::component::Component;
use engine_ecs::pair::{Pair, Wildcard};
use engine_ecs::query::term::Without;
use engine_ecs::query::{
- TermWithFieldTuple as QueryTermWithFieldTuple,
- TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
+ TermTuple as QueryTermTuple,
+ Query,
};
use engine_ecs::uid::Uid;
-use engine_ecs::{Component, Query, World};
+use engine_ecs::{World, Component};
use parking_lot::{Mutex, Once};
pub static SETUP: Once = Once::new();
@@ -47,12 +47,11 @@ fn setup()
});
}
-fn assert_query_finds_ents<QueryFieldTerms, QueryFieldlessTerms>(
- query: Query<'_, QueryFieldTerms, QueryFieldlessTerms>,
+fn assert_query_finds_ents<'world, QueryTerms>(
+ query: Query<'world, QueryTerms>,
mut expected_ent_ids: Vec<Uid>,
) where
- QueryFieldTerms: QueryTermWithFieldTuple,
- QueryFieldlessTerms: QueryTermWithoutFieldTuple,
+ QueryTerms: QueryTermTuple<'world>,
{
assert!(
query.iter_with_euids().all(|(ent_id, _)| {
@@ -96,7 +95,7 @@ fn query_archetype_exists_with_edges_to_next_archetypes()
let ent_4_id = world.spawn((A, B, C, G, F));
assert_query_finds_ents(
- world.query::<(&A, &B, &C), ()>(),
+ world.query::<(&A, &B, &C)>(),
vec![ent_1_id, ent_2_id, ent_3_id, ent_4_id],
);
}
@@ -114,7 +113,7 @@ fn query_archetype_exists_with_2_comps_diff_to_next_archetype()
let ent_2_id = world.spawn((A, B, F));
- assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -130,7 +129,7 @@ fn query_archetype_exists_with_2_comps_diff_to_next_archetype_rev()
let ent_2_id = world.spawn((A, B, C, D, F));
- assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -146,7 +145,7 @@ fn query_archetype_exists_with_3_comps_diff_to_next_archetype()
let ent_2_id = world.spawn((A, B, F));
- assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -162,7 +161,7 @@ fn query_archetype_exists_with_3_comps_diff_to_next_archetype_rev()
let ent_2_id = world.spawn((A, B, C, D, E, F));
- assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -178,7 +177,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype()
let ent_2_id = world.spawn((A, B, G));
- assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &G)>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -194,7 +193,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype_rev()
let ent_2_id = world.spawn((A, B, C, D, E, F, G));
- assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]);
+ assert_query_finds_ents(world.query::<(&A, &B, &G)>(), vec![ent_1_id, ent_2_id]);
}
#[test]
@@ -211,7 +210,7 @@ fn query_archetype_exists_with_4_comps_diff_to_next_archetype_and_opt_comp()
let ent_2_id = world.spawn((A, B, G));
assert_query_finds_ents(
- world.query::<(&A, Option<&E>, &G), ()>(),
+ world.query::<(&A, Option<&E>, &G)>(),
vec![ent_1_id, ent_2_id],
);
}
@@ -232,7 +231,7 @@ fn query_archetype_nonexistant()
world.spawn((A, B, C, G, F));
- assert_query_finds_ents(world.query::<(&A, &E), ()>(), vec![ent_2_id, ent_3_id]);
+ assert_query_finds_ents(world.query::<(&A, &E)>(), vec![ent_2_id, ent_3_id]);
}
#[test]
@@ -250,7 +249,7 @@ fn query_archetype_nonexistant_and_opt_comp()
world.spawn((A, B, C, G, F));
assert_query_finds_ents(
- world.query::<(&A, &E, Option<&D>), ()>(),
+ world.query::<(&A, &E, Option<&D>)>(),
vec![ent_2_id, ent_3_id],
);
}
@@ -273,7 +272,7 @@ fn query_without_comp_and_archetype_exists()
let ent_3_id = world.spawn((A, B, C, G, F));
assert_query_finds_ents(
- world.query::<(&A, &B, &C), (Without<E>,)>(),
+ world.query::<(&A, &B, &C, Without<E>)>(),
vec![ent_1_id, ent_2_id, ent_3_id],
);
}
@@ -295,7 +294,7 @@ fn query_without_required_comp_and_archetype_exists()
world.spawn((A, B, C, G));
world.spawn((A, B, C, G, F));
- assert_query_finds_ents(world.query::<(&A, &B), (Without<B>,)>(), vec![]);
+ assert_query_finds_ents(world.query::<(&A, &B, Without<B>)>(), vec![]);
}
#[test]
@@ -317,7 +316,7 @@ fn query_without_comp_and_archetype_nonexistant()
world.spawn((A, B, C, G, F, E));
assert_query_finds_ents(
- world.query::<(&A, &E), (Without<F>,)>(),
+ world.query::<(&A, &E, Without<F>)>(),
vec![ent_1_id, ent_2_id],
);
}
@@ -363,7 +362,7 @@ fn query_with_wildcard_target_pair()
));
assert_query_finds_ents(
- world.query::<(&B, Pair<G, Wildcard>), ()>(),
+ world.query::<(&B, Pair<G, Wildcard>)>(),
vec![ent_2_id, ent_3_id, ent_4_id],
);
}
@@ -407,7 +406,7 @@ fn query_with_component_target_pair()
));
assert_query_finds_ents(
- world.query::<(&B, Pair<G, &F>), ()>(),
+ world.query::<(&B, Pair<G, &F>)>(),
vec![ent_2_id, ent_3_id],
);
}