use std::any::TypeId;
use crate::{Entity, Universe, WorldId, components::Component};
#[derive(Default)]
pub struct Query {
required_components: Vec<TypeId>,
excluded_components: Vec<TypeId>,
possible_worlds: Vec<WorldId>,
excluded_worlds: Vec<WorldId>,
}
impl Query {
#[must_use]
pub fn empty() -> Self {
Self::default()
}
#[must_use]
pub fn with_component<C: Component>(mut self) -> Self {
let id = TypeId::of::<C>();
debug_assert!(
!self.excluded_components.contains(&id),
"with_component and without_component called with the same type — \
this query will never match any entity"
);
if !self.required_components.contains(&id) {
self.required_components.push(id);
}
self
}
#[must_use]
pub fn without_component<C: Component>(mut self) -> Self {
let id = TypeId::of::<C>();
debug_assert!(
!self.required_components.contains(&id),
"without_component and with_component called with the same type — \
this query will never match any entity"
);
if !self.excluded_components.contains(&id) {
self.excluded_components.push(id);
}
self
}
#[must_use]
pub fn with_world(mut self, world: WorldId) -> Self {
debug_assert!(
!self.excluded_worlds.contains(&world),
"with_world and without_world called with the same WorldId — \
this query will never match any entity"
);
if !self.possible_worlds.contains(&world) {
self.possible_worlds.push(world);
}
self
}
#[must_use]
pub fn without_world(mut self, world: WorldId) -> Self {
debug_assert!(
!self.possible_worlds.contains(&world),
"without_world and with_world called with the same WorldId — \
this query will never match any entity"
);
if !self.excluded_worlds.contains(&world) {
self.excluded_worlds.push(world);
}
self
}
#[must_use]
pub(crate) fn matches(&self, universe: &Universe, entity: Entity) -> bool {
let world_ok = self.possible_worlds.is_empty()
|| self
.possible_worlds
.iter()
.any(|&world| universe.is_in_world(world, entity));
if !world_ok {
return false;
}
if self
.excluded_worlds
.iter()
.any(|&world| universe.is_in_world(world, entity))
{
return false;
}
self.required_components
.iter()
.all(|&t| universe.components.contains(entity, t))
&& self
.excluded_components
.iter()
.all(|&t| !universe.components.contains(entity, t))
}
pub(crate) fn query<'u>(&'u self, universe: &'u Universe) -> impl Iterator<Item = Entity> {
universe
.entities
.keys()
.copied()
.filter(|&e| self.matches(universe, e))
}
}