spru-bevy 0.1.0

bevy plugins for the spru strategy and digital board game framework.
Documentation
use std::{any, marker::PhantomData};

use bevy::prelude;
use spru::item;

use crate::{client::component, common};

#[doc(hidden)]
#[derive(Debug)]
pub struct BevyStorage<'l, State> {
    world: &'l mut prelude::World,
    entity_map: &'l mut super::component::EntityMap,
    game_id: spru::game::Id,
    client_id: spru::player::Id,
    _state: PhantomData<fn() -> State>,
}

impl<'l, State> BevyStorage<'l, State> {
    pub(crate) fn new(
        world: &'l mut prelude::World,
        entity_map: &'l mut super::component::EntityMap,
        game_id: spru::game::Id,
        client_id: spru::player::Id,
    ) -> Self {
        Self {
            world,
            entity_map,
            game_id,
            client_id,
            _state: PhantomData,
        }
    }
}

impl<'l, State: spru::State> spru::item::Storage for BevyStorage<'l, State> {
    type State = State;

    fn get<T>(&self, id: item::IdT<T>) -> spru::item::storage::Result<&spru::Item<T>>
    where
        T: spru::item::storage::Storable<Self::State>,
    {
        let id = id.untyped();
        let entity = self.entity_map.get(id)?;
        Ok(self
            .world
            .get::<component::Item<T>>(entity)
            .ok_or(super::BevyError::ComponentNotFound(
                id,
                entity,
                any::type_name::<T>(),
            ))?
            .item())
    }

    #[allow(refining_impl_trait)]
    fn get_mut<T>(
        &mut self,
        id: item::IdT<T>,
    ) -> spru::item::storage::Result<bevy::prelude::Mut<'_, spru::Item<T>>>
    where
        T: spru::item::storage::Storable<Self::State>,
    {
        let id = id.untyped();
        let entity = self.entity_map.get(id)?;
        Ok(self
            .world
            .get_mut::<component::Item<T>>(entity)
            .ok_or(super::BevyError::ComponentNotFound(
                id,
                entity,
                any::type_name::<T>(),
            ))?
            .map_unchanged(|sc| sc.item_mut()))
    }

    fn create<T>(&mut self, value: spru::Item<T>) -> spru::item::storage::Result<()>
    where
        T: spru::item::storage::Storable<Self::State>,
    {
        let id = value.id().untyped();
        self.entity_map.insert_as(id, || {
            Ok(self
                .world
                .spawn((
                    prelude::Name::new(format!(
                        "[{:x}:{}.{id}] {}",
                        self.game_id.friendly_display(),
                        self.client_id,
                        any::type_name::<T>()
                    )),
                    component::Item::new(value),
                    common::component::GameId::new(self.game_id),
                    super::component::ClientId::new(self.client_id),
                ))
                .id())
        })?;
        Ok(())
    }

    fn destroy<T>(&mut self, id: item::IdT<T>) -> spru::item::storage::Result<spru::Item<T>>
    where
        T: spru::item::storage::Storable<Self::State>,
    {
        let id = id.untyped();
        self.entity_map
            .remove_as(id, |entity| {
                let mut entity_mut = self
                    .world
                    .get_entity_mut(entity)
                    .map_err(|_| super::BevyError::EntityNotFound(id, entity))?;
                match entity_mut.take::<component::Item<T>>() {
                    Some(item) => Ok(item.into_inner()),
                    None => Err(super::BevyError::ComponentNotFound(
                        id,
                        entity,
                        any::type_name::<T>(),
                    )),
                }
            })
            .map_err(Into::into)
    }
}