Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/change_detection/maybe_location.rs
7219 views
1
#[cfg(feature = "bevy_reflect")]
2
use bevy_reflect::Reflect;
3
use 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)]
22
pub 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
29
impl<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
39
impl<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
123
impl<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
190
impl<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
205
impl<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
229
impl<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
277
impl 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
}
292
293