mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 14:40:19 +00:00
Update ExactSizeIterator
impl to support archetypal filters (With, Without) (#5124)
# Objective - Fixes #3142 ## Solution - Done according to #3142 - Created new marker trait `ArchetypeFilter` - Implement said trait to: - `With<T>` - `Without<T>` - tuples containing only types that implement `ArchetypeFilter`, from 0 to 15 elements - `Or<T>` where T is a tuple as described previously - Changed `ExactSizeIterator` impl to include a new generic that must implement `WorldQuery` and `ArchetypeFilter` - Added new tests --- ## Changelog ### Added - `Query`s with archetypal filters can now use `.iter().len()` to get the exact size of the iterator.
This commit is contained in:
parent
e28b88b378
commit
6e50b249a4
5 changed files with 218 additions and 8 deletions
|
@ -739,3 +739,30 @@ impl_tick_filter!(
|
|||
ChangedFetch,
|
||||
ComponentTicks::is_changed
|
||||
);
|
||||
|
||||
/// A marker trait to indicate that the filter works at an archetype level.
|
||||
///
|
||||
/// This is needed to implement [`ExactSizeIterator`](std::iter::ExactSizeIterator) for
|
||||
/// [`QueryIter`](crate::query::QueryIter) that contains archetype-level filters.
|
||||
///
|
||||
/// The trait must only be implement for filters where its corresponding [`Fetch::IS_ARCHETYPAL`](crate::query::Fetch::IS_ARCHETYPAL)
|
||||
/// is [`prim@true`]. As such, only the [`With`] and [`Without`] filters can implement the trait.
|
||||
/// [Tuples](prim@tuple) and [`Or`] filters are automatically implemented with the trait only if its containing types
|
||||
/// also implement the same trait.
|
||||
///
|
||||
/// [`Added`] and [`Changed`] works with entities, and therefore are not archetypal. As such
|
||||
/// they do not implement [`ArchetypeFilter`].
|
||||
pub trait ArchetypeFilter {}
|
||||
|
||||
impl<T> ArchetypeFilter for With<T> {}
|
||||
impl<T> ArchetypeFilter for Without<T> {}
|
||||
|
||||
macro_rules! impl_archetype_filter_tuple {
|
||||
($($filter: ident),*) => {
|
||||
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for ($($filter,)*) {}
|
||||
|
||||
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for Or<($($filter,)*)> {}
|
||||
};
|
||||
}
|
||||
|
||||
all_tuples!(impl_archetype_filter_tuple, 0, 15, F);
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
archetype::{ArchetypeId, Archetypes},
|
||||
entity::{Entities, Entity},
|
||||
prelude::World,
|
||||
query::{Fetch, QueryState, WorldQuery},
|
||||
query::{ArchetypeFilter, Fetch, QueryState, WorldQuery},
|
||||
storage::{TableId, Tables},
|
||||
};
|
||||
use std::{borrow::Borrow, iter::FusedIterator, marker::PhantomData, mem::MaybeUninit};
|
||||
|
@ -346,15 +346,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: We can cheaply implement this for unfiltered Queries because we have:
|
||||
// (1) pre-computed archetype matches
|
||||
// (2) each archetype pre-computes length
|
||||
// (3) there are no per-entity filters
|
||||
// TODO: add an ArchetypeOnlyFilter that enables us to implement this for filters like With<T>.
|
||||
// This would need to be added to all types that implement Filter with Filter::IS_ARCHETYPAL = true
|
||||
impl<'w, 's, Q: WorldQuery, QF> ExactSizeIterator for QueryIter<'w, 's, Q, QF, ()>
|
||||
impl<'w, 's, Q: WorldQuery, QF, F> ExactSizeIterator for QueryIter<'w, 's, Q, QF, F>
|
||||
where
|
||||
QF: Fetch<'w, State = Q::State>,
|
||||
F: WorldQuery + ArchetypeFilter,
|
||||
{
|
||||
fn len(&self) -> usize {
|
||||
self.query_state
|
||||
|
|
|
@ -31,6 +31,8 @@ mod tests {
|
|||
struct B(usize);
|
||||
#[derive(Component, Debug, Eq, PartialEq, Clone, Copy)]
|
||||
struct C(usize);
|
||||
#[derive(Component, Debug, Eq, PartialEq, Clone, Copy)]
|
||||
struct D(usize);
|
||||
|
||||
#[derive(Component, Debug, Eq, PartialEq, Clone, Copy)]
|
||||
#[component(storage = "SparseSet")]
|
||||
|
@ -51,6 +53,145 @@ mod tests {
|
|||
assert_eq!(values, vec![&B(3)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_filtered_len() {
|
||||
let mut world = World::new();
|
||||
world.spawn().insert_bundle((A(1), B(1)));
|
||||
world.spawn().insert_bundle((A(2),));
|
||||
world.spawn().insert_bundle((A(3),));
|
||||
|
||||
let mut values = world.query_filtered::<&A, With<B>>();
|
||||
let n = 1;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, Without<B>>();
|
||||
let n = 2;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
|
||||
let mut world = World::new();
|
||||
world.spawn().insert_bundle((A(1), B(1), C(1)));
|
||||
world.spawn().insert_bundle((A(2), B(2)));
|
||||
world.spawn().insert_bundle((A(3), B(3)));
|
||||
world.spawn().insert_bundle((A(4), C(4)));
|
||||
world.spawn().insert_bundle((A(5), C(5)));
|
||||
world.spawn().insert_bundle((A(6), C(6)));
|
||||
world.spawn().insert_bundle((A(7),));
|
||||
world.spawn().insert_bundle((A(8),));
|
||||
world.spawn().insert_bundle((A(9),));
|
||||
world.spawn().insert_bundle((A(10),));
|
||||
|
||||
// With/Without for B and C
|
||||
let mut values = world.query_filtered::<&A, With<B>>();
|
||||
let n = 3;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, With<C>>();
|
||||
let n = 4;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, Without<B>>();
|
||||
let n = 7;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, Without<C>>();
|
||||
let n = 6;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
|
||||
// With/Without (And) combinations
|
||||
let mut values = world.query_filtered::<&A, (With<B>, With<C>)>();
|
||||
let n = 1;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, (With<B>, Without<C>)>();
|
||||
let n = 2;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, (Without<B>, With<C>)>();
|
||||
let n = 3;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, (Without<B>, Without<C>)>();
|
||||
let n = 4;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
|
||||
// With/Without Or<()> combinations
|
||||
let mut values = world.query_filtered::<&A, Or<(With<B>, With<C>)>>();
|
||||
let n = 6;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, Or<(With<B>, Without<C>)>>();
|
||||
let n = 7;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, Or<(Without<B>, With<C>)>>();
|
||||
let n = 8;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, Or<(Without<B>, Without<C>)>>();
|
||||
let n = 9;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
|
||||
let mut values = world.query_filtered::<&A, (Or<(With<B>,)>, Or<(With<C>,)>)>();
|
||||
let n = 1;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, Or<(Or<(With<B>, With<C>)>, With<D>)>>();
|
||||
let n = 6;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
|
||||
world.spawn().insert_bundle((A(11), D(11)));
|
||||
|
||||
let mut values = world.query_filtered::<&A, Or<(Or<(With<B>, With<C>)>, With<D>)>>();
|
||||
let n = 7;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
let mut values = world.query_filtered::<&A, Or<(Or<(With<B>, With<C>)>, Without<D>)>>();
|
||||
let n = 10;
|
||||
assert_eq!(values.iter(&world).size_hint().0, n);
|
||||
assert_eq!(values.iter(&world).size_hint().1.unwrap(), n);
|
||||
assert_eq!(values.iter(&world).len(), n);
|
||||
assert_eq!(values.iter(&world).count(), n);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_iter_combinations() {
|
||||
let mut world = World::new();
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
use bevy_ecs::prelude::*;
|
||||
|
||||
#[derive(Component)]
|
||||
struct Foo;
|
||||
|
||||
fn on_changed(query: Query<&Foo, Changed<Foo>>) {
|
||||
// this should fail to compile
|
||||
is_exact_size_iterator(query.iter());
|
||||
}
|
||||
|
||||
fn on_added(query: Query<&Foo, Added<Foo>>) {
|
||||
// this should fail to compile
|
||||
is_exact_size_iterator(query.iter());
|
||||
}
|
||||
|
||||
fn is_exact_size_iterator<T: ExactSizeIterator>(_iter: T) {}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,29 @@
|
|||
error[E0277]: the trait bound `bevy_ecs::query::Changed<Foo>: ArchetypeFilter` is not satisfied
|
||||
--> tests/ui/query_exact_sized_iterator_safety.rs:8:28
|
||||
|
|
||||
8 | is_exact_size_iterator(query.iter());
|
||||
| ---------------------- ^^^^^^^^^^^^ the trait `ArchetypeFilter` is not implemented for `bevy_ecs::query::Changed<Foo>`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed<Foo>>`
|
||||
note: required by a bound in `is_exact_size_iterator`
|
||||
--> tests/ui/query_exact_sized_iterator_safety.rs:16:30
|
||||
|
|
||||
16 | fn is_exact_size_iterator<T: ExactSizeIterator>(_iter: T) {}
|
||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `is_exact_size_iterator`
|
||||
|
||||
error[E0277]: the trait bound `bevy_ecs::query::Added<Foo>: ArchetypeFilter` is not satisfied
|
||||
--> tests/ui/query_exact_sized_iterator_safety.rs:13:28
|
||||
|
|
||||
13 | is_exact_size_iterator(query.iter());
|
||||
| ---------------------- ^^^^^^^^^^^^ the trait `ArchetypeFilter` is not implemented for `bevy_ecs::query::Added<Foo>`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added<Foo>>`
|
||||
note: required by a bound in `is_exact_size_iterator`
|
||||
--> tests/ui/query_exact_sized_iterator_safety.rs:16:30
|
||||
|
|
||||
16 | fn is_exact_size_iterator<T: ExactSizeIterator>(_iter: T) {}
|
||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `is_exact_size_iterator`
|
Loading…
Reference in a new issue