mod borrow;
mod data;
mod dfs;
mod difference;
mod entity;
mod iter;
mod one;
mod planar;
mod searcher;
mod topo;
mod walk;
use itertools::Itertools;
pub use walk::{Children, DfsIter, GraphBorrow, GraphQuery, Node};
use core::fmt::Debug;
use crate::{
archetype::Slot,
component::ComponentValue,
fetch::FmtQuery,
filter::{All, BatchSize, Filtered, With, WithRelation, Without, WithoutRelation},
relation::RelationExt,
system::Access,
util::TuplePush,
Component, Entity, Fetch, FetchItem, World,
};
use alloc::vec::Vec;
use self::borrow::QueryBorrowState;
pub(crate) use borrow::*;
pub use data::*;
pub use dfs::*;
pub use entity::EntityBorrow;
pub(crate) use iter::*;
pub use one::QueryOne;
pub use planar::*;
pub use searcher::ArchetypeSearcher;
pub use topo::{Topo, TopoBorrow, TopoIter};
pub type EntityQuery<Q, F> = Query<Q, F, Entity>;
#[doc(hidden)]
pub trait QueryStrategy<'w, Q, F> {
type Borrow;
fn borrow(&'w mut self, query_state: QueryBorrowState<'w, Q, F>, dirty: bool) -> Self::Borrow;
fn access(&self, world: &'w World, fetch: &'w Filtered<Q, F>, dst: &mut Vec<Access>);
}
#[derive(Clone)]
pub struct Query<Q, F = All, S = Planar> {
fetch: Filtered<Q, F>,
change_tick: u32,
archetype_gen: u32,
strategy: S,
}
impl<Q: Debug, F: Debug, S: Debug> Debug for Query<Q, F, S>
where
Q: for<'x> Fetch<'x>,
F: for<'x> Fetch<'x>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Query")
.field("fetch", &FmtQuery(&self.fetch.fetch))
.field("filter", &FmtQuery(&self.fetch.filter))
.field("change_tick", &self.change_tick)
.field("strategy", &self.strategy)
.finish()
}
}
impl<Q> Query<Q, All, Planar> {
pub fn new(fetch: Q) -> Self
where
Q: for<'x> Fetch<'x>,
{
Self {
fetch: Filtered::new(fetch, All, false),
change_tick: 0,
strategy: Planar::new(),
archetype_gen: 0,
}
}
pub fn with_components(mut self) -> Self {
self.fetch.include_components = true;
self.archetype_gen = 0;
self
}
}
impl<Q, F> Query<Q, F, Planar>
where
Q: for<'x> Fetch<'x>,
F: for<'x> Fetch<'x>,
{
pub fn with_strategy<S>(self, strategy: S) -> Query<Q, F, S>
where
S: for<'w> QueryStrategy<'w, Q, F>,
{
Query {
fetch: self.fetch,
change_tick: self.change_tick,
archetype_gen: 0,
strategy,
}
}
pub fn entity(self, id: Entity) -> EntityQuery<Q, F>
where
Entity: for<'w> QueryStrategy<'w, Q, F>,
{
self.with_strategy(id)
}
pub fn topo<T: ComponentValue>(self, relation: impl RelationExt<T>) -> Query<Q, F, Topo>
where
Topo: for<'w> QueryStrategy<'w, Q, F>,
{
self.with_strategy(Topo::new(relation))
}
pub fn collect_vec<'w, T>(&'w mut self, world: &'w World) -> Vec<T>
where
T: 'static,
Q: for<'q> FetchItem<'q, Item = T>,
{
let mut borrow = self.borrow(world);
borrow.iter().collect()
}
pub fn collect_sorted_vec<'w, T>(&'w mut self, world: &'w World) -> Vec<T>
where
T: 'static + Ord,
Q: for<'q> FetchItem<'q, Item = T>,
{
let mut borrow = self.borrow(world);
borrow.iter().sorted().collect()
}
}
impl<Q, F, S> Query<Q, F, S>
where
Q: for<'x> Fetch<'x>,
F: for<'x> Fetch<'x>,
{
pub fn filter<G>(self, filter: G) -> Query<Q, F::PushRight, S>
where
F: TuplePush<G>,
{
Query {
fetch: Filtered::new(
self.fetch.fetch,
self.fetch.filter.push_right(filter),
self.fetch.include_components,
),
change_tick: self.change_tick,
archetype_gen: 0,
strategy: self.strategy,
}
}
pub fn batch_size(self, size: Slot) -> Query<Q, F::PushRight, S>
where
F: TuplePush<BatchSize>,
{
self.filter(BatchSize(size))
}
pub fn with_relation<T: ComponentValue>(
self,
rel: impl RelationExt<T>,
) -> Query<Q, F::PushRight, S>
where
F: TuplePush<WithRelation>,
{
self.filter(rel.with_relation())
}
pub fn without_relation<T: ComponentValue>(
self,
rel: impl RelationExt<T>,
) -> Query<Q, F::PushRight, S>
where
F: TuplePush<WithoutRelation>,
{
self.filter(rel.without_relation())
}
pub fn without<T: ComponentValue>(self, component: Component<T>) -> Query<Q, F::PushRight, S>
where
F: TuplePush<Without>,
{
self.filter(component.without())
}
pub fn with<T: ComponentValue>(self, component: Component<T>) -> Query<Q, F::PushRight, S>
where
F: TuplePush<With>,
{
self.filter(component.with())
}
fn prepare_tick(&mut self, world: &World) -> (u32, u32) {
let mut old_tick = self.change_tick;
let new_tick = if Q::MUTABLE {
world.advance_change_tick();
world.change_tick()
} else {
world.change_tick()
};
if new_tick < old_tick {
old_tick = 0;
}
self.change_tick = new_tick;
(old_tick, new_tick)
}
pub fn borrow<'w>(&'w mut self, world: &'w World) -> S::Borrow
where
S: QueryStrategy<'w, Q, F>,
{
profile_function!();
let (old_tick, new_tick) = self.prepare_tick(world);
let borrow_state = QueryBorrowState {
old_tick,
new_tick,
world,
fetch: &self.fetch,
};
let archetype_gen = world.archetype_gen();
let dirty = archetype_gen > self.archetype_gen;
self.archetype_gen = archetype_gen;
self.strategy.borrow(borrow_state, dirty)
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use crate::{
components::name, error::MissingComponent, filter::Or, Entity, Error, FetchExt, Query,
};
use super::*;
#[test]
fn changes() {
component! {
window_width: f32,
window_height: f32,
allow_vsync: bool,
resources,
}
let mut world = World::new();
Entity::builder()
.set(window_width(), 800.0)
.set(window_height(), 600.0)
.set(allow_vsync(), false)
.append_to(&mut world, resources())
.unwrap();
let mut query = Query::new((window_width(), window_height(), allow_vsync())).filter(Or((
window_width().modified(),
window_height().modified(),
allow_vsync().modified(),
)));
assert_eq!(
query.borrow(&world).get(resources()),
Ok((&800.0, &600.0, &false))
);
world.set(resources(), allow_vsync(), true).unwrap();
assert_eq!(
query.borrow(&world).get(resources()),
Ok((&800.0, &600.0, &true))
);
assert!(query.borrow(&world).get(resources()).is_err());
}
#[test]
fn get_disjoint() {
component! {
a: i32,
b: i32,
c: i32,
}
let mut world = World::new();
let _id = Entity::builder().set(a(), 5).set(b(), 5).spawn(&mut world);
let _id2 = Entity::builder()
.set(a(), 3)
.set(b(), 3)
.set(c(), 1)
.spawn(&mut world);
let _id3 = Entity::builder().set(a(), 7).set(b(), 5).spawn(&mut world);
let id4 = Entity::builder().set(a(), 7).spawn(&mut world);
let mut query = Query::new((a().modified(), b(), c().opt()));
let borrow = query.borrow(&world);
drop(borrow);
let mut borrow = query.borrow(&world);
assert_eq!(
borrow.get(id4),
Err(Error::MissingComponent(MissingComponent {
id: id4,
desc: b().desc()
}))
);
}
#[test]
fn test_planar() {
let mut world = World::new();
component! {
a: i32,
}
let id = Entity::builder()
.set(name(), "id".into())
.set(a(), 5)
.spawn(&mut world);
let id2 = Entity::builder()
.set(name(), "id2".into())
.set(a(), 7)
.spawn(&mut world);
let mut query = Query::new(name());
assert_eq!(query.borrow(&world).get(id), Ok(&"id".into()));
assert_eq!(query.borrow(&world).get(id2), Ok(&"id2".into()));
assert_eq!(
query.borrow(&world).get(a().id()),
Err(Error::DoesNotMatch(a().id()))
);
let mut query = query.with_components();
assert_eq!(query.borrow(&world).get(a().id()), Ok(&"a".into()));
}
}