checs 0.5.1

An Entity-Component-System library.
Documentation
//! Querying entities and their components.
//!
//! This is the "System" part of Entity-Component-System.

use crate::component;
use crate::entity;
use crate::iter;
use crate::sparse;

/// An iterator to find entities that share components.
///
/// It creates an intersection of [`ComponentVec`]s and yields the entities as well as their
/// components.
///
/// The `struct` is created by calling the [`into_query`] method on tuples of [`ComponentVec`]s.
///
/// [`ComponentVec`]: component::ComponentVec
/// [`into_query`]: IntoQuery::into_query
///
/// # Examples
///
/// ```
/// use checs::{ComponentVec, IntoQuery, LendingIterator};
/// use checs::entity::Allocator;
///
/// let mut entities = Allocator::new();
/// let e0 = entities.alloc();
/// let e1 = entities.alloc();
/// let e2 = entities.alloc();
/// let e3 = entities.alloc();
/// let e4 = entities.alloc();
///
/// let mut strs = ComponentVec::new();
/// strs.insert(e0, "ninety-nine");
/// strs.insert(e1, "zero");
/// strs.insert(e2, "forty-two");
/// strs.insert(e4, "seventeen");
///
/// let mut ints = ComponentVec::new();
/// ints.insert(e0, 99);
/// ints.insert(e2, 42);
/// ints.insert(e3, 1);
/// ints.insert(e4, 17);
///
/// let mut query = (&strs, &mut ints).into_query();
///
/// assert_eq!(query.next(), Some((e0, (&"ninety-nine", &mut 99))));
/// assert_eq!(query.next(), Some((e2, (&"forty-two", &mut 42))));
/// assert_eq!(query.next(), Some((e4, (&"seventeen", &mut 17))));
/// assert_eq!(query.next(), None);
/// ```
/// > Note: In the above example entities `e1` and `e3` each only have one of the components not
/// > both, so they are not included in the query.
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Query<I, C> {
    entity_iter: I,
    components: C,
}

/// A trait for converting a value into some kind of query.
///
/// This crate provides implementations for [`ComponentVec`]s. More specifically it provides
/// generic implementations for tuples of types that implement [`IntoRefOrMut`].
///
/// [`ComponentVec`]: component::ComponentVec
/// [`IntoRefOrMut`]: component::IntoRefOrMut
///
/// # Examples
///
/// See [`Query`] for more interesting examples.
///
/// ```
/// use checs::{ComponentVec, IntoQuery};
///
/// let ints: ComponentVec<i32> = ComponentVec::new();
/// let mut floats: ComponentVec<f32> = ComponentVec::new();
///
/// let query = (&ints, &mut floats).into_query();
/// ```
pub trait IntoQuery<'a> {
    /// The type of the query.
    type Query;

    /// Creates a query from a value.
    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,)*)
    };

    // This should expand to something like the following...
    //
    // ```
    // Intersection::new(Intersection::new(sets[0].iter(), &sets[1]), &sets[2])
    // ```
    ($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 {
                // NOTE The type parameters `$T` are also used as variable
                // names. Therefore `non_snake_case` is allowed.
                #![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> {
                // NOTE The type parameters `$T` are also used as variable
                // names. Therefore `non_snake_case` is allowed.
                #![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));
    }
}