1use crate::{
2 meta::MetaTransform, Asset, AssetId, AssetIndex, AssetIndexAllocator, AssetPath, AssetServer,
3 ErasedAssetIndex, ReflectHandle, UntypedAssetId,
4};
5use alloc::sync::Arc;
6use bevy_ecs::template::{FromTemplate, SpecializeFromTemplate, Template, TemplateContext};
7use bevy_platform::{collections::Equivalent, sync::Mutex};
8use bevy_reflect::{enums::Enum, FromReflect, PartialReflect, Reflect, ReflectRef, TypePath};
9use core::{
10 any::TypeId,
11 hash::{Hash, Hasher},
12 marker::PhantomData,
13};
14use crossbeam_channel::{Receiver, Sender};
15use disqualified::ShortName;
16use thiserror::Error;
17use uuid::Uuid;
18
19#[derive(Clone)]
22pub struct AssetHandleProvider {
23 pub(crate) allocator: Arc<AssetIndexAllocator>,
24 pub(crate) drop_sender: Sender<DropEvent>,
25 pub(crate) drop_receiver: Receiver<DropEvent>,
26 pub(crate) type_id: TypeId,
27}
28
29#[derive(Debug)]
30pub(crate) struct DropEvent {
31 pub(crate) index: ErasedAssetIndex,
32 pub(crate) asset_server_managed: bool,
33}
34
35impl AssetHandleProvider {
36 pub(crate) fn new(type_id: TypeId, allocator: Arc<AssetIndexAllocator>) -> Self {
37 let (drop_sender, drop_receiver) = crossbeam_channel::unbounded();
38 Self {
39 type_id,
40 allocator,
41 drop_sender,
42 drop_receiver,
43 }
44 }
45
46 pub fn reserve_handle(&self) -> UntypedHandle {
49 let index = self.allocator.reserve();
50 UntypedHandle::Strong(self.get_handle(index, false, None, None))
51 }
52
53 pub(crate) fn get_handle(
54 &self,
55 index: AssetIndex,
56 asset_server_managed: bool,
57 path: Option<AssetPath<'static>>,
58 meta_transform: Option<MetaTransform>,
59 ) -> Arc<StrongHandle> {
60 Arc::new(StrongHandle {
61 index,
62 type_id: self.type_id,
63 drop_sender: self.drop_sender.clone(),
64 meta_transform,
65 path,
66 asset_server_managed,
67 })
68 }
69
70 pub(crate) fn reserve_handle_internal(
71 &self,
72 asset_server_managed: bool,
73 path: Option<AssetPath<'static>>,
74 meta_transform: Option<MetaTransform>,
75 ) -> Arc<StrongHandle> {
76 let index = self.allocator.reserve();
77 self.get_handle(index, asset_server_managed, path, meta_transform)
78 }
79}
80
81#[derive(TypePath)]
84pub struct StrongHandle {
85 pub(crate) index: AssetIndex,
86 pub(crate) type_id: TypeId,
87 pub(crate) asset_server_managed: bool,
88 pub(crate) path: Option<AssetPath<'static>>,
89 pub(crate) meta_transform: Option<MetaTransform>,
93 pub(crate) drop_sender: Sender<DropEvent>,
94}
95
96impl Drop for StrongHandle {
97 fn drop(&mut self) {
98 let _ = self.drop_sender.send(DropEvent {
99 index: ErasedAssetIndex::new(self.index, self.type_id),
100 asset_server_managed: self.asset_server_managed,
101 });
102 }
103}
104
105impl core::fmt::Debug for StrongHandle {
106 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
107 f.debug_struct("StrongHandle")
108 .field("index", &self.index)
109 .field("type_id", &self.type_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#[derive(Reflect)]
133#[reflect(Debug, Hash, PartialEq, Clone, Handle, from_reflect = false)]
134pub enum Handle<A: Asset> {
135 Strong(Arc<StrongHandle>),
138 Uuid(Uuid, #[reflect(ignore, clone)] PhantomData<fn() -> A>),
141}
142
143impl<A: Asset> FromReflect for Handle<A>
148where
149 Handle<A>: Send + Sync,
150 A: TypePath,
151{
152 fn from_reflect(reflect_value: &dyn PartialReflect) -> Option<Self> {
153 let ReflectRef::Enum(enum_value) = PartialReflect::reflect_ref(reflect_value) else {
154 return None;
155 };
156
157 match Enum::variant_name(enum_value) {
158 "Strong" => {
159 let strong_field = enum_value.field_at(0usize)?;
160 let strong_handle = Arc::<StrongHandle>::from_reflect(strong_field)?;
161
162 if strong_handle.type_id != TypeId::of::<A>() {
164 return None;
165 }
166
167 Some(Handle::Strong(strong_handle))
168 }
169 "Uuid" => {
170 let uuid_field = enum_value.field_at(0usize)?;
171 let uuid = Uuid::from_reflect(uuid_field)?;
172
173 Some(Handle::Uuid(uuid, Default::default()))
174 }
175 _ => None,
176 }
177 }
178}
179
180impl<T: Asset> Clone for Handle<T> {
181 fn clone(&self) -> Self {
182 match self {
183 Handle::Strong(handle) => Handle::Strong(handle.clone()),
184 Handle::Uuid(uuid, ..) => Handle::Uuid(*uuid, PhantomData),
185 }
186 }
187}
188
189impl<A: Asset> Handle<A> {
190 #[inline]
192 pub fn id(&self) -> AssetId<A> {
193 match self {
194 Handle::Strong(handle) => AssetId::Index {
195 index: handle.index,
196 marker: PhantomData,
197 },
198 Handle::Uuid(uuid, ..) => AssetId::Uuid { uuid: *uuid },
199 }
200 }
201
202 #[inline]
204 pub fn path(&self) -> Option<&AssetPath<'static>> {
205 match self {
206 Handle::Strong(handle) => handle.path.as_ref(),
207 Handle::Uuid(..) => None,
208 }
209 }
210
211 #[inline]
213 pub fn is_uuid(&self) -> bool {
214 matches!(self, Handle::Uuid(..))
215 }
216
217 #[inline]
219 pub fn is_strong(&self) -> bool {
220 matches!(self, Handle::Strong(_))
221 }
222
223 #[inline]
227 pub fn untyped(self) -> UntypedHandle {
228 self.into()
229 }
230}
231
232impl<A: Asset> Default for Handle<A> {
233 fn default() -> Self {
234 Handle::Uuid(AssetId::<A>::DEFAULT_UUID, PhantomData)
235 }
236}
237
238impl<T: Asset> Unpin for Handle<T> where for<'a> [()]: SpecializeFromTemplate {}
243
244impl<T: Asset> FromTemplate for Handle<T> {
245 type Template = HandleTemplate<T>;
246}
247
248#[derive(Reflect)]
273pub enum HandleTemplate<T: Asset> {
274 Path(AssetPath<'static>),
276 Handle(Handle<T>),
278 Value(ArcMutexValue<T>),
283}
284
285impl<T: Asset> HandleTemplate<T> {
286 pub fn value(value: impl Into<T>) -> Self {
292 HandleTemplate::Value(ArcMutexValue(Arc::new(Mutex::new(AssetOrHandle::Value(
293 Some(value.into()),
294 )))))
295 }
296}
297
298#[derive(Reflect)]
302#[reflect(opaque)]
303pub struct ArcMutexValue<T: Asset>(Arc<Mutex<AssetOrHandle<T>>>);
304
305impl<T: Asset> Clone for ArcMutexValue<T> {
306 fn clone(&self) -> Self {
307 Self(self.0.clone())
308 }
309}
310
311#[derive(Reflect)]
312enum AssetOrHandle<T: Asset> {
313 Value(Option<T>),
314 Handle(Handle<T>),
315}
316
317impl<T: Asset> Default for AssetOrHandle<T> {
318 fn default() -> Self {
319 Self::Handle(Default::default())
320 }
321}
322
323impl<T: Asset> Default for HandleTemplate<T> {
324 fn default() -> Self {
325 Self::Handle(Default::default())
326 }
327}
328
329impl<I: Into<AssetPath<'static>>, T: Asset> From<I> for HandleTemplate<T> {
330 fn from(value: I) -> Self {
331 Self::Path(value.into())
332 }
333}
334
335impl<T: Asset> From<Handle<T>> for HandleTemplate<T> {
336 fn from(value: Handle<T>) -> Self {
337 Self::Handle(value)
338 }
339}
340
341impl<T: Asset> Template for HandleTemplate<T> {
342 type Output = Handle<T>;
343 fn build_template(&self, context: &mut TemplateContext) -> bevy_ecs::error::Result<Handle<T>> {
344 Ok(match self {
345 HandleTemplate::Path(asset_path) => context.resource::<AssetServer>().load(asset_path),
346 HandleTemplate::Handle(handle) => handle.clone(),
347 HandleTemplate::Value(value) => {
348 let mut value_or_handle = value.0.lock().unwrap();
351 match &mut *value_or_handle {
352 AssetOrHandle::Value(value) => {
353 let handle = context.resource::<AssetServer>().add(value.take().unwrap());
356 *value_or_handle = AssetOrHandle::Handle(handle.clone());
357 handle
358 }
359 AssetOrHandle::Handle(handle) => handle.clone(),
360 }
361 }
362 })
363 }
364
365 fn clone_template(&self) -> Self {
366 match self {
367 HandleTemplate::Path(asset_path) => HandleTemplate::Path(asset_path.clone()),
368 HandleTemplate::Handle(handle) => HandleTemplate::Handle(handle.clone()),
369 HandleTemplate::Value(value) => HandleTemplate::Value(value.clone()),
370 }
371 }
372}
373
374pub fn asset_value<I: Into<A>, A: Asset>(asset: I) -> HandleTemplate<A> {
380 HandleTemplate::value(asset)
381}
382
383impl<A: Asset> core::fmt::Debug for Handle<A> {
384 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
385 let name = ShortName::of::<A>();
386 match self {
387 Handle::Strong(handle) => {
388 write!(
389 f,
390 "StrongHandle<{name}>{{ index: {:?}, type_id: {:?}, path: {:?} }}",
391 handle.index, handle.type_id, handle.path
392 )
393 }
394 Handle::Uuid(uuid, ..) => write!(f, "UuidHandle<{name}>({uuid:?})"),
395 }
396 }
397}
398
399impl<A: Asset> Hash for Handle<A> {
400 #[inline]
401 fn hash<H: Hasher>(&self, state: &mut H) {
402 self.id().hash(state);
403 }
404}
405
406impl<T: Asset> Equivalent<Handle<T>> for AssetId<T> {
408 fn equivalent(&self, key: &Handle<T>) -> bool {
409 *self == key.id()
410 }
411}
412
413impl<A: Asset> PartialOrd for Handle<A> {
414 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
415 Some(self.cmp(other))
416 }
417}
418
419impl<A: Asset> Ord for Handle<A> {
420 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
421 self.id().cmp(&other.id())
422 }
423}
424
425impl<A: Asset> PartialEq for Handle<A> {
426 #[inline]
427 fn eq(&self, other: &Self) -> bool {
428 self.id() == other.id()
429 }
430}
431
432impl<A: Asset> Eq for Handle<A> {}
433
434impl<A: Asset> From<&Handle<A>> for AssetId<A> {
435 #[inline]
436 fn from(value: &Handle<A>) -> Self {
437 value.id()
438 }
439}
440
441impl<A: Asset> From<&Handle<A>> for UntypedAssetId {
442 #[inline]
443 fn from(value: &Handle<A>) -> Self {
444 value.id().into()
445 }
446}
447
448impl<A: Asset> From<&mut Handle<A>> for AssetId<A> {
449 #[inline]
450 fn from(value: &mut Handle<A>) -> Self {
451 value.id()
452 }
453}
454
455impl<A: Asset> From<&mut Handle<A>> for UntypedAssetId {
456 #[inline]
457 fn from(value: &mut Handle<A>) -> Self {
458 value.id().into()
459 }
460}
461
462impl<A: Asset> From<Uuid> for Handle<A> {
463 #[inline]
464 fn from(uuid: Uuid) -> Self {
465 Handle::Uuid(uuid, PhantomData)
466 }
467}
468
469#[derive(Clone, Reflect)]
475pub enum UntypedHandle {
476 Strong(Arc<StrongHandle>),
478 Uuid {
480 type_id: TypeId,
482 uuid: Uuid,
484 },
485}
486
487impl UntypedHandle {
488 pub fn default_for_type(type_id: TypeId) -> Self {
490 Self::Uuid {
491 type_id,
492 uuid: AssetId::<()>::DEFAULT_UUID,
493 }
494 }
495
496 #[inline]
498 pub fn id(&self) -> UntypedAssetId {
499 match self {
500 UntypedHandle::Strong(handle) => UntypedAssetId::Index {
501 type_id: handle.type_id,
502 index: handle.index,
503 },
504 UntypedHandle::Uuid { type_id, uuid } => UntypedAssetId::Uuid {
505 uuid: *uuid,
506 type_id: *type_id,
507 },
508 }
509 }
510
511 #[inline]
513 pub fn path(&self) -> Option<&AssetPath<'static>> {
514 match self {
515 UntypedHandle::Strong(handle) => handle.path.as_ref(),
516 UntypedHandle::Uuid { .. } => None,
517 }
518 }
519
520 #[inline]
522 pub fn type_id(&self) -> TypeId {
523 match self {
524 UntypedHandle::Strong(handle) => handle.type_id,
525 UntypedHandle::Uuid { type_id, .. } => *type_id,
526 }
527 }
528
529 #[inline]
531 pub fn typed_unchecked<A: Asset>(self) -> Handle<A> {
532 match self {
533 UntypedHandle::Strong(handle) => Handle::Strong(handle),
534 UntypedHandle::Uuid { uuid, .. } => Handle::Uuid(uuid, PhantomData),
535 }
536 }
537
538 #[inline]
543 pub fn typed_debug_checked<A: Asset>(self) -> Handle<A> {
544 debug_assert_eq!(
545 self.type_id(),
546 TypeId::of::<A>(),
547 "The target Handle<A>'s TypeId does not match the TypeId of this UntypedHandle"
548 );
549 self.typed_unchecked()
550 }
551
552 #[inline]
554 pub fn typed<A: Asset>(self) -> Handle<A> {
555 let Ok(handle) = self.try_typed() else {
556 panic!(
557 "The target Handle<{}>'s TypeId does not match the TypeId of this UntypedHandle",
558 core::any::type_name::<A>()
559 )
560 };
561
562 handle
563 }
564
565 #[inline]
567 pub fn try_typed<A: Asset>(self) -> Result<Handle<A>, UntypedAssetConversionError> {
568 Handle::try_from(self)
569 }
570
571 #[inline]
574 pub fn meta_transform(&self) -> Option<&MetaTransform> {
575 match self {
576 UntypedHandle::Strong(handle) => handle.meta_transform.as_ref(),
577 UntypedHandle::Uuid { .. } => None,
578 }
579 }
580}
581
582impl PartialEq for UntypedHandle {
583 #[inline]
584 fn eq(&self, other: &Self) -> bool {
585 self.id() == other.id() && self.type_id() == other.type_id()
586 }
587}
588
589impl Eq for UntypedHandle {}
590
591impl Hash for UntypedHandle {
592 #[inline]
593 fn hash<H: Hasher>(&self, state: &mut H) {
594 self.id().hash(state);
595 }
596}
597
598impl core::fmt::Debug for UntypedHandle {
599 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
600 match self {
601 UntypedHandle::Strong(handle) => {
602 write!(
603 f,
604 "StrongHandle{{ type_id: {:?}, id: {:?}, path: {:?} }}",
605 handle.type_id, handle.index, handle.path
606 )
607 }
608 UntypedHandle::Uuid { type_id, uuid } => {
609 write!(f, "UuidHandle{{ type_id: {type_id:?}, uuid: {uuid:?} }}",)
610 }
611 }
612 }
613}
614
615impl PartialOrd for UntypedHandle {
616 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
617 if self.type_id() == other.type_id() {
618 self.id().partial_cmp(&other.id())
619 } else {
620 None
621 }
622 }
623}
624
625impl From<&UntypedHandle> for UntypedAssetId {
626 #[inline]
627 fn from(value: &UntypedHandle) -> Self {
628 value.id()
629 }
630}
631
632impl<A: Asset> PartialEq<UntypedHandle> for Handle<A> {
635 #[inline]
636 fn eq(&self, other: &UntypedHandle) -> bool {
637 TypeId::of::<A>() == other.type_id() && self.id() == other.id()
638 }
639}
640
641impl<A: Asset> PartialEq<Handle<A>> for UntypedHandle {
642 #[inline]
643 fn eq(&self, other: &Handle<A>) -> bool {
644 other.eq(self)
645 }
646}
647
648impl<A: Asset> PartialOrd<UntypedHandle> for Handle<A> {
649 #[inline]
650 fn partial_cmp(&self, other: &UntypedHandle) -> Option<core::cmp::Ordering> {
651 if TypeId::of::<A>() != other.type_id() {
652 None
653 } else {
654 self.id().partial_cmp(&other.id())
655 }
656 }
657}
658
659impl<A: Asset> PartialOrd<Handle<A>> for UntypedHandle {
660 #[inline]
661 fn partial_cmp(&self, other: &Handle<A>) -> Option<core::cmp::Ordering> {
662 Some(other.partial_cmp(self)?.reverse())
663 }
664}
665
666impl<A: Asset> From<Handle<A>> for UntypedHandle {
667 fn from(value: Handle<A>) -> Self {
668 match value {
669 Handle::Strong(handle) => UntypedHandle::Strong(handle),
670 Handle::Uuid(uuid, _) => UntypedHandle::Uuid {
671 type_id: TypeId::of::<A>(),
672 uuid,
673 },
674 }
675 }
676}
677
678impl<A: Asset> TryFrom<UntypedHandle> for Handle<A> {
679 type Error = UntypedAssetConversionError;
680
681 fn try_from(value: UntypedHandle) -> Result<Self, Self::Error> {
682 let found = value.type_id();
683 let expected = TypeId::of::<A>();
684
685 if found != expected {
686 return Err(UntypedAssetConversionError::TypeIdMismatch { expected, found });
687 }
688
689 Ok(match value {
690 UntypedHandle::Strong(handle) => Handle::Strong(handle),
691 UntypedHandle::Uuid { uuid, .. } => Handle::Uuid(uuid, PhantomData),
692 })
693 }
694}
695
696#[macro_export]
706macro_rules! uuid_handle {
707 ($uuid:expr) => {{
708 $crate::Handle::Uuid($crate::uuid::uuid!($uuid), core::marker::PhantomData)
709 }};
710}
711
712#[deprecated = "Use uuid_handle! instead"]
713#[macro_export]
714macro_rules! weak_handle {
715 ($uuid:expr) => {
716 $crate::uuid_handle!($uuid)
717 };
718}
719
720#[derive(Error, Debug, PartialEq, Clone)]
722#[non_exhaustive]
723pub enum UntypedAssetConversionError {
724 #[error(
726 "This UntypedHandle is for {found:?} and cannot be converted into a Handle<{expected:?}>"
727 )]
728 TypeIdMismatch {
729 expected: TypeId,
731 found: TypeId,
733 },
734}
735
736#[cfg(test)]
737mod tests {
738 use alloc::boxed::Box;
739 use bevy_platform::hash::FixedHasher;
740 use bevy_reflect::PartialReflect;
741 use core::hash::BuildHasher;
742 use uuid::Uuid;
743
744 use crate::tests::create_app;
745
746 use super::*;
747
748 type TestAsset = ();
749
750 const UUID_1: Uuid = Uuid::from_u128(123);
751 const UUID_2: Uuid = Uuid::from_u128(456);
752
753 fn hash<T: Hash>(data: &T) -> u64 {
755 FixedHasher.hash_one(data)
756 }
757
758 #[test]
760 fn equality() {
761 let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
762 let untyped = UntypedHandle::Uuid {
763 type_id: TypeId::of::<TestAsset>(),
764 uuid: UUID_1,
765 };
766
767 assert_eq!(
768 Ok(typed.clone()),
769 Handle::<TestAsset>::try_from(untyped.clone())
770 );
771 assert_eq!(UntypedHandle::from(typed.clone()), untyped);
772 assert_eq!(typed, untyped);
773 }
774
775 #[test]
777 #[expect(
778 clippy::cmp_owned,
779 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."
780 )]
781 fn ordering() {
782 assert!(UUID_1 < UUID_2);
783
784 let typed_1 = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
785 let typed_2 = Handle::<TestAsset>::Uuid(UUID_2, PhantomData);
786 let untyped_1 = UntypedHandle::Uuid {
787 type_id: TypeId::of::<TestAsset>(),
788 uuid: UUID_1,
789 };
790 let untyped_2 = UntypedHandle::Uuid {
791 type_id: TypeId::of::<TestAsset>(),
792 uuid: UUID_2,
793 };
794
795 assert!(typed_1 < typed_2);
796 assert!(untyped_1 < untyped_2);
797
798 assert!(UntypedHandle::from(typed_1.clone()) < untyped_2);
799 assert!(untyped_1 < UntypedHandle::from(typed_2.clone()));
800
801 assert!(Handle::<TestAsset>::try_from(untyped_1.clone()).unwrap() < typed_2);
802 assert!(typed_1 < Handle::<TestAsset>::try_from(untyped_2.clone()).unwrap());
803
804 assert!(typed_1 < untyped_2);
805 assert!(untyped_1 < typed_2);
806 }
807
808 #[test]
810 fn hashing() {
811 let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
812 let untyped = UntypedHandle::Uuid {
813 type_id: TypeId::of::<TestAsset>(),
814 uuid: UUID_1,
815 };
816
817 assert_eq!(
818 hash(&typed),
819 hash(&Handle::<TestAsset>::try_from(untyped.clone()).unwrap())
820 );
821 assert_eq!(hash(&UntypedHandle::from(typed.clone())), hash(&untyped));
822 assert_eq!(hash(&typed), hash(&untyped));
823 }
824
825 #[test]
827 fn conversion() {
828 let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
829 let untyped = UntypedHandle::Uuid {
830 type_id: TypeId::of::<TestAsset>(),
831 uuid: UUID_1,
832 };
833
834 assert_eq!(typed, Handle::try_from(untyped.clone()).unwrap());
835 assert_eq!(UntypedHandle::from(typed.clone()), untyped);
836 }
837
838 #[test]
839 fn from_uuid() {
840 let uuid = UUID_1;
841 let handle: Handle<TestAsset> = uuid.into();
842
843 assert!(handle.is_uuid());
844 assert_eq!(handle.id(), AssetId::Uuid { uuid });
845 }
846
847 #[test]
849 fn strong_handle_reflect_clone() {
850 use crate::{AssetApp, Assets, VisitAssetDependencies};
851 use bevy_reflect::FromReflect;
852
853 #[derive(Reflect)]
854 struct MyAsset {
855 value: u32,
856 }
857 impl Asset for MyAsset {}
858 impl VisitAssetDependencies for MyAsset {
859 fn visit_dependencies(&self, _visit: &mut impl FnMut(UntypedAssetId)) {}
860 }
861
862 let mut app = create_app().0;
863 app.init_asset::<MyAsset>();
864 let mut assets = app.world_mut().resource_mut::<Assets<MyAsset>>();
865
866 let handle: Handle<MyAsset> = assets.add(MyAsset { value: 1 });
867 match &handle {
868 Handle::Strong(strong) => {
869 assert_eq!(
870 Arc::strong_count(strong),
871 1,
872 "Inserting the asset should result in a strong count of 1"
873 );
874
875 let reflected: &dyn Reflect = &handle;
876 let _cloned_handle: Box<dyn Reflect> = reflected.reflect_clone().unwrap();
877
878 assert_eq!(
879 Arc::strong_count(strong),
880 2,
881 "Cloning the handle with reflect should increase the strong count to 2"
882 );
883
884 let dynamic_handle: Box<dyn PartialReflect> = reflected.to_dynamic();
885
886 assert_eq!(
887 Arc::strong_count(strong),
888 3,
889 "Converting the handle to a dynamic should increase the strong count to 3"
890 );
891
892 let from_reflect_handle: Handle<MyAsset> =
893 FromReflect::from_reflect(&*dynamic_handle).unwrap();
894
895 assert_eq!(Arc::strong_count(strong), 4, "Converting the reflected value back to a handle should increase the strong count to 4");
896 assert!(
897 from_reflect_handle.is_strong(),
898 "The cloned handle should still be strong"
899 );
900 }
901 _ => panic!("Expected a strong handle"),
902 }
903 }
904
905 #[test]
906 fn handle_from_reflect_verifies_type_id() {
907 use crate::{AssetApp, Assets};
908 use bevy_reflect::FromReflect;
909
910 #[derive(Reflect, Asset)]
911 struct A;
912 #[derive(Reflect, Asset)]
913 struct B;
914
915 let mut app = create_app().0;
916 app.init_asset::<A>().init_asset::<B>();
917
918 let mut assets = app.world_mut().resource_mut::<Assets<A>>();
919 let handle_a = assets.add(A);
920
921 let dynamic_handle_a = handle_a.to_dynamic();
922 let reflected_handle_a = handle_a.as_partial_reflect();
923
924 let handle_b_from_reflect_dynamic: Option<Handle<B>> =
925 FromReflect::from_reflect(&*dynamic_handle_a);
926 let handle_b_from_reflect: Option<Handle<B>> =
927 FromReflect::from_reflect(reflected_handle_a);
928 let handle_a_from_reflect: Option<Handle<A>> =
929 FromReflect::from_reflect(reflected_handle_a);
930 assert!(
931 handle_b_from_reflect.is_none(),
932 "Handle<B> should not be constructible from reflected Handle<A>"
933 );
934 assert!(
935 handle_b_from_reflect_dynamic.is_none(),
936 "Handle<B> should not be constructible from dynamic Handle<A>"
937 );
938 assert!(
939 handle_a_from_reflect.is_some(),
940 "Handle<A> should be constructible from reflected Handle<A>"
941 );
942 }
943}