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