use crate::{
change_detection::MutUntyped,
component::ComponentId,
world::{error::EntityComponentError, unsafe_world_cell::UnsafeEntityCell},
};
use alloc::vec::Vec;
use bevy_platform::collections::{HashMap, HashSet};
use bevy_ptr::Ptr;
use core::mem::MaybeUninit;
pub unsafe trait DynamicComponentFetch {
type Ref<'w>;
type Mut<'w>;
unsafe fn fetch_ref(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Ref<'_>, EntityComponentError>;
unsafe fn fetch_mut(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError>;
unsafe fn fetch_mut_assume_mutable(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError>;
}
unsafe impl DynamicComponentFetch for ComponentId {
type Ref<'w> = Ptr<'w>;
type Mut<'w> = MutUntyped<'w>;
unsafe fn fetch_ref(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Ref<'_>, EntityComponentError> {
unsafe { cell.get_by_id(self) }.ok_or(EntityComponentError::MissingComponent(self))
}
unsafe fn fetch_mut(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
unsafe { cell.get_mut_by_id(self) }
.map_err(|_| EntityComponentError::MissingComponent(self))
}
unsafe fn fetch_mut_assume_mutable(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
unsafe { cell.get_mut_assume_mutable_by_id(self) }
.map_err(|_| EntityComponentError::MissingComponent(self))
}
}
unsafe impl<const N: usize> DynamicComponentFetch for [ComponentId; N] {
type Ref<'w> = [Ptr<'w>; N];
type Mut<'w> = [MutUntyped<'w>; N];
unsafe fn fetch_ref(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Ref<'_>, EntityComponentError> {
unsafe { <&Self>::fetch_ref(&self, cell) }
}
unsafe fn fetch_mut(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
unsafe { <&Self>::fetch_mut(&self, cell) }
}
unsafe fn fetch_mut_assume_mutable(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
unsafe { <&Self>::fetch_mut_assume_mutable(&self, cell) }
}
}
unsafe impl<const N: usize> DynamicComponentFetch for &'_ [ComponentId; N] {
type Ref<'w> = [Ptr<'w>; N];
type Mut<'w> = [MutUntyped<'w>; N];
unsafe fn fetch_ref(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Ref<'_>, EntityComponentError> {
let mut ptrs = [const { MaybeUninit::uninit() }; N];
for (ptr, &id) in core::iter::zip(&mut ptrs, self) {
*ptr = MaybeUninit::new(
unsafe { cell.get_by_id(id) }.ok_or(EntityComponentError::MissingComponent(id))?,
);
}
let ptrs = ptrs.map(|ptr| unsafe { MaybeUninit::assume_init(ptr) });
Ok(ptrs)
}
unsafe fn fetch_mut(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
for i in 0..self.len() {
for j in 0..i {
if self[i] == self[j] {
return Err(EntityComponentError::AliasedMutability(self[i]));
}
}
}
let mut ptrs = [const { MaybeUninit::uninit() }; N];
for (ptr, &id) in core::iter::zip(&mut ptrs, self) {
*ptr = MaybeUninit::new(
unsafe { cell.get_mut_by_id(id) }
.map_err(|_| EntityComponentError::MissingComponent(id))?,
);
}
let ptrs = ptrs.map(|ptr| unsafe { MaybeUninit::assume_init(ptr) });
Ok(ptrs)
}
unsafe fn fetch_mut_assume_mutable(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
for i in 0..self.len() {
for j in 0..i {
if self[i] == self[j] {
return Err(EntityComponentError::AliasedMutability(self[i]));
}
}
}
let mut ptrs = [const { MaybeUninit::uninit() }; N];
for (ptr, &id) in core::iter::zip(&mut ptrs, self) {
*ptr = MaybeUninit::new(
unsafe { cell.get_mut_assume_mutable_by_id(id) }
.map_err(|_| EntityComponentError::MissingComponent(id))?,
);
}
let ptrs = ptrs.map(|ptr| unsafe { MaybeUninit::assume_init(ptr) });
Ok(ptrs)
}
}
unsafe impl DynamicComponentFetch for &'_ [ComponentId] {
type Ref<'w> = Vec<Ptr<'w>>;
type Mut<'w> = Vec<MutUntyped<'w>>;
unsafe fn fetch_ref(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Ref<'_>, EntityComponentError> {
let mut ptrs = Vec::with_capacity(self.len());
for &id in self {
ptrs.push(
unsafe { cell.get_by_id(id) }.ok_or(EntityComponentError::MissingComponent(id))?,
);
}
Ok(ptrs)
}
unsafe fn fetch_mut(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
for i in 0..self.len() {
for j in 0..i {
if self[i] == self[j] {
return Err(EntityComponentError::AliasedMutability(self[i]));
}
}
}
let mut ptrs = Vec::with_capacity(self.len());
for &id in self {
ptrs.push(
unsafe { cell.get_mut_by_id(id) }
.map_err(|_| EntityComponentError::MissingComponent(id))?,
);
}
Ok(ptrs)
}
unsafe fn fetch_mut_assume_mutable(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
for i in 0..self.len() {
for j in 0..i {
if self[i] == self[j] {
return Err(EntityComponentError::AliasedMutability(self[i]));
}
}
}
let mut ptrs = Vec::with_capacity(self.len());
for &id in self {
ptrs.push(
unsafe { cell.get_mut_assume_mutable_by_id(id) }
.map_err(|_| EntityComponentError::MissingComponent(id))?,
);
}
Ok(ptrs)
}
}
unsafe impl DynamicComponentFetch for &'_ HashSet<ComponentId> {
type Ref<'w> = HashMap<ComponentId, Ptr<'w>>;
type Mut<'w> = HashMap<ComponentId, MutUntyped<'w>>;
unsafe fn fetch_ref(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Ref<'_>, EntityComponentError> {
let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default());
for &id in self {
ptrs.insert(
id,
unsafe { cell.get_by_id(id) }.ok_or(EntityComponentError::MissingComponent(id))?,
);
}
Ok(ptrs)
}
unsafe fn fetch_mut(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default());
for &id in self {
ptrs.insert(
id,
unsafe { cell.get_mut_by_id(id) }
.map_err(|_| EntityComponentError::MissingComponent(id))?,
);
}
Ok(ptrs)
}
unsafe fn fetch_mut_assume_mutable(
self,
cell: UnsafeEntityCell<'_>,
) -> Result<Self::Mut<'_>, EntityComponentError> {
let mut ptrs = HashMap::with_capacity_and_hasher(self.len(), Default::default());
for &id in self {
ptrs.insert(
id,
unsafe { cell.get_mut_assume_mutable_by_id(id) }
.map_err(|_| EntityComponentError::MissingComponent(id))?,
);
}
Ok(ptrs)
}
}