diff --git a/src/lib.rs b/src/lib.rs index 3261ebf5..a368e4b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,8 +126,9 @@ pub use tracking::{ }; pub use unique::UniqueStorage; pub use views::{ - AllStoragesView, AllStoragesViewMut, EntitiesView, EntitiesViewMut, UniqueView, UniqueViewMut, - View, ViewMut, + AllStoragesView, AllStoragesViewMut, EntitiesView, EntitiesViewMut, UniqueOrDefaultView, + UniqueOrDefaultViewMut, UniqueOrInitView, UniqueOrInitViewMut, UniqueView, UniqueViewMut, View, + ViewMut, }; pub use world::{World, WorldBuilder}; diff --git a/src/views.rs b/src/views.rs index 277246de..71d78625 100644 --- a/src/views.rs +++ b/src/views.rs @@ -1,5 +1,9 @@ mod all_storages; mod entities; +mod unique_or_default; +mod unique_or_default_mut; +mod unique_or_init; +mod unique_or_init_mut; mod unique_view; mod unique_view_mut; mod view; @@ -7,6 +11,10 @@ mod view_mut; pub use all_storages::{AllStoragesView, AllStoragesViewMut}; pub use entities::{EntitiesView, EntitiesViewMut}; +pub use unique_or_default::UniqueOrDefaultView; +pub use unique_or_default_mut::UniqueOrDefaultViewMut; +pub use unique_or_init::UniqueOrInitView; +pub use unique_or_init_mut::UniqueOrInitViewMut; pub use unique_view::UniqueView; pub use unique_view_mut::UniqueViewMut; pub use view::View; diff --git a/src/views/unique_or_default.rs b/src/views/unique_or_default.rs new file mode 100644 index 00000000..96927b8b --- /dev/null +++ b/src/views/unique_or_default.rs @@ -0,0 +1,61 @@ +use crate::all_storages::AllStorages; +use crate::atomic_refcell::ARef; +use crate::borrow::{Borrow, WorldBorrow}; +use crate::component::Unique; +use crate::info::TypeInfo; +use crate::tracking::TrackingTimestamp; +use crate::views::UniqueView; +use crate::world::World; +use crate::{error, BorrowInfo}; +use std::ops::Deref; + +/// Shared view over a unique component storage. +/// +/// If the component is not already present, its default value will be inserted. +pub struct UniqueOrDefaultView<'v, T: Unique + Default>(UniqueView<'v, T>); + +impl<'v, T: Unique + Default> Deref for UniqueOrDefaultView<'v, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'v, T: Unique + Default + Send + Sync> WorldBorrow for UniqueOrDefaultView<'v, T> { + type WorldView<'a> = UniqueOrDefaultView<'a, T>; + + fn world_borrow( + world: &World, + last_run: Option, + current: TrackingTimestamp, + ) -> Result, error::GetStorage> { + let all_storages = world + .all_storages() + .map_err(error::GetStorage::AllStoragesBorrow)?; + + match all_storages.borrow::>() { + Ok(_) => {} + Err(error::GetStorage::MissingStorage { .. }) => all_storages.add_unique(T::default()), + Err(err) => return Err(err), + }; + + let (all_storages, all_borrow) = unsafe { ARef::destructure(all_storages) }; + + let view = UniqueView::borrow(all_storages, Some(all_borrow), last_run, current)?; + + Ok(UniqueOrDefaultView(view)) + } +} + +unsafe impl<'v, T: Unique + Default + Send + Sync> BorrowInfo for UniqueOrDefaultView<'v, T> { + fn borrow_info(info: &mut Vec) { + UniqueView::::borrow_info(info); + } + + fn enable_tracking( + enable_tracking_fn: &mut Vec Result<(), error::GetStorage>>, + ) { + UniqueView::::enable_tracking(enable_tracking_fn); + } +} diff --git a/src/views/unique_or_default_mut.rs b/src/views/unique_or_default_mut.rs new file mode 100644 index 00000000..80369f9a --- /dev/null +++ b/src/views/unique_or_default_mut.rs @@ -0,0 +1,67 @@ +use crate::all_storages::AllStorages; +use crate::atomic_refcell::ARef; +use crate::borrow::{Borrow, WorldBorrow}; +use crate::component::Unique; +use crate::info::TypeInfo; +use crate::tracking::TrackingTimestamp; +use crate::views::UniqueViewMut; +use crate::world::World; +use crate::{error, BorrowInfo}; +use std::ops::{Deref, DerefMut}; + +/// Exclusive view over a unique component storage. +/// +/// If the component is not already present, its default value will be inserted. +pub struct UniqueOrDefaultViewMut<'v, T: Unique + Default>(UniqueViewMut<'v, T>); + +impl<'v, T: Unique + Default> Deref for UniqueOrDefaultViewMut<'v, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'v, T: Unique + Default> DerefMut for UniqueOrDefaultViewMut<'v, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'v, T: Unique + Default + Send + Sync> WorldBorrow for UniqueOrDefaultViewMut<'v, T> { + type WorldView<'a> = UniqueOrDefaultViewMut<'a, T>; + + fn world_borrow( + world: &World, + last_run: Option, + current: TrackingTimestamp, + ) -> Result, error::GetStorage> { + let all_storages = world + .all_storages() + .map_err(error::GetStorage::AllStoragesBorrow)?; + + match all_storages.borrow::>() { + Ok(_) => {} + Err(error::GetStorage::MissingStorage { .. }) => all_storages.add_unique(T::default()), + Err(err) => return Err(err), + }; + + let (all_storages, all_borrow) = unsafe { ARef::destructure(all_storages) }; + + let view = UniqueViewMut::borrow(all_storages, Some(all_borrow), last_run, current)?; + + Ok(UniqueOrDefaultViewMut(view)) + } +} + +unsafe impl<'v, T: Unique + Default + Send + Sync> BorrowInfo for UniqueOrDefaultViewMut<'v, T> { + fn borrow_info(info: &mut Vec) { + UniqueViewMut::::borrow_info(info); + } + + fn enable_tracking( + enable_tracking_fn: &mut Vec Result<(), error::GetStorage>>, + ) { + UniqueViewMut::::enable_tracking(enable_tracking_fn); + } +} diff --git a/src/views/unique_or_init.rs b/src/views/unique_or_init.rs new file mode 100644 index 00000000..97e9db6f --- /dev/null +++ b/src/views/unique_or_init.rs @@ -0,0 +1,181 @@ +use crate::all_storages::AllStorages; +use crate::atomic_refcell::{ARef, SharedBorrow}; +use crate::borrow::{Borrow, BorrowInfo, WorldBorrow}; +use crate::component::Unique; +use crate::error; +use crate::info::TypeInfo; +use crate::tracking::TrackingTimestamp; +use crate::views::UniqueView; +use crate::world::World; +use core::cell::OnceCell; + +/// Shared view over a unique component storage. +/// +/// The component can be initialized with this view. +pub struct UniqueOrInitView<'v, T: Unique + Default> { + cell: OnceCell>, + all_storages: &'v AllStorages, + all_borrow: SharedBorrow<'v>, + last_run: Option, + current: TrackingTimestamp, +} + +impl<'v, T: Unique + Default + Send + Sync> UniqueOrInitView<'v, T> { + /// Gets a reference to the inner [`UniqueView`]. + /// + /// Returns `None` if this view doesn't contains the inner [`UniqueView`]. + pub fn get(&self) -> Option<&UniqueView<'v, T>> { + self.cell.get() + } + + /// Adds the unique component to the `World`. + /// + /// Returns `true` if the component was inserted. + /// + /// ### Borrows + /// + /// - `Unique` storage (shared) + /// + /// ### Errors + /// + /// - `Unique` storage borrow failed (it can be initialized and borrowed elsewhere). + pub fn set(&self, unique: T) -> Result { + if self.cell.get().is_some() { + return Ok(false); + } + + self.all_storages.add_unique(unique); + + self.cell + .set(UniqueView::borrow( + self.all_storages, + Some(self.all_borrow.clone()), + self.last_run, + self.current, + )?) + .unwrap_or_else(|_| unreachable!("Cell is expected to be empty")); + + Ok(true) + } + + /// Fetches the unique component from the `World`. + /// + /// Returns `true` if the component was fetched. + /// + /// ### Borrows + /// + /// - `Unique` storage (shared) + /// + /// ### Errors + /// + /// - `Unique` storage borrow failed (it can be initialized and borrowed elsewhere). + pub fn fetch(&self) -> Result { + if self.cell.get().is_some() { + return Ok(false); + } + + self.cell + .set(UniqueView::borrow( + self.all_storages, + Some(self.all_borrow.clone()), + self.last_run, + self.current, + )?) + .unwrap_or_else(|_| unreachable!("Cell is expected to be empty")); + + Ok(true) + } + + /// Gets the unique component from the `World`.\ + /// Adds it to the `World` if not present. + /// + /// ### Borrows + /// + /// - `Unique` storage (shared) + /// + /// ### Errors + /// + /// - `Unique` storage borrow failed (it can be initialized and borrowed elsewhere). + pub fn get_or_init( + &self, + f: impl FnOnce() -> T, + ) -> Result<&UniqueView<'v, T>, error::GetStorage> { + if let Some(view) = self.cell.get() { + return Ok(view); + } + + let view = match UniqueView::borrow( + self.all_storages, + Some(self.all_borrow.clone()), + self.last_run, + self.current, + ) { + Ok(view) => view, + Err(error::GetStorage::MissingStorage { .. }) => { + self.all_storages.add_unique(f()); + + UniqueView::borrow( + self.all_storages, + Some(self.all_borrow.clone()), + self.last_run, + self.current, + )? + } + Err(err) => return Err(err), + }; + + self.cell + .set(view) + .unwrap_or_else(|_| unreachable!("Cell is expected to be empty")); + + Ok(self + .cell + .get() + .unwrap_or_else(|| unreachable!("Cell is expected to be initialized"))) + } +} + +impl<'v, T: Unique + Default + Send + Sync> WorldBorrow for UniqueOrInitView<'v, T> { + type WorldView<'a> = UniqueOrInitView<'a, T>; + + fn world_borrow( + world: &World, + last_run: Option, + current: TrackingTimestamp, + ) -> Result, error::GetStorage> { + let all_storages = world + .all_storages() + .map_err(error::GetStorage::AllStoragesBorrow)?; + + let (all_storages, all_borrow) = unsafe { ARef::destructure(all_storages) }; + let cell = OnceCell::new(); + + match UniqueView::borrow(all_storages, Some(all_borrow.clone()), last_run, current) { + Ok(view) => cell + .set(view) + .unwrap_or_else(|_| unreachable!("Cell is expected to be empty")), + Err(error::GetStorage::MissingStorage { .. }) => {} + Err(err) => return Err(err), + }; + + Ok(UniqueOrInitView { + cell, + all_storages, + all_borrow, + last_run, + current, + }) + } +} + +unsafe impl<'v, T: Unique + Default + Send + Sync> BorrowInfo for UniqueOrInitView<'v, T> { + fn borrow_info(info: &mut Vec) { + UniqueView::::borrow_info(info); + } + + fn enable_tracking( + enable_tracking_fn: &mut Vec Result<(), error::GetStorage>>, + ) { + UniqueView::::enable_tracking(enable_tracking_fn); + } +} diff --git a/src/views/unique_or_init_mut.rs b/src/views/unique_or_init_mut.rs new file mode 100644 index 00000000..253177ff --- /dev/null +++ b/src/views/unique_or_init_mut.rs @@ -0,0 +1,196 @@ +use crate::all_storages::AllStorages; +use crate::atomic_refcell::{ARef, SharedBorrow}; +use crate::borrow::{Borrow, BorrowInfo, WorldBorrow}; +use crate::component::Unique; +use crate::error; +use crate::info::TypeInfo; +use crate::tracking::TrackingTimestamp; +use crate::views::UniqueViewMut; +use crate::world::World; +use core::cell::OnceCell; + +/// Exclusive view over a unique component storage. +/// +/// The component can be initialized with this view. +pub struct UniqueOrInitViewMut<'v, T: Unique + Default> { + cell: OnceCell>, + all_storages: &'v AllStorages, + all_borrow: SharedBorrow<'v>, + last_run: Option, + current: TrackingTimestamp, +} + +impl<'v, T: Unique + Default + Send + Sync> UniqueOrInitViewMut<'v, T> { + /// Gets a reference to the inner [`UniqueViewMut`]. + /// + /// Returns `None` if this view doesn't contains the inner [`UniqueViewMut`]. + pub fn get(&self) -> Option<&UniqueViewMut<'v, T>> { + self.cell.get() + } + + /// Adds the unique component to the `World`. + /// + /// Returns `true` if the component was inserted. + /// + /// ### Borrows + /// + /// - `Unique` storage (exclusive) + /// + /// ### Errors + /// + /// - `Unique` storage borrow failed (it can be initialized and borrowed elsewhere). + pub fn get_mut(&mut self) -> Option<&mut UniqueViewMut<'v, T>> { + self.cell.get_mut() + } + + /// Fetches the unique component from the `World`. + /// + /// Returns `true` if the component was fetched. + /// + /// ### Borrows + /// + /// - `Unique` storage (exclusive) + /// + /// ### Errors + /// + /// - `Unique` storage borrow failed (it can be initialized and borrowed elsewhere). + pub fn set(&self, unique: T) -> Result { + if self.cell.get().is_some() { + return Ok(false); + } + + self.all_storages.add_unique(unique); + + self.cell + .set(UniqueViewMut::borrow( + self.all_storages, + Some(self.all_borrow.clone()), + self.last_run, + self.current, + )?) + .unwrap_or_else(|_| unreachable!("Cell is expected to be empty")); + + Ok(true) + } + + /// Fetches the unique component from the `World`. + /// + /// Returns `true` if the component was fetched. + /// + /// ### Borrows + /// + /// - `Unique` storage (exclusive) + /// + /// ### Errors + /// + /// - `Unique` storage borrow failed (it can be initialized and borrowed elsewhere). + pub fn fetch(&self) -> Result { + if self.cell.get().is_some() { + return Ok(false); + } + + self.cell + .set(UniqueViewMut::borrow( + self.all_storages, + Some(self.all_borrow.clone()), + self.last_run, + self.current, + )?) + .unwrap_or_else(|_| unreachable!("Cell is expected to be empty")); + + Ok(true) + } + + /// Gets the unique component from the `World`.\ + /// Adds it to the `World` if not present. + /// + /// ### Borrows + /// + /// - `Unique` storage (exclusive) + /// + /// ### Errors + /// + /// - `Unique` storage borrow failed (it can be initialized and borrowed elsewhere). + pub fn get_or_init( + &self, + f: impl FnOnce() -> T, + ) -> Result<&UniqueViewMut<'v, T>, error::GetStorage> { + if let Some(view) = self.cell.get() { + return Ok(view); + } + + let view = match UniqueViewMut::borrow( + self.all_storages, + Some(self.all_borrow.clone()), + self.last_run, + self.current, + ) { + Ok(view) => view, + Err(error::GetStorage::MissingStorage { .. }) => { + self.all_storages.add_unique(f()); + + UniqueViewMut::borrow( + self.all_storages, + Some(self.all_borrow.clone()), + self.last_run, + self.current, + )? + } + Err(err) => return Err(err), + }; + + self.cell + .set(view) + .unwrap_or_else(|_| unreachable!("Cell is expected to be empty")); + + Ok(self + .cell + .get() + .unwrap_or_else(|| unreachable!("Cell is expected to be initialized"))) + } +} + +impl<'v, T: Unique + Default + Send + Sync> WorldBorrow for UniqueOrInitViewMut<'v, T> { + type WorldView<'a> = UniqueOrInitViewMut<'a, T>; + + fn world_borrow( + world: &World, + last_run: Option, + current: TrackingTimestamp, + ) -> Result, error::GetStorage> { + let all_storages = world + .all_storages() + .map_err(error::GetStorage::AllStoragesBorrow)?; + + let (all_storages, all_borrow) = unsafe { ARef::destructure(all_storages) }; + let cell = OnceCell::new(); + + match UniqueViewMut::borrow(all_storages, Some(all_borrow.clone()), last_run, current) { + Ok(view) => cell + .set(view) + .unwrap_or_else(|_| unreachable!("Cell is expected to be empty")), + Err(error::GetStorage::MissingStorage { .. }) => {} + Err(err) => return Err(err), + }; + + Ok(UniqueOrInitViewMut { + cell, + all_storages, + all_borrow, + last_run, + current, + }) + } +} + +unsafe impl<'v, T: Unique + Default + Send + Sync> BorrowInfo for UniqueOrInitViewMut<'v, T> { + fn borrow_info(info: &mut Vec) { + UniqueViewMut::::borrow_info(info); + } + + fn enable_tracking( + enable_tracking_fn: &mut Vec Result<(), error::GetStorage>>, + ) { + UniqueViewMut::::enable_tracking(enable_tracking_fn); + } +} diff --git a/tests/unique.rs b/tests/unique.rs index 45d3de68..77ce8a88 100644 --- a/tests/unique.rs +++ b/tests/unique.rs @@ -2,6 +2,7 @@ use core::any::type_name; use shipyard::error; use shipyard::*; +#[derive(Default, Debug, PartialEq)] struct USIZE(usize); impl Component for USIZE { type Tracking = track::Untracked; @@ -169,3 +170,44 @@ fn non_send_remove() { .join() .unwrap(); } + +#[test] +fn unique_or_default() { + let world = World::new(); + + world.run(|u: UniqueOrDefaultView| assert_eq!(*u, USIZE(0))); +} + +#[test] +fn unique_or_default_mut() { + let world = World::new(); + + world.run(|mut u: UniqueOrDefaultViewMut| { + u.0 += 1; + assert_eq!(*u, USIZE(1)) + }); +} + +#[test] +fn unique_or_init() { + let world = World::new(); + + world.run(|u: UniqueOrInitView| { + assert_eq!(**u.get_or_init(|| USIZE(10)).unwrap(), USIZE(10)) + }); + + world.run(|u: UniqueOrInitView| assert_eq!(**u.get().unwrap(), USIZE(10))); +} + +#[test] +fn unique_or_init_mut() { + let world = World::new(); + + world.run(|u: UniqueOrInitViewMut| { + assert_eq!(**u.get_or_init(|| USIZE(10)).unwrap(), USIZE(10)) + }); + + world.run(|mut u: UniqueOrInitViewMut| u.get_mut().unwrap().0 += 1); + + world.run(|u: UniqueOrInitViewMut| assert_eq!(**u.get().unwrap(), USIZE(11))); +}