Path: blob/main/crates/bevy_ecs/src/change_detection/maybe_location.rs
7219 views
#[cfg(feature = "bevy_reflect")]1use bevy_reflect::Reflect;2use core::{3marker::PhantomData,4ops::{Deref, DerefMut},5panic::Location,6};78/// A value that contains a `T` if the `track_location` feature is enabled,9/// and is a ZST if it is not.10///11/// The overall API is similar to [`Option`], but whether the value is `Some` or `None` is set at compile12/// time and is the same for all values.13///14/// If the `track_location` feature is disabled, then all functions on this type that return15/// an `MaybeLocation` will have an empty body and should be removed by the optimizer.16///17/// This allows code to be written that will be checked by the compiler even when the feature is disabled,18/// but that will be entirely removed during compilation.19#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]20#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]21pub struct MaybeLocation<T: ?Sized = &'static Location<'static>> {22#[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]23marker: PhantomData<T>,24#[cfg(feature = "track_location")]25value: T,26}2728impl<T: core::fmt::Display> core::fmt::Display for MaybeLocation<T> {29fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {30#[cfg(feature = "track_location")]31{32self.value.fmt(_f)?;33}34Ok(())35}36}3738impl<T> MaybeLocation<T> {39/// Constructs a new `MaybeLocation` that wraps the given value.40///41/// This may only accept `Copy` types,42/// since it needs to drop the value if the `track_location` feature is disabled,43/// and non-`Copy` types cannot be dropped in `const` context.44/// Use [`new_with`][Self::new_with] if you need to construct a non-`Copy` value.45///46/// # See also47/// - [`new_with`][Self::new_with] to initialize using a closure.48/// - [`new_with_flattened`][Self::new_with_flattened] to initialize using a closure that returns an `Option<MaybeLocation<T>>`.49#[inline]50pub const fn new(_value: T) -> Self51where52T: Copy,53{54Self {55#[cfg(feature = "track_location")]56value: _value,57marker: PhantomData,58}59}6061/// Constructs a new `MaybeLocation` that wraps the result of the given closure.62///63/// # See also64/// - [`new`][Self::new] to initialize using a value.65/// - [`new_with_flattened`][Self::new_with_flattened] to initialize using a closure that returns an `Option<MaybeLocation<T>>`.66#[inline]67pub fn new_with(_f: impl FnOnce() -> T) -> Self {68Self {69#[cfg(feature = "track_location")]70value: _f(),71marker: PhantomData,72}73}7475/// Maps an `MaybeLocation<T> `to `MaybeLocation<U>` by applying a function to a contained value.76#[inline]77pub fn map<U>(self, _f: impl FnOnce(T) -> U) -> MaybeLocation<U> {78MaybeLocation {79#[cfg(feature = "track_location")]80value: _f(self.value),81marker: PhantomData,82}83}8485/// Converts a pair of `MaybeLocation` values to an `MaybeLocation` of a tuple.86#[inline]87pub fn zip<U>(self, _other: MaybeLocation<U>) -> MaybeLocation<(T, U)> {88MaybeLocation {89#[cfg(feature = "track_location")]90value: (self.value, _other.value),91marker: PhantomData,92}93}9495/// Returns the contained value or a default.96/// If the `track_location` feature is enabled, this always returns the contained value.97/// If it is disabled, this always returns `T::Default()`.98#[inline]99pub fn unwrap_or_default(self) -> T100where101T: Default,102{103self.into_option().unwrap_or_default()104}105106/// Converts an `MaybeLocation` to an [`Option`] to allow run-time branching.107/// If the `track_location` feature is enabled, this always returns `Some`.108/// If it is disabled, this always returns `None`.109#[inline]110pub fn into_option(self) -> Option<T> {111#[cfg(feature = "track_location")]112{113Some(self.value)114}115#[cfg(not(feature = "track_location"))]116{117None118}119}120}121122impl<T> MaybeLocation<Option<T>> {123/// Constructs a new `MaybeLocation` that wraps the result of the given closure.124/// If the closure returns `Some`, it unwraps the inner value.125///126/// # See also127/// - [`new`][Self::new] to initialize using a value.128/// - [`new_with`][Self::new_with] to initialize using a closure.129#[inline]130pub fn new_with_flattened(_f: impl FnOnce() -> Option<MaybeLocation<T>>) -> Self {131Self {132#[cfg(feature = "track_location")]133value: _f().map(|value| value.value),134marker: PhantomData,135}136}137138/// Transposes a `MaybeLocation` of an [`Option`] into an [`Option`] of a `MaybeLocation`.139///140/// This can be useful if you want to use the `?` operator to exit early141/// if the `track_location` feature is enabled but the value is not found.142///143/// If the `track_location` feature is enabled,144/// this returns `Some` if the inner value is `Some`145/// and `None` if the inner value is `None`.146///147/// If it is disabled, this always returns `Some`.148///149/// # Example150///151/// ```152/// # use bevy_ecs::{change_detection::MaybeLocation, world::World};153/// # use core::panic::Location;154/// #155/// # fn test() -> Option<()> {156/// let mut world = World::new();157/// let entity = world.spawn(()).id();158/// let location: MaybeLocation<Option<&'static Location<'static>>> =159/// world.entities().entity_get_spawned_or_despawned_by(entity);160/// let location: MaybeLocation<&'static Location<'static>> = location.transpose()?;161/// # Some(())162/// # }163/// # test();164/// ```165///166/// # See also167///168/// - [`into_option`][Self::into_option] to convert to an `Option<Option<T>>`.169/// When used with [`Option::flatten`], this will have a similar effect,170/// but will return `None` when the `track_location` feature is disabled.171#[inline]172pub fn transpose(self) -> Option<MaybeLocation<T>> {173#[cfg(feature = "track_location")]174{175self.value.map(|value| MaybeLocation {176value,177marker: PhantomData,178})179}180#[cfg(not(feature = "track_location"))]181{182Some(MaybeLocation {183marker: PhantomData,184})185}186}187}188189impl<T> MaybeLocation<&T> {190/// Maps an `MaybeLocation<&T>` to an `MaybeLocation<T>` by copying the contents.191#[inline]192pub const fn copied(&self) -> MaybeLocation<T>193where194T: Copy,195{196MaybeLocation {197#[cfg(feature = "track_location")]198value: *self.value,199marker: PhantomData,200}201}202}203204impl<T> MaybeLocation<&mut T> {205/// Maps an `MaybeLocation<&mut T>` to an `MaybeLocation<T>` by copying the contents.206#[inline]207pub const fn copied(&self) -> MaybeLocation<T>208where209T: Copy,210{211MaybeLocation {212#[cfg(feature = "track_location")]213value: *self.value,214marker: PhantomData,215}216}217218/// Assigns the contents of an `MaybeLocation<T>` to an `MaybeLocation<&mut T>`.219#[inline]220pub fn assign(&mut self, _value: MaybeLocation<T>) {221#[cfg(feature = "track_location")]222{223*self.value = _value.value;224}225}226}227228impl<T: ?Sized> MaybeLocation<T> {229/// Converts from `&MaybeLocation<T>` to `MaybeLocation<&T>`.230#[inline]231pub const fn as_ref(&self) -> MaybeLocation<&T> {232MaybeLocation {233#[cfg(feature = "track_location")]234value: &self.value,235marker: PhantomData,236}237}238239/// Converts from `&mut MaybeLocation<T>` to `MaybeLocation<&mut T>`.240#[inline]241pub const fn as_mut(&mut self) -> MaybeLocation<&mut T> {242MaybeLocation {243#[cfg(feature = "track_location")]244value: &mut self.value,245marker: PhantomData,246}247}248249/// Converts from `&MaybeLocation<T>` to `MaybeLocation<&T::Target>`.250#[inline]251pub fn as_deref(&self) -> MaybeLocation<&T::Target>252where253T: Deref,254{255MaybeLocation {256#[cfg(feature = "track_location")]257value: &*self.value,258marker: PhantomData,259}260}261262/// Converts from `&mut MaybeLocation<T>` to `MaybeLocation<&mut T::Target>`.263#[inline]264pub fn as_deref_mut(&mut self) -> MaybeLocation<&mut T::Target>265where266T: DerefMut,267{268MaybeLocation {269#[cfg(feature = "track_location")]270value: &mut *self.value,271marker: PhantomData,272}273}274}275276impl MaybeLocation {277/// Returns the source location of the caller of this function. If that function's caller is278/// annotated then its call location will be returned, and so on up the stack to the first call279/// within a non-tracked function body.280#[inline]281#[track_caller]282pub const fn caller() -> Self {283// Note that this cannot use `new_with`, since `FnOnce` invocations cannot be annotated with `#[track_caller]`.284MaybeLocation {285#[cfg(feature = "track_location")]286value: Location::caller(),287marker: PhantomData,288}289}290}291292293