use core::{ffi::c_void, marker::PhantomData, ptr::NonNull};
use crate::core::*;
use crate::sys;
#[derive(Debug, Clone, Copy)]
pub struct CachedRef<'a, T> {
pub(crate) world: WorldRef<'a>,
pub(crate) component_ref: sys::ecs_ref_t,
pub(crate) _marker: PhantomData<T>,
}
impl<'a, T> CachedRef<'a, T> {
pub fn new(
world: impl WorldProvider<'a>,
entity: impl Into<Entity>,
component: impl IntoId,
) -> CachedRef<'a, T> {
let world = world.world();
let world_ptr = unsafe {
sys::ecs_get_world(world.world_ptr_mut() as *const c_void) as *mut sys::ecs_world_t
};
let id = *component.into_id(world);
debug_assert!(
id != 0,
"Tried to create invalid `CachedRef` type. id is 0."
);
const {
assert!(
core::mem::size_of::<T>() != 0,
"Cached Ref cannot be created for zero-sized types / tags."
);
}
debug_assert!(
{
let type_ = unsafe { sys::ecs_get_typeid(world_ptr, id) };
let ti = unsafe { sys::ecs_get_type_info(world_ptr, type_) };
ti.is_null() || unsafe { (*ti).size } != 0
},
"Cannot create ref to empty type"
);
let component_ref = unsafe { sys::ecs_ref_init_id(world_ptr, *entity.into(), id) };
assert_ne!(
component_ref.entity, 0,
"Tried to create invalid `CachedRef` type."
);
CachedRef::<T> {
world,
component_ref,
_marker: PhantomData,
}
}
pub fn entity(&self) -> EntityView<'a> {
EntityView::new_from(self.world, self.component_ref.entity)
}
pub fn component(&self) -> IdView<'a> {
IdView::new_from_id(self.world, self.component_ref.id)
}
pub fn has(&mut self) -> bool {
!unsafe {
sys::ecs_ref_get_id(
self.world.world_ptr_mut(),
&mut self.component_ref,
self.component_ref.id,
)
}
.is_null()
}
pub fn world(&self) -> WorldRef<'a> {
self.world
}
}
impl<'a, T: ComponentId> CachedRef<'a, T> {
pub fn try_get<R>(&mut self, callback: impl FnOnce(&mut T) -> R) -> Option<R> {
NonNull::new(unsafe {
sys::ecs_ref_get_id(
self.world.world_ptr_mut(),
&mut self.component_ref,
self.component_ref.id,
) as *mut T
})
.map(|mut t| unsafe { t.as_mut() })
.map(callback)
}
pub fn get<R>(&mut self, callback: impl FnOnce(&mut T) -> R) -> R {
let mut ref_comp = NonNull::new(unsafe {
sys::ecs_ref_get_id(
self.world.world_ptr_mut(),
&mut self.component_ref,
self.component_ref.id,
) as *mut T
})
.expect("Component not found, use try_get if you want to handle this case");
callback(unsafe { ref_comp.as_mut() })
}
}
impl<'a> CachedRef<'a, core::ffi::c_void> {
pub fn try_get<R>(&mut self, callback: impl FnOnce(*mut core::ffi::c_void) -> R) -> Option<R> {
NonNull::new(unsafe {
sys::ecs_ref_get_id(
self.world.world_ptr_mut(),
&mut self.component_ref,
self.component_ref.id,
)
})
.map(NonNull::as_ptr)
.map(callback)
}
pub fn get<R>(&mut self, callback: impl FnOnce(*mut core::ffi::c_void) -> R) -> R {
let ref_comp = NonNull::new(unsafe {
sys::ecs_ref_get_id(
self.world.world_ptr_mut(),
&mut self.component_ref,
self.component_ref.id,
)
})
.expect("Component not found, use try_get if you want to handle this case");
callback(ref_comp.as_ptr())
}
}