Skip to content

Commit b68f786

Browse files
committed
feat: implement WithoutOne as counterpart for WithOne
1 parent ebd4a2d commit b68f786

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

src/one.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,3 +850,95 @@ impl<Trait: ?Sized + TraitQuery> QueryFilter for WithOne<Trait> {
850850
true
851851
}
852852
}
853+
854+
/// [`WorldQuery`] filter for entities without any [one](crate::One) component
855+
/// implementing a trait.
856+
pub struct WithoutOne<Trait: ?Sized + TraitQuery>(PhantomData<&'static Trait>);
857+
858+
// this takes inspiration from `With` in bevy's main repo
859+
unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for WithoutOne<Trait> {
860+
type Item<'w> = ();
861+
type Fetch<'w> = ();
862+
type State = TraitQueryState<Trait>;
863+
864+
#[inline]
865+
fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> {
866+
item
867+
}
868+
869+
#[inline]
870+
unsafe fn init_fetch(
871+
_world: UnsafeWorldCell<'_>,
872+
_state: &Self::State,
873+
_last_run: Tick,
874+
_this_run: Tick,
875+
) {
876+
}
877+
878+
const IS_DENSE: bool = false;
879+
880+
#[inline]
881+
unsafe fn set_archetype<'w>(
882+
_fetch: &mut (),
883+
_state: &Self::State,
884+
_archetype: &'w bevy_ecs::archetype::Archetype,
885+
_table: &'w bevy_ecs::storage::Table,
886+
) {
887+
}
888+
889+
#[inline]
890+
unsafe fn set_table(_fetch: &mut (), _state: &Self::State, _table: &bevy_ecs::storage::Table) {}
891+
892+
#[inline]
893+
unsafe fn fetch<'w>(
894+
_fetch: &mut Self::Fetch<'w>,
895+
_entity: Entity,
896+
_table_row: TableRow,
897+
) -> Self::Item<'w> {
898+
}
899+
900+
#[inline]
901+
fn update_component_access(
902+
state: &Self::State,
903+
access: &mut bevy_ecs::query::FilteredAccess<ComponentId>,
904+
) {
905+
for &component in &*state.components {
906+
assert!(
907+
!access.access().has_write(component),
908+
"&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
909+
std::any::type_name::<Trait>(),
910+
);
911+
access.and_without(component);
912+
}
913+
}
914+
915+
#[inline]
916+
fn init_state(world: &mut World) -> Self::State {
917+
TraitQueryState::init(world)
918+
}
919+
920+
#[inline]
921+
fn get_state(world: &World) -> Option<Self::State> {
922+
TraitQueryState::get(world)
923+
}
924+
925+
#[inline]
926+
fn matches_component_set(
927+
state: &Self::State,
928+
set_contains_id: &impl Fn(ComponentId) -> bool,
929+
) -> bool {
930+
!state.components.iter().any(|&id| set_contains_id(id))
931+
}
932+
}
933+
934+
/// SAFETY: read-only access
935+
impl<Trait: ?Sized + TraitQuery> QueryFilter for WithoutOne<Trait> {
936+
const IS_ARCHETYPAL: bool = false;
937+
unsafe fn filter_fetch(
938+
_fetch: &mut Self::Fetch<'_>,
939+
_entity: Entity,
940+
_table_row: TableRow,
941+
) -> bool {
942+
true
943+
}
944+
}

src/tests.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,44 @@ fn print_with_one_filter_info(
537537
output.0.push(Default::default());
538538
}
539539

540+
#[test]
541+
fn without_any_filter() {
542+
let mut world = World::new();
543+
world.init_resource::<Output>();
544+
world
545+
.register_component_as::<dyn Person, Human>()
546+
.register_component_as::<dyn Person, Dolphin>();
547+
548+
world.spawn(Human("Henry".to_owned(), 22));
549+
world.spawn((Human("Henry".to_owned(), 22), Dolphin(22)));
550+
world.spawn(Dolphin(22));
551+
world.spawn(Fem);
552+
553+
let mut schedule = Schedule::default();
554+
schedule.add_systems(print_without_any_filter_info);
555+
556+
schedule.run(&mut world);
557+
558+
assert_eq!(
559+
world.resource::<Output>().0,
560+
&["People that are neither Human or Dolphin:", "3v1", "",]
561+
);
562+
}
563+
564+
// Prints the entity id of every Entity where none of its components implement the trait
565+
fn print_without_any_filter_info(
566+
people: Query<Entity, WithoutOne<dyn Person>>,
567+
mut output: ResMut<Output>,
568+
) {
569+
output
570+
.0
571+
.push("People that are neither Human or Dolphin:".to_string());
572+
for person in (&people).into_iter() {
573+
output.0.push(format!("{person:?}"));
574+
}
575+
output.0.push(Default::default());
576+
}
577+
540578
#[queryable]
541579
pub trait Messages {
542580
fn send(&mut self, _: &dyn Display);

0 commit comments

Comments
 (0)