use crate::component;
use crate::entity;
use crate::iter;
use crate::sparse;
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Query<I, C> {
entity_iter: I,
components: C,
}
pub trait IntoQuery<'a> {
type Query;
fn into_query(self) -> Self::Query;
}
macro_rules! expand {
($macro:ident, $head:ident) => { };
($macro:ident, $head:ident, $($tail:ident),*) => {
$macro!{$head, $($tail),*}
expand!{$macro, $($tail),*}
};
}
#[inline]
fn swap_shortest_first<const N: usize>(sets: &mut [&sparse::Set; N]) {
if let Some((i, _)) = sets.iter().enumerate().min_by_key(|(_, s)| s.len()) {
sets.swap(0, i);
}
}
macro_rules! init_intersection {
(@step $_idx:expr, $sets:ident, $acc:tt,) => {
$acc
};
(@step $idx:expr, $sets:ident, $acc:tt, $head:ident, $($tail:ident,)*) => {
init_intersection!(@step $idx + 1usize, $sets, ($crate::intersect::Intersection::new($acc, &$sets[$idx])), $($tail,)*)
};
($sets:ident, $head:ident, $($tail:ident,)*) => {
init_intersection!(@step 1usize, $sets, ($sets[0].iter()), $($tail,)*)
};
}
macro_rules! intersection_type {
($_head:ty) => {
$crate::iter::Iter<'a, $crate::entity::Entity>
};
($_head:ty, $($tail:ty),*) => {
$crate::intersect::Intersection<'a, intersection_type!($($tail),*)>
};
}
macro_rules! impl_query {
($($T:ident),*) => {
impl<'a, $($T),*> IntoQuery<'a> for ($($T),*,)
where
$(
$T: $crate::component::IntoRefOrMut<'a>,
)*
{
type Query = Query<
intersection_type!($($T),*),
($(
$crate::component::RefOrMut<'a, $T::Components>,
)*)
>;
#[inline]
fn into_query(self) -> Self::Query {
#![allow(non_snake_case)]
let ($($T),*,) = self;
$(
let $T = $T.into_ref_or_mut();
)*
let mut sets = [$($T.entities),*];
swap_shortest_first(&mut sets);
let it = init_intersection!(sets, $($T),*,);
Query {
entity_iter: it,
components: ($($T),*,),
}
}
}
impl<'l, $($T),*> $crate::iter::Lifetime<'l> for ($($T),*,)
where
$(
$T: $crate::iter::Lifetime<'l>,
)*
{
type Item = (
$(
<$T as $crate::iter::Lifetime<'l>>::Item,
)*
);
}
impl<'a, $($T),*> $crate::component::GetRefOrMut for ($($T),*,)
where
$(
$T: $crate::component::GetRefOrMut,
)*
{
#[inline]
fn get_ref_or_mut(
&mut self,
entity: $crate::entity::Entity
) -> Option<<Self as $crate::iter::Lifetime<'_>>::Item> {
#![allow(non_snake_case)]
let ($($T),*,) = self;
Some((
$(
$T.get_ref_or_mut(entity)?,
)*
))
}
}
};
}
expand!(impl_query, A, B, C, D, E, F, G);
impl<'l, Iter, C> iter::Lifetime<'l> for Query<Iter, C>
where
Iter: Iterator<Item = entity::Entity>,
C: iter::Lifetime<'l>,
{
type Item = (entity::Entity, <C as iter::Lifetime<'l>>::Item);
}
impl<Iter, C> iter::LendingIterator for Query<Iter, C>
where
Iter: Iterator<Item = entity::Entity>,
C: component::GetRefOrMut,
{
#[inline]
fn next(&mut self) -> Option<<Self as iter::Lifetime<'_>>::Item> {
let entity = self.entity_iter.next()?;
let ref_or_mut = self
.components
.get_ref_or_mut(entity)
.expect("entity must exist");
Some((entity, ref_or_mut))
}
}
impl<'a, T> IntoQuery<'a> for &'a component::ComponentVec<T>
where
T: 'a,
{
type Query = component::Iter<'a, T>;
#[inline]
fn into_query(self) -> Self::Query {
self.iter()
}
}
impl<'a, T> IntoQuery<'a> for (&'a component::ComponentVec<T>,)
where
T: 'a,
{
type Query = component::Iter<'a, T>;
#[inline]
fn into_query(self) -> Self::Query {
self.0.iter()
}
}
impl<'a, T> IntoQuery<'a> for &'a mut component::ComponentVec<T>
where
T: 'a,
{
type Query = component::IterMut<'a, T>;
#[inline]
fn into_query(self) -> Self::Query {
self.iter_mut()
}
}
impl<'a, T> IntoQuery<'a> for (&'a mut component::ComponentVec<T>,)
where
T: 'a,
{
type Query = component::IterMut<'a, T>;
#[inline]
fn into_query(self) -> Self::Query {
self.0.iter_mut()
}
}
#[cfg(test)]
mod tests {
use crate::component;
use crate::entity::Entity;
#[test]
fn into_query() {
let mut comps1 = component::ComponentVec::new();
comps1.insert(Entity::from(0), "ninety-nine");
comps1.insert(Entity::from(1), "zero");
comps1.insert(Entity::from(2), "forty-two");
comps1.insert(Entity::from(4), "seventeen");
let mut comps2 = component::ComponentVec::new();
comps2.insert(Entity::from(0), 99);
comps2.insert(Entity::from(2), 41);
comps2.insert(Entity::from(3), 1);
comps2.insert(Entity::from(4), 17);
use crate::query::IntoQuery;
let mut query = (&comps1, &mut comps2).into_query();
use crate::iter::LendingIterator;
assert_eq!(
query.next(),
Some((Entity::from(0), (&"ninety-nine", &mut 99)))
);
let item = query.next();
assert_eq!(item, Some((Entity::from(2), (&"forty-two", &mut 41))));
if let Some((_entity, (_str, int))) = item {
*int += 1;
}
assert_eq!(
query.next(),
Some((Entity::from(4), (&"seventeen", &mut 17)))
);
assert_eq!(query.next(), None);
assert_eq!(comps2.get(Entity::from(2)), Some(&42));
}
}