bevy_spliff 0.1.2

A crate for doing joins in bevy.
Documentation
use bevy_ecs::{
    component::ComponentId,
    prelude::*,
    query::{EcsAccessLevel, EcsAccessType, QueryData, WorldQuery},
    world::unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell},
};
use std::iter;

pub trait JoinResultMapper: 'static + Send + Sync {
    type Item<'w, 's, D: QueryData>;

    fn map_results<'w, 's, D: QueryData>(items: Vec<D::Item<'w, 's>>) -> Self::Item<'w, 's, D>;

    fn shrink<'wlong: 'wshort, 'wshort, 's, D: QueryData>(
        item: Self::Item<'wlong, 's, D>,
    ) -> Self::Item<'wshort, 's, D>;
}

pub trait Joinable: Component {
    type Out<'a>: Iterator<Item = Entity>
    where
        Self: 'a;
    type Mapper: JoinResultMapper;

    fn targets(&self) -> Self::Out<'_>;
}

pub(crate) trait FetchJoiner<'w, Ref: Joinable + Component> {
    fn world(&self) -> UnsafeWorldCell<'w>;

    #[inline(always)]
    unsafe fn iter_joined(
        &self,
        entity: Entity,
    ) -> Option<impl Iterator<Item = (Entity, UnsafeEntityCell<'w>)>> {
        unsafe {
            let r = self.world().get_entity(entity).ok()?.get::<Ref>()?;
            Some(r.targets().filter_map(|target| {
                self.world()
                    .get_entity(target)
                    .ok()
                    .map(|cell| (target, cell))
            }))
        }
    }
}

pub(crate) trait JoinState {
    type Data: QueryData;
    fn ref_id(&self) -> ComponentId;
    fn target_state(&self) -> &<Self::Data as WorldQuery>::State;

    #[inline(always)]
    fn iter_access(&self) -> impl Iterator<Item = EcsAccessType<'_>> {
        iter::once(EcsAccessType::Component(EcsAccessLevel::Read(
            self.ref_id(),
        )))
        .chain(Self::Data::iter_access(self.target_state()))
    }
}