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