Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_asset/src/handle.rs
9394 views
1
use crate::{
2
meta::MetaTransform, Asset, AssetId, AssetIndex, AssetIndexAllocator, AssetPath,
3
ErasedAssetIndex, UntypedAssetId,
4
};
5
use alloc::sync::Arc;
6
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
7
use core::{
8
any::TypeId,
9
hash::{Hash, Hasher},
10
marker::PhantomData,
11
};
12
use crossbeam_channel::{Receiver, Sender};
13
use disqualified::ShortName;
14
use thiserror::Error;
15
use uuid::Uuid;
16
17
/// Provides [`Handle`] and [`UntypedHandle`] _for a specific asset type_.
18
/// This should _only_ be used for one specific asset type.
19
#[derive(Clone)]
20
pub struct AssetHandleProvider {
21
pub(crate) allocator: Arc<AssetIndexAllocator>,
22
pub(crate) drop_sender: Sender<DropEvent>,
23
pub(crate) drop_receiver: Receiver<DropEvent>,
24
pub(crate) type_id: TypeId,
25
}
26
27
#[derive(Debug)]
28
pub(crate) struct DropEvent {
29
pub(crate) index: ErasedAssetIndex,
30
pub(crate) asset_server_managed: bool,
31
}
32
33
impl AssetHandleProvider {
34
pub(crate) fn new(type_id: TypeId, allocator: Arc<AssetIndexAllocator>) -> Self {
35
let (drop_sender, drop_receiver) = crossbeam_channel::unbounded();
36
Self {
37
type_id,
38
allocator,
39
drop_sender,
40
drop_receiver,
41
}
42
}
43
44
/// Reserves a new strong [`UntypedHandle`] (with a new [`UntypedAssetId`]). The stored [`Asset`] [`TypeId`] in the
45
/// [`UntypedHandle`] will match the [`Asset`] [`TypeId`] assigned to this [`AssetHandleProvider`].
46
pub fn reserve_handle(&self) -> UntypedHandle {
47
let index = self.allocator.reserve();
48
UntypedHandle::Strong(self.get_handle(index, false, None, None))
49
}
50
51
pub(crate) fn get_handle(
52
&self,
53
index: AssetIndex,
54
asset_server_managed: bool,
55
path: Option<AssetPath<'static>>,
56
meta_transform: Option<MetaTransform>,
57
) -> Arc<StrongHandle> {
58
Arc::new(StrongHandle {
59
index,
60
type_id: self.type_id,
61
drop_sender: self.drop_sender.clone(),
62
meta_transform,
63
path,
64
asset_server_managed,
65
})
66
}
67
68
pub(crate) fn reserve_handle_internal(
69
&self,
70
asset_server_managed: bool,
71
path: Option<AssetPath<'static>>,
72
meta_transform: Option<MetaTransform>,
73
) -> Arc<StrongHandle> {
74
let index = self.allocator.reserve();
75
self.get_handle(index, asset_server_managed, path, meta_transform)
76
}
77
}
78
79
/// The internal "strong" [`Asset`] handle storage for [`Handle::Strong`] and [`UntypedHandle::Strong`]. When this is dropped,
80
/// the [`Asset`] will be freed. It also stores some asset metadata for easy access from handles.
81
#[derive(TypePath)]
82
pub struct StrongHandle {
83
pub(crate) index: AssetIndex,
84
pub(crate) type_id: TypeId,
85
pub(crate) asset_server_managed: bool,
86
pub(crate) path: Option<AssetPath<'static>>,
87
/// Modifies asset meta. This is stored on the handle because it is:
88
/// 1. configuration tied to the lifetime of a specific asset load
89
/// 2. configuration that must be repeatable when the asset is hot-reloaded
90
pub(crate) meta_transform: Option<MetaTransform>,
91
pub(crate) drop_sender: Sender<DropEvent>,
92
}
93
94
impl Drop for StrongHandle {
95
fn drop(&mut self) {
96
let _ = self.drop_sender.send(DropEvent {
97
index: ErasedAssetIndex::new(self.index, self.type_id),
98
asset_server_managed: self.asset_server_managed,
99
});
100
}
101
}
102
103
impl core::fmt::Debug for StrongHandle {
104
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
105
f.debug_struct("StrongHandle")
106
.field("index", &self.index)
107
.field("type_id", &self.type_id)
108
.field("asset_server_managed", &self.asset_server_managed)
109
.field("path", &self.path)
110
.field("drop_sender", &self.drop_sender)
111
.finish()
112
}
113
}
114
115
/// A handle to a specific [`Asset`] of type `A`. Handles act as abstract "references" to
116
/// assets, whose data are stored in the [`Assets<A>`](crate::prelude::Assets) resource,
117
/// avoiding the need to store multiple copies of the same data.
118
///
119
/// If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
120
/// alive until the [`Handle`] is dropped. If a [`Handle`] is [`Handle::Uuid`], it does not necessarily reference a live [`Asset`],
121
/// nor will it keep assets alive.
122
///
123
/// Modifying a *handle* will change which existing asset is referenced, but modifying the *asset*
124
/// (by mutating the [`Assets`](crate::prelude::Assets) resource) will change the asset for all handles referencing it.
125
///
126
/// [`Handle`] can be cloned. If a [`Handle::Strong`] is cloned, the referenced [`Asset`] will not be freed until _all_ instances
127
/// of the [`Handle`] are dropped.
128
///
129
/// [`Handle::Strong`], via [`StrongHandle`] also provides access to useful [`Asset`] metadata, such as the [`AssetPath`] (if it exists).
130
#[derive(Reflect)]
131
#[reflect(Default, Debug, Hash, PartialEq, Clone)]
132
pub enum Handle<A: Asset> {
133
/// A "strong" reference to a live (or loading) [`Asset`]. If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
134
/// alive until the [`Handle`] is dropped. Strong handles also provide access to additional asset metadata.
135
Strong(Arc<StrongHandle>),
136
/// A reference to an [`Asset`] using a stable-across-runs / const identifier. Dropping this
137
/// handle will not result in the asset being dropped.
138
Uuid(Uuid, #[reflect(ignore, clone)] PhantomData<fn() -> A>),
139
}
140
141
impl<T: Asset> Clone for Handle<T> {
142
fn clone(&self) -> Self {
143
match self {
144
Handle::Strong(handle) => Handle::Strong(handle.clone()),
145
Handle::Uuid(uuid, ..) => Handle::Uuid(*uuid, PhantomData),
146
}
147
}
148
}
149
150
impl<A: Asset> Handle<A> {
151
/// Returns the [`AssetId`] of this [`Asset`].
152
#[inline]
153
pub fn id(&self) -> AssetId<A> {
154
match self {
155
Handle::Strong(handle) => AssetId::Index {
156
index: handle.index,
157
marker: PhantomData,
158
},
159
Handle::Uuid(uuid, ..) => AssetId::Uuid { uuid: *uuid },
160
}
161
}
162
163
/// Returns the path if this is (1) a strong handle and (2) the asset has a path
164
#[inline]
165
pub fn path(&self) -> Option<&AssetPath<'static>> {
166
match self {
167
Handle::Strong(handle) => handle.path.as_ref(),
168
Handle::Uuid(..) => None,
169
}
170
}
171
172
/// Returns `true` if this is a uuid handle.
173
#[inline]
174
pub fn is_uuid(&self) -> bool {
175
matches!(self, Handle::Uuid(..))
176
}
177
178
/// Returns `true` if this is a strong handle.
179
#[inline]
180
pub fn is_strong(&self) -> bool {
181
matches!(self, Handle::Strong(_))
182
}
183
184
/// Converts this [`Handle`] to an "untyped" / "generic-less" [`UntypedHandle`], which stores the [`Asset`] type information
185
/// _inside_ [`UntypedHandle`]. This will return [`UntypedHandle::Strong`] for [`Handle::Strong`] and [`UntypedHandle::Uuid`] for
186
/// [`Handle::Uuid`].
187
#[inline]
188
pub fn untyped(self) -> UntypedHandle {
189
self.into()
190
}
191
}
192
193
impl<A: Asset> Default for Handle<A> {
194
fn default() -> Self {
195
Handle::Uuid(AssetId::<A>::DEFAULT_UUID, PhantomData)
196
}
197
}
198
199
impl<A: Asset> core::fmt::Debug for Handle<A> {
200
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
201
let name = ShortName::of::<A>();
202
match self {
203
Handle::Strong(handle) => {
204
write!(
205
f,
206
"StrongHandle<{name}>{{ index: {:?}, type_id: {:?}, path: {:?} }}",
207
handle.index, handle.type_id, handle.path
208
)
209
}
210
Handle::Uuid(uuid, ..) => write!(f, "UuidHandle<{name}>({uuid:?})"),
211
}
212
}
213
}
214
215
impl<A: Asset> Hash for Handle<A> {
216
#[inline]
217
fn hash<H: Hasher>(&self, state: &mut H) {
218
self.id().hash(state);
219
}
220
}
221
222
impl<A: Asset> PartialOrd for Handle<A> {
223
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
224
Some(self.cmp(other))
225
}
226
}
227
228
impl<A: Asset> Ord for Handle<A> {
229
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
230
self.id().cmp(&other.id())
231
}
232
}
233
234
impl<A: Asset> PartialEq for Handle<A> {
235
#[inline]
236
fn eq(&self, other: &Self) -> bool {
237
self.id() == other.id()
238
}
239
}
240
241
impl<A: Asset> Eq for Handle<A> {}
242
243
impl<A: Asset> From<&Handle<A>> for AssetId<A> {
244
#[inline]
245
fn from(value: &Handle<A>) -> Self {
246
value.id()
247
}
248
}
249
250
impl<A: Asset> From<&Handle<A>> for UntypedAssetId {
251
#[inline]
252
fn from(value: &Handle<A>) -> Self {
253
value.id().into()
254
}
255
}
256
257
impl<A: Asset> From<&mut Handle<A>> for AssetId<A> {
258
#[inline]
259
fn from(value: &mut Handle<A>) -> Self {
260
value.id()
261
}
262
}
263
264
impl<A: Asset> From<&mut Handle<A>> for UntypedAssetId {
265
#[inline]
266
fn from(value: &mut Handle<A>) -> Self {
267
value.id().into()
268
}
269
}
270
271
impl<A: Asset> From<Uuid> for Handle<A> {
272
#[inline]
273
fn from(uuid: Uuid) -> Self {
274
Handle::Uuid(uuid, PhantomData)
275
}
276
}
277
278
/// An untyped variant of [`Handle`], which internally stores the [`Asset`] type information at runtime
279
/// as a [`TypeId`] instead of encoding it in the compile-time type. This allows handles across [`Asset`] types
280
/// to be stored together and compared.
281
///
282
/// See [`Handle`] for more information.
283
#[derive(Clone, Reflect)]
284
pub enum UntypedHandle {
285
/// A strong handle, which will keep the referenced [`Asset`] alive until all strong handles are dropped.
286
Strong(Arc<StrongHandle>),
287
/// A UUID handle, which does not keep the referenced [`Asset`] alive.
288
Uuid {
289
/// An identifier that records the underlying asset type.
290
type_id: TypeId,
291
/// The UUID provided during asset registration.
292
uuid: Uuid,
293
},
294
}
295
296
impl UntypedHandle {
297
/// Returns the [`UntypedAssetId`] for the referenced asset.
298
#[inline]
299
pub fn id(&self) -> UntypedAssetId {
300
match self {
301
UntypedHandle::Strong(handle) => UntypedAssetId::Index {
302
type_id: handle.type_id,
303
index: handle.index,
304
},
305
UntypedHandle::Uuid { type_id, uuid } => UntypedAssetId::Uuid {
306
uuid: *uuid,
307
type_id: *type_id,
308
},
309
}
310
}
311
312
/// Returns the path if this is (1) a strong handle and (2) the asset has a path
313
#[inline]
314
pub fn path(&self) -> Option<&AssetPath<'static>> {
315
match self {
316
UntypedHandle::Strong(handle) => handle.path.as_ref(),
317
UntypedHandle::Uuid { .. } => None,
318
}
319
}
320
321
/// Returns the [`TypeId`] of the referenced [`Asset`].
322
#[inline]
323
pub fn type_id(&self) -> TypeId {
324
match self {
325
UntypedHandle::Strong(handle) => handle.type_id,
326
UntypedHandle::Uuid { type_id, .. } => *type_id,
327
}
328
}
329
330
/// Converts to a typed Handle. This _will not check if the target Handle type matches_.
331
#[inline]
332
pub fn typed_unchecked<A: Asset>(self) -> Handle<A> {
333
match self {
334
UntypedHandle::Strong(handle) => Handle::Strong(handle),
335
UntypedHandle::Uuid { uuid, .. } => Handle::Uuid(uuid, PhantomData),
336
}
337
}
338
339
/// Converts to a typed Handle. This will check the type when compiled with debug asserts, but it
340
/// _will not check if the target Handle type matches in release builds_. Use this as an optimization
341
/// when you want some degree of validation at dev-time, but you are also very certain that the type
342
/// actually matches.
343
#[inline]
344
pub fn typed_debug_checked<A: Asset>(self) -> Handle<A> {
345
debug_assert_eq!(
346
self.type_id(),
347
TypeId::of::<A>(),
348
"The target Handle<A>'s TypeId does not match the TypeId of this UntypedHandle"
349
);
350
self.typed_unchecked()
351
}
352
353
/// Converts to a typed Handle. This will panic if the internal [`TypeId`] does not match the given asset type `A`
354
#[inline]
355
pub fn typed<A: Asset>(self) -> Handle<A> {
356
let Ok(handle) = self.try_typed() else {
357
panic!(
358
"The target Handle<{}>'s TypeId does not match the TypeId of this UntypedHandle",
359
core::any::type_name::<A>()
360
)
361
};
362
363
handle
364
}
365
366
/// Converts to a typed Handle. This will panic if the internal [`TypeId`] does not match the given asset type `A`
367
#[inline]
368
pub fn try_typed<A: Asset>(self) -> Result<Handle<A>, UntypedAssetConversionError> {
369
Handle::try_from(self)
370
}
371
372
/// The "meta transform" for the strong handle. This will only be [`Some`] if the handle is strong and there is a meta transform
373
/// associated with it.
374
#[inline]
375
pub fn meta_transform(&self) -> Option<&MetaTransform> {
376
match self {
377
UntypedHandle::Strong(handle) => handle.meta_transform.as_ref(),
378
UntypedHandle::Uuid { .. } => None,
379
}
380
}
381
}
382
383
impl PartialEq for UntypedHandle {
384
#[inline]
385
fn eq(&self, other: &Self) -> bool {
386
self.id() == other.id() && self.type_id() == other.type_id()
387
}
388
}
389
390
impl Eq for UntypedHandle {}
391
392
impl Hash for UntypedHandle {
393
#[inline]
394
fn hash<H: Hasher>(&self, state: &mut H) {
395
self.id().hash(state);
396
}
397
}
398
399
impl core::fmt::Debug for UntypedHandle {
400
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
401
match self {
402
UntypedHandle::Strong(handle) => {
403
write!(
404
f,
405
"StrongHandle{{ type_id: {:?}, id: {:?}, path: {:?} }}",
406
handle.type_id, handle.index, handle.path
407
)
408
}
409
UntypedHandle::Uuid { type_id, uuid } => {
410
write!(f, "UuidHandle{{ type_id: {type_id:?}, uuid: {uuid:?} }}",)
411
}
412
}
413
}
414
}
415
416
impl PartialOrd for UntypedHandle {
417
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
418
if self.type_id() == other.type_id() {
419
self.id().partial_cmp(&other.id())
420
} else {
421
None
422
}
423
}
424
}
425
426
impl From<&UntypedHandle> for UntypedAssetId {
427
#[inline]
428
fn from(value: &UntypedHandle) -> Self {
429
value.id()
430
}
431
}
432
433
// Cross Operations
434
435
impl<A: Asset> PartialEq<UntypedHandle> for Handle<A> {
436
#[inline]
437
fn eq(&self, other: &UntypedHandle) -> bool {
438
TypeId::of::<A>() == other.type_id() && self.id() == other.id()
439
}
440
}
441
442
impl<A: Asset> PartialEq<Handle<A>> for UntypedHandle {
443
#[inline]
444
fn eq(&self, other: &Handle<A>) -> bool {
445
other.eq(self)
446
}
447
}
448
449
impl<A: Asset> PartialOrd<UntypedHandle> for Handle<A> {
450
#[inline]
451
fn partial_cmp(&self, other: &UntypedHandle) -> Option<core::cmp::Ordering> {
452
if TypeId::of::<A>() != other.type_id() {
453
None
454
} else {
455
self.id().partial_cmp(&other.id())
456
}
457
}
458
}
459
460
impl<A: Asset> PartialOrd<Handle<A>> for UntypedHandle {
461
#[inline]
462
fn partial_cmp(&self, other: &Handle<A>) -> Option<core::cmp::Ordering> {
463
Some(other.partial_cmp(self)?.reverse())
464
}
465
}
466
467
impl<A: Asset> From<Handle<A>> for UntypedHandle {
468
fn from(value: Handle<A>) -> Self {
469
match value {
470
Handle::Strong(handle) => UntypedHandle::Strong(handle),
471
Handle::Uuid(uuid, _) => UntypedHandle::Uuid {
472
type_id: TypeId::of::<A>(),
473
uuid,
474
},
475
}
476
}
477
}
478
479
impl<A: Asset> TryFrom<UntypedHandle> for Handle<A> {
480
type Error = UntypedAssetConversionError;
481
482
fn try_from(value: UntypedHandle) -> Result<Self, Self::Error> {
483
let found = value.type_id();
484
let expected = TypeId::of::<A>();
485
486
if found != expected {
487
return Err(UntypedAssetConversionError::TypeIdMismatch { expected, found });
488
}
489
490
Ok(match value {
491
UntypedHandle::Strong(handle) => Handle::Strong(handle),
492
UntypedHandle::Uuid { uuid, .. } => Handle::Uuid(uuid, PhantomData),
493
})
494
}
495
}
496
497
/// Creates a [`Handle`] from a string literal containing a UUID.
498
///
499
/// # Examples
500
///
501
/// ```
502
/// # use bevy_asset::{Handle, uuid_handle};
503
/// # type Image = ();
504
/// const IMAGE: Handle<Image> = uuid_handle!("1347c9b7-c46a-48e7-b7b8-023a354b7cac");
505
/// ```
506
#[macro_export]
507
macro_rules! uuid_handle {
508
($uuid:expr) => {{
509
$crate::Handle::Uuid($crate::uuid::uuid!($uuid), core::marker::PhantomData)
510
}};
511
}
512
513
#[deprecated = "Use uuid_handle! instead"]
514
#[macro_export]
515
macro_rules! weak_handle {
516
($uuid:expr) => {
517
$crate::uuid_handle!($uuid)
518
};
519
}
520
521
/// Errors preventing the conversion of to/from an [`UntypedHandle`] and a [`Handle`].
522
#[derive(Error, Debug, PartialEq, Clone)]
523
#[non_exhaustive]
524
pub enum UntypedAssetConversionError {
525
/// Caused when trying to convert an [`UntypedHandle`] into a [`Handle`] of the wrong type.
526
#[error(
527
"This UntypedHandle is for {found:?} and cannot be converted into a Handle<{expected:?}>"
528
)]
529
TypeIdMismatch {
530
/// The expected [`TypeId`] of the [`Handle`] being converted to.
531
expected: TypeId,
532
/// The [`TypeId`] of the [`UntypedHandle`] being converted from.
533
found: TypeId,
534
},
535
}
536
537
#[cfg(test)]
538
mod tests {
539
use alloc::boxed::Box;
540
use bevy_platform::hash::FixedHasher;
541
use bevy_reflect::PartialReflect;
542
use core::hash::BuildHasher;
543
use uuid::Uuid;
544
545
use crate::tests::create_app;
546
547
use super::*;
548
549
type TestAsset = ();
550
551
const UUID_1: Uuid = Uuid::from_u128(123);
552
const UUID_2: Uuid = Uuid::from_u128(456);
553
554
/// Simple utility to directly hash a value using a fixed hasher
555
fn hash<T: Hash>(data: &T) -> u64 {
556
FixedHasher.hash_one(data)
557
}
558
559
/// Typed and Untyped `Handles` should be equivalent to each other and themselves
560
#[test]
561
fn equality() {
562
let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
563
let untyped = UntypedHandle::Uuid {
564
type_id: TypeId::of::<TestAsset>(),
565
uuid: UUID_1,
566
};
567
568
assert_eq!(
569
Ok(typed.clone()),
570
Handle::<TestAsset>::try_from(untyped.clone())
571
);
572
assert_eq!(UntypedHandle::from(typed.clone()), untyped);
573
assert_eq!(typed, untyped);
574
}
575
576
/// Typed and Untyped `Handles` should be orderable amongst each other and themselves
577
#[test]
578
#[expect(
579
clippy::cmp_owned,
580
reason = "This lints on the assertion that a typed handle converted to an untyped handle maintains its ordering compared to an untyped handle. While the conversion would normally be useless, we need to ensure that converted handles maintain their ordering, making the conversion necessary here."
581
)]
582
fn ordering() {
583
assert!(UUID_1 < UUID_2);
584
585
let typed_1 = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
586
let typed_2 = Handle::<TestAsset>::Uuid(UUID_2, PhantomData);
587
let untyped_1 = UntypedHandle::Uuid {
588
type_id: TypeId::of::<TestAsset>(),
589
uuid: UUID_1,
590
};
591
let untyped_2 = UntypedHandle::Uuid {
592
type_id: TypeId::of::<TestAsset>(),
593
uuid: UUID_2,
594
};
595
596
assert!(typed_1 < typed_2);
597
assert!(untyped_1 < untyped_2);
598
599
assert!(UntypedHandle::from(typed_1.clone()) < untyped_2);
600
assert!(untyped_1 < UntypedHandle::from(typed_2.clone()));
601
602
assert!(Handle::<TestAsset>::try_from(untyped_1.clone()).unwrap() < typed_2);
603
assert!(typed_1 < Handle::<TestAsset>::try_from(untyped_2.clone()).unwrap());
604
605
assert!(typed_1 < untyped_2);
606
assert!(untyped_1 < typed_2);
607
}
608
609
/// Typed and Untyped `Handles` should be equivalently hashable to each other and themselves
610
#[test]
611
fn hashing() {
612
let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
613
let untyped = UntypedHandle::Uuid {
614
type_id: TypeId::of::<TestAsset>(),
615
uuid: UUID_1,
616
};
617
618
assert_eq!(
619
hash(&typed),
620
hash(&Handle::<TestAsset>::try_from(untyped.clone()).unwrap())
621
);
622
assert_eq!(hash(&UntypedHandle::from(typed.clone())), hash(&untyped));
623
assert_eq!(hash(&typed), hash(&untyped));
624
}
625
626
/// Typed and Untyped `Handles` should be interchangeable
627
#[test]
628
fn conversion() {
629
let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
630
let untyped = UntypedHandle::Uuid {
631
type_id: TypeId::of::<TestAsset>(),
632
uuid: UUID_1,
633
};
634
635
assert_eq!(typed, Handle::try_from(untyped.clone()).unwrap());
636
assert_eq!(UntypedHandle::from(typed.clone()), untyped);
637
}
638
639
#[test]
640
fn from_uuid() {
641
let uuid = UUID_1;
642
let handle: Handle<TestAsset> = uuid.into();
643
644
assert!(handle.is_uuid());
645
assert_eq!(handle.id(), AssetId::Uuid { uuid });
646
}
647
648
/// `PartialReflect::reflect_clone`/`PartialReflect::to_dynamic` should increase the strong count of a strong handle
649
#[test]
650
fn strong_handle_reflect_clone() {
651
use crate::{AssetApp, Assets, VisitAssetDependencies};
652
use bevy_reflect::FromReflect;
653
654
#[derive(Reflect)]
655
struct MyAsset {
656
value: u32,
657
}
658
impl Asset for MyAsset {}
659
impl VisitAssetDependencies for MyAsset {
660
fn visit_dependencies(&self, _visit: &mut impl FnMut(UntypedAssetId)) {}
661
}
662
663
let mut app = create_app().0;
664
app.init_asset::<MyAsset>();
665
let mut assets = app.world_mut().resource_mut::<Assets<MyAsset>>();
666
667
let handle: Handle<MyAsset> = assets.add(MyAsset { value: 1 });
668
match &handle {
669
Handle::Strong(strong) => {
670
assert_eq!(
671
Arc::strong_count(strong),
672
1,
673
"Inserting the asset should result in a strong count of 1"
674
);
675
676
let reflected: &dyn Reflect = &handle;
677
let _cloned_handle: Box<dyn Reflect> = reflected.reflect_clone().unwrap();
678
679
assert_eq!(
680
Arc::strong_count(strong),
681
2,
682
"Cloning the handle with reflect should increase the strong count to 2"
683
);
684
685
let dynamic_handle: Box<dyn PartialReflect> = reflected.to_dynamic();
686
687
assert_eq!(
688
Arc::strong_count(strong),
689
3,
690
"Converting the handle to a dynamic should increase the strong count to 3"
691
);
692
693
let from_reflect_handle: Handle<MyAsset> =
694
FromReflect::from_reflect(&*dynamic_handle).unwrap();
695
696
assert_eq!(Arc::strong_count(strong), 4, "Converting the reflected value back to a handle should increase the strong count to 4");
697
assert!(
698
from_reflect_handle.is_strong(),
699
"The cloned handle should still be strong"
700
);
701
}
702
_ => panic!("Expected a strong handle"),
703
}
704
}
705
}
706
707