ivy-resources 0.10.3

Handle based resource management and shared state
use atomic_refcell::AtomicRef;
use derive_more::{From, Into};
use hecs::Component;
use hecs_schedule::{
    borrow::{ComponentBorrow, ContextBorrow},
    impl_into_borrow, *,
};
use smallvec::smallvec;
use std::{any::type_name, marker::PhantomData};

use crate::{ResourceCache, Resources};

#[derive(From, Into)]
pub struct ResourceView<'a, T>(pub(crate) AtomicRef<'a, ResourceCache<T>>);

pub struct ResourceViewMut<'a, T> {
    value: *mut ResourceCache<T>,
    pub cell: AtomicRef<'a, Resources>,
}

impl<'a, T> std::ops::Deref for ResourceView<'a, T> {
    type Target = ResourceCache<T>;

    fn deref(&self) -> &Self::Target {
        self.0.deref()
    }
}

impl<'a, T> std::ops::Deref for ResourceViewMut<'a, T> {
    type Target = ResourceCache<T>;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.value }
    }
}

impl<'a, T> std::ops::DerefMut for ResourceViewMut<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { &mut *self.value }
    }
}

impl<'a, T: Component> ContextBorrow<'a> for ResourceView<'a, T> {
    type Target = Self;

    fn borrow(context: &'a Context) -> hecs_schedule::error::Result<Self::Target> {
        context
            .cell::<&Resources>()?
            .try_borrow()
            .map_err(|_| hecs_schedule::Error::Borrow(type_name::<T>()))
            .map(|cell| {
                AtomicRef::map(cell, |cell| unsafe {
                    cell.cast::<Resources>()
                        .as_ref()
                        .fetch()
                        .expect("Failed to borrow from resources")
                        .value
                })
                .into()
            })
    }
}

pub(crate) struct BorrowMarker<T>(PhantomData<T>);

impl<'a, T: 'static> ComponentBorrow for ResourceView<'a, T> {
    fn borrows() -> borrow::Borrows {
        smallvec![Access::of::<&BorrowMarker<T>>()]
    }

    fn has_dynamic(id: std::any::TypeId, exclusive: bool) -> bool {
        let l = Access::of::<&T>();

        l.id() == id && !exclusive
    }

    fn has<U: IntoAccess>() -> bool {
        Access::of::<&T>() == U::access()
    }
}

impl<'a, T: Component> ContextBorrow<'a> for ResourceViewMut<'a, T> {
    type Target = Self;

    fn borrow(context: &'a Context) -> hecs_schedule::error::Result<Self::Target> {
        context
            .cell::<&Resources>()?
            .try_borrow()
            .map_err(|_| hecs_schedule::Error::Borrow(type_name::<T>()))
            .map(|cell| {
                let cell =
                    AtomicRef::map(cell, |cell| unsafe { cell.cast::<Resources>().as_ref() });

                let value = cell
                    .fetch_mut()
                    .expect("Failed to borrow from resources mutably")
                    .value as *mut _;

                ResourceViewMut { value, cell }
            })
    }
}

impl<'a, T: 'static> ComponentBorrow for ResourceViewMut<'a, T> {
    fn borrows() -> borrow::Borrows {
        smallvec![Access::of::<&mut BorrowMarker<T>>()]
    }

    fn has_dynamic(id: std::any::TypeId, _: bool) -> bool {
        let l = Access::of::<&mut T>();

        l.id() == id
    }

    fn has<U: IntoAccess>() -> bool {
        Access::of::<&mut T>().id() == U::access().id()
    }
}

impl_into_borrow!(Component, ResourceView => ResourcesBorrow);
impl_into_borrow!(Component, ResourceViewMut => ResourcesBorrowMut);