Skip to main content

bevy_asset/
handle.rs

1use crate::{
2    Asset, AssetId, AssetIndex, AssetIndexAllocator, AssetPath, AssetServer, Assets,
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/// Provides [`Handle`] and [`UntypedHandle`] _for a specific asset type_.
20/// This should _only_ be used for one specific asset type.
21#[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    /// Reserves a new strong [`UntypedHandle`] (with a new [`UntypedAssetId`]). The stored [`Asset`] [`TypeId`] in the
47    /// [`UntypedHandle`] will match the [`Asset`] [`TypeId`] assigned to this [`AssetHandleProvider`].
48    pub fn reserve_handle(&self) -> UntypedHandle {
49        let index = self.allocator.reserve();
50        UntypedHandle::Strong(self.get_handle(index, false, 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    ) -> Arc<StrongHandle> {
59        Arc::new(StrongHandle {
60            index,
61            type_id: self.type_id,
62            drop_sender: self.drop_sender.clone(),
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    ) -> Arc<StrongHandle> {
73        let index = self.allocator.reserve();
74        self.get_handle(index, asset_server_managed, path)
75    }
76}
77
78/// The internal "strong" [`Asset`] handle storage for [`Handle::Strong`] and [`UntypedHandle::Strong`]. When this is dropped,
79/// the [`Asset`] will be freed. It also stores some asset metadata for easy access from handles.
80#[derive(TypePath, Debug)]
81pub struct StrongHandle {
82    pub(crate) index: AssetIndex,
83    pub(crate) type_id: TypeId,
84    pub(crate) asset_server_managed: bool,
85    pub(crate) path: Option<AssetPath<'static>>,
86    pub(crate) drop_sender: Sender<DropEvent>,
87}
88
89impl Drop for StrongHandle {
90    fn drop(&mut self) {
91        let _ = self.drop_sender.send(DropEvent {
92            index: ErasedAssetIndex::new(self.index, self.type_id),
93            asset_server_managed: self.asset_server_managed,
94        });
95    }
96}
97
98/// A handle to a specific [`Asset`] of type `A`. Handles act as abstract "references" to
99/// assets, whose data are stored in the [`Assets<A>`] resource,
100/// avoiding the need to store multiple copies of the same data.
101///
102/// If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
103/// alive until the [`Handle`] is dropped. If a [`Handle`] is [`Handle::Uuid`], it does not necessarily reference a live [`Asset`],
104/// nor will it keep assets alive.
105///
106/// Modifying a *handle* will change which existing asset is referenced, but modifying the *asset*
107/// (by mutating the [`Assets`] resource) will change the asset for all handles referencing it.
108///
109/// [`Handle`] can be cloned. If a [`Handle::Strong`] is cloned, the referenced [`Asset`] will not be freed until _all_ instances
110/// of the [`Handle`] are dropped.
111///
112/// [`Handle::Strong`], via [`StrongHandle`] also provides access to useful [`Asset`] metadata, such as the [`AssetPath`] (if it exists).
113#[derive(Reflect)]
114#[reflect(Debug, Hash, PartialEq, Clone, Handle, from_reflect = false)]
115pub enum Handle<A: Asset> {
116    /// A "strong" reference to a live (or loading) [`Asset`]. If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
117    /// alive until the [`Handle`] is dropped. Strong handles also provide access to additional asset metadata.
118    Strong(Arc<StrongHandle>),
119    /// A reference to an [`Asset`] using a stable-across-runs / const identifier. Dropping this
120    /// handle will not result in the asset being dropped.
121    Uuid(Uuid, #[reflect(ignore, clone)] PhantomData<fn() -> A>),
122}
123
124// `Handle` needs a custom `FromReflect` to do extra type checking - see the
125// `strong_handle.type_id` check below.
126impl<A: Asset> FromReflect for Handle<A>
127where
128    Handle<A>: Send + Sync,
129    A: TypePath,
130{
131    fn from_reflect(reflect_value: &dyn PartialReflect) -> Option<Self> {
132        let ReflectRef::Enum(enum_value) = PartialReflect::reflect_ref(reflect_value) else {
133            return None;
134        };
135
136        match Enum::variant_name(enum_value) {
137            "Strong" => {
138                let strong_field = enum_value.field_at(0usize)?;
139                let strong_handle = Arc::<StrongHandle>::from_reflect(strong_field)?;
140
141                // This is necessary as otherwise you could construct Handle<A> via Handle<B>
142                if strong_handle.type_id != TypeId::of::<A>() {
143                    return None;
144                }
145
146                Some(Handle::Strong(strong_handle))
147            }
148            "Uuid" => {
149                let uuid_field = enum_value.field_at(0usize)?;
150                let uuid = Uuid::from_reflect(uuid_field)?;
151
152                Some(Handle::Uuid(uuid, Default::default()))
153            }
154            _ => None,
155        }
156    }
157}
158
159impl<T: Asset> Clone for Handle<T> {
160    fn clone(&self) -> Self {
161        match self {
162            Handle::Strong(handle) => Handle::Strong(handle.clone()),
163            Handle::Uuid(uuid, ..) => Handle::Uuid(*uuid, PhantomData),
164        }
165    }
166}
167
168impl<A: Asset> Handle<A> {
169    /// Returns the [`AssetId`] of this [`Asset`].
170    #[inline]
171    pub fn id(&self) -> AssetId<A> {
172        match self {
173            Handle::Strong(handle) => AssetId::Index {
174                index: handle.index,
175                marker: PhantomData,
176            },
177            Handle::Uuid(uuid, ..) => AssetId::Uuid { uuid: *uuid },
178        }
179    }
180
181    /// Returns the path if this is (1) a strong handle and (2) the asset has a path
182    #[inline]
183    pub fn path(&self) -> Option<&AssetPath<'static>> {
184        match self {
185            Handle::Strong(handle) => handle.path.as_ref(),
186            Handle::Uuid(..) => None,
187        }
188    }
189
190    /// Returns `true` if this is a UUID handle.
191    #[inline]
192    pub fn is_uuid(&self) -> bool {
193        matches!(self, Handle::Uuid(..))
194    }
195
196    /// Returns `true` if this is a strong handle.
197    #[inline]
198    pub fn is_strong(&self) -> bool {
199        matches!(self, Handle::Strong(_))
200    }
201
202    /// Converts this [`Handle`] to an "untyped" / "generic-less" [`UntypedHandle`], which stores the [`Asset`] type information
203    /// _inside_ [`UntypedHandle`]. This will return [`UntypedHandle::Strong`] for [`Handle::Strong`] and [`UntypedHandle::Uuid`] for
204    /// [`Handle::Uuid`].
205    #[inline]
206    pub fn untyped(self) -> UntypedHandle {
207        self.into()
208    }
209}
210
211impl<A: Asset> Default for Handle<A> {
212    fn default() -> Self {
213        Handle::Uuid(AssetId::<A>::DEFAULT_UUID, PhantomData)
214    }
215}
216
217// This enables FromTemplate specialization for `Handle<T>` using the
218// ["auto trait specialization" trick](https://github.com/coolcatcoder/rust_techniques/issues/1)
219// This enables Handle to implement Default _and_ implement FromTemplate, without conflicting with the
220// blanket impl of FromTemplate for T: Default + Clone.
221impl<T: Asset> Unpin for Handle<T> where for<'a> [()]: SpecializeFromTemplate {}
222
223impl<T: Asset> FromTemplate for Handle<T> {
224    type Template = HandleTemplate<T>;
225}
226
227/// A [`Template`] that produces a [`Handle`].
228///
229/// # How asset paths are resolved in templates
230///
231/// When a type with a [`Handle<T>`] field derives [`FromTemplate`], that field is replaced by its
232/// template type, [`HandleTemplate<T>`], when created via BSN.
233/// We can see that [`HandleTemplate<T>`] has the following trait impl block:
234///
235/// ```rust, ignore
236/// impl<I: Into<AssetPath<'static>>, T: Asset> From<I> for HandleTemplate<T> {
237///     fn from(value: I) -> Self {
238///         Self::Path(value.into())
239///     }
240/// }
241/// ```
242///
243/// [`AssetPath<'static>`] implements [`From<&'static str>`].
244/// Because of that, assigning a string literal to a `Handle<T>` field automatically converts it into
245/// [`HandleTemplate<T>::Path`] with that asset path when used in the `bsn!` macro.
246/// Calls to `bsn!` automatically insert `.into()` conversions, and due to Rust's blanket impl that turns [`From`] trait impls into their [`Into`]
247/// equivalents, the conversion from `&'static str` to `AssetPath<'static>` is handled automatically.
248/// Finally, the [`HandleTemplate<T>::Path`] generated gets converted to a [`Handle<T>`] during scene initialization,
249/// as the asset is loaded from the given path, and the resulting handle is assigned to the field,
250/// pointing to the asset that was found at the file path in our original string.
251#[derive(Reflect)]
252pub enum HandleTemplate<T: Asset> {
253    /// Creates a [`Handle`] by calling [`AssetServer::load`] on the given [`AssetPath`].
254    Path(AssetPath<'static>),
255    /// Creates a [`Handle`] by cloning the given [`Handle`] value.
256    Handle(Handle<T>),
257    /// Creates a [`Handle`] by adding the given asset value using [`AssetServer::add`]. This will
258    /// cache the resulting [`Handle`] on the template and reuse it for future template builds.
259    ///
260    /// This should generally be constructed using [`HandleTemplate::value`] or [`asset_value`].
261    Value(ArcMutexValue<T>),
262}
263
264impl<T: Asset> HandleTemplate<T> {
265    /// This will create a new [`HandleTemplate`] for the given `asset` value. This makes it possible
266    /// to define assets "inline" in templates / scenes that produce a [`Handle`].
267    ///
268    /// This supports [`Into`]
269    /// to automatically convert values that can become `A`.
270    pub fn value(value: impl Into<T>) -> Self {
271        HandleTemplate::Value(ArcMutexValue(Arc::new(Mutex::new(AssetOrHandle::Value(
272            Some(value.into()),
273        )))))
274    }
275}
276
277/// Stores an [`Arc<Mutex<AssetOrHandle<T>>>`].
278///
279/// This intermediary type exists largely to enable reflect(opaque).
280#[derive(Reflect)]
281#[reflect(opaque)]
282pub struct ArcMutexValue<T: Asset>(Arc<Mutex<AssetOrHandle<T>>>);
283
284impl<T: Asset> Clone for ArcMutexValue<T> {
285    fn clone(&self) -> Self {
286        Self(self.0.clone())
287    }
288}
289
290#[derive(Reflect)]
291enum AssetOrHandle<T: Asset> {
292    Value(Option<T>),
293    Handle(Handle<T>),
294}
295
296impl<T: Asset> Default for AssetOrHandle<T> {
297    fn default() -> Self {
298        Self::Handle(Default::default())
299    }
300}
301
302impl<T: Asset> Default for HandleTemplate<T> {
303    fn default() -> Self {
304        Self::Handle(Default::default())
305    }
306}
307
308impl<I: Into<AssetPath<'static>>, T: Asset> From<I> for HandleTemplate<T> {
309    fn from(value: I) -> Self {
310        Self::Path(value.into())
311    }
312}
313
314impl<T: Asset> From<Handle<T>> for HandleTemplate<T> {
315    fn from(value: Handle<T>) -> Self {
316        Self::Handle(value)
317    }
318}
319
320impl<T: Asset> Template for HandleTemplate<T> {
321    type Output = Handle<T>;
322    fn build_template(&self, context: &mut TemplateContext) -> bevy_ecs::error::Result<Handle<T>> {
323        Ok(match self {
324            HandleTemplate::Path(asset_path) => context.resource::<AssetServer>().load(asset_path),
325            HandleTemplate::Handle(handle) => handle.clone(),
326            HandleTemplate::Value(value) => {
327                // This unwrap is ok. If another caller panicked while holding this mutex, then the
328                // program is in an invalid state and this should panic too.
329                let mut value_or_handle = value.0.lock().unwrap();
330                match &mut *value_or_handle {
331                    AssetOrHandle::Value(value) => {
332                        // This unwrap is ok because AssetOrHandle::Value will always either contain a Some Value
333                        // when it is in this state (AssetOrHandle is private).
334                        let handle = context
335                            .resource_mut::<Assets<T>>()
336                            .add(value.take().unwrap());
337                        *value_or_handle = AssetOrHandle::Handle(handle.clone());
338                        handle
339                    }
340                    AssetOrHandle::Handle(handle) => handle.clone(),
341                }
342            }
343        })
344    }
345
346    fn clone_template(&self) -> Self {
347        match self {
348            HandleTemplate::Path(asset_path) => HandleTemplate::Path(asset_path.clone()),
349            HandleTemplate::Handle(handle) => HandleTemplate::Handle(handle.clone()),
350            HandleTemplate::Value(value) => HandleTemplate::Value(value.clone()),
351        }
352    }
353}
354
355/// This will create a new [`HandleTemplate`] for the given `asset` value. This makes it possible
356/// to define assets "inline" in templates / scenes that produce a [`Handle`].
357///
358/// This supports [`Into`]
359/// to automatically convert values that can become `A`.
360pub fn asset_value<I: Into<A>, A: Asset>(asset: I) -> HandleTemplate<A> {
361    HandleTemplate::value(asset)
362}
363
364impl<A: Asset> core::fmt::Debug for Handle<A> {
365    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
366        let name = ShortName::of::<A>();
367        match self {
368            Handle::Strong(handle) => {
369                write!(
370                    f,
371                    "StrongHandle<{name}>{{ index: {:?}, type_id: {:?}, path: {:?} }}",
372                    handle.index, handle.type_id, handle.path
373                )
374            }
375            Handle::Uuid(uuid, ..) => write!(f, "UuidHandle<{name}>({uuid:?})"),
376        }
377    }
378}
379
380impl<A: Asset> Hash for Handle<A> {
381    #[inline]
382    fn hash<H: Hasher>(&self, state: &mut H) {
383        self.id().hash(state);
384    }
385}
386
387// Handle uses AssetId when hashing. This enables using AssetId instead of handle with hashsets and hashmaps.
388impl<T: Asset> Equivalent<Handle<T>> for AssetId<T> {
389    fn equivalent(&self, key: &Handle<T>) -> bool {
390        *self == key.id()
391    }
392}
393
394impl<A: Asset> PartialOrd for Handle<A> {
395    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
396        Some(self.cmp(other))
397    }
398}
399
400impl<A: Asset> Ord for Handle<A> {
401    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
402        self.id().cmp(&other.id())
403    }
404}
405
406impl<A: Asset> PartialEq for Handle<A> {
407    #[inline]
408    fn eq(&self, other: &Self) -> bool {
409        self.id() == other.id()
410    }
411}
412
413impl<A: Asset> Eq for Handle<A> {}
414
415impl<A: Asset> From<&Handle<A>> for AssetId<A> {
416    #[inline]
417    fn from(value: &Handle<A>) -> Self {
418        value.id()
419    }
420}
421
422impl<A: Asset> From<&Handle<A>> for UntypedAssetId {
423    #[inline]
424    fn from(value: &Handle<A>) -> Self {
425        value.id().into()
426    }
427}
428
429impl<A: Asset> From<&mut Handle<A>> for AssetId<A> {
430    #[inline]
431    fn from(value: &mut Handle<A>) -> Self {
432        value.id()
433    }
434}
435
436impl<A: Asset> From<&mut Handle<A>> for UntypedAssetId {
437    #[inline]
438    fn from(value: &mut Handle<A>) -> Self {
439        value.id().into()
440    }
441}
442
443impl<A: Asset> From<Uuid> for Handle<A> {
444    #[inline]
445    fn from(uuid: Uuid) -> Self {
446        Handle::Uuid(uuid, PhantomData)
447    }
448}
449
450/// An untyped variant of [`Handle`], which internally stores the [`Asset`] type information at runtime
451/// as a [`TypeId`] instead of encoding it in the compile-time type. This allows handles across [`Asset`] types
452/// to be stored together and compared.
453///
454/// See [`Handle`] for more information.
455#[derive(Clone, Reflect)]
456pub enum UntypedHandle {
457    /// A strong handle, which will keep the referenced [`Asset`] alive until all strong handles are dropped.
458    Strong(Arc<StrongHandle>),
459    /// A UUID handle, which does not keep the referenced [`Asset`] alive.
460    Uuid {
461        /// An identifier that records the underlying asset type.
462        type_id: TypeId,
463        /// The UUID provided during asset registration.
464        uuid: Uuid,
465    },
466}
467
468impl UntypedHandle {
469    /// Returns the equivalent of [`Handle`]'s default implementation for the given type ID.
470    pub fn default_for_type(type_id: TypeId) -> Self {
471        Self::Uuid {
472            type_id,
473            uuid: AssetId::<()>::DEFAULT_UUID,
474        }
475    }
476
477    /// Returns the [`UntypedAssetId`] for the referenced asset.
478    #[inline]
479    pub fn id(&self) -> UntypedAssetId {
480        match self {
481            UntypedHandle::Strong(handle) => UntypedAssetId::Index {
482                type_id: handle.type_id,
483                index: handle.index,
484            },
485            UntypedHandle::Uuid { type_id, uuid } => UntypedAssetId::Uuid {
486                uuid: *uuid,
487                type_id: *type_id,
488            },
489        }
490    }
491
492    /// Returns the path if this is (1) a strong handle and (2) the asset has a path
493    #[inline]
494    pub fn path(&self) -> Option<&AssetPath<'static>> {
495        match self {
496            UntypedHandle::Strong(handle) => handle.path.as_ref(),
497            UntypedHandle::Uuid { .. } => None,
498        }
499    }
500
501    /// Returns the [`TypeId`] of the referenced [`Asset`].
502    #[inline]
503    pub fn type_id(&self) -> TypeId {
504        match self {
505            UntypedHandle::Strong(handle) => handle.type_id,
506            UntypedHandle::Uuid { type_id, .. } => *type_id,
507        }
508    }
509
510    /// Converts to a typed Handle. This _will not check if the target Handle type matches_.
511    #[inline]
512    pub fn typed_unchecked<A: Asset>(self) -> Handle<A> {
513        match self {
514            UntypedHandle::Strong(handle) => Handle::Strong(handle),
515            UntypedHandle::Uuid { uuid, .. } => Handle::Uuid(uuid, PhantomData),
516        }
517    }
518
519    /// Converts to a typed Handle. This will check the type when compiled with debug asserts, but it
520    ///  _will not check if the target Handle type matches in release builds_. Use this as an optimization
521    /// when you want some degree of validation at dev-time, but you are also very certain that the type
522    /// actually matches.
523    #[inline]
524    pub fn typed_debug_checked<A: Asset>(self) -> Handle<A> {
525        debug_assert_eq!(
526            self.type_id(),
527            TypeId::of::<A>(),
528            "The target Handle<A>'s TypeId does not match the TypeId of this UntypedHandle"
529        );
530        self.typed_unchecked()
531    }
532
533    /// Converts to a typed Handle. This will panic if the internal [`TypeId`] does not match the given asset type `A`
534    #[inline]
535    pub fn typed<A: Asset>(self) -> Handle<A> {
536        let Ok(handle) = self.try_typed() else {
537            panic!(
538                "The target Handle<{}>'s TypeId does not match the TypeId of this UntypedHandle",
539                core::any::type_name::<A>()
540            )
541        };
542
543        handle
544    }
545
546    /// Converts to a typed Handle if the internal [`TypeId`] matches the given asset type `A`.
547    #[inline]
548    pub fn try_typed<A: Asset>(self) -> Result<Handle<A>, UntypedAssetConversionError> {
549        Handle::try_from(self)
550    }
551}
552
553impl PartialEq for UntypedHandle {
554    #[inline]
555    fn eq(&self, other: &Self) -> bool {
556        self.id() == other.id() && self.type_id() == other.type_id()
557    }
558}
559
560impl Eq for UntypedHandle {}
561
562impl Hash for UntypedHandle {
563    #[inline]
564    fn hash<H: Hasher>(&self, state: &mut H) {
565        self.id().hash(state);
566    }
567}
568
569impl core::fmt::Debug for UntypedHandle {
570    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
571        match self {
572            UntypedHandle::Strong(handle) => {
573                write!(
574                    f,
575                    "StrongHandle{{ type_id: {:?}, id: {:?}, path: {:?} }}",
576                    handle.type_id, handle.index, handle.path
577                )
578            }
579            UntypedHandle::Uuid { type_id, uuid } => {
580                write!(f, "UuidHandle{{ type_id: {type_id:?}, uuid: {uuid:?} }}",)
581            }
582        }
583    }
584}
585
586impl PartialOrd for UntypedHandle {
587    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
588        if self.type_id() == other.type_id() {
589            self.id().partial_cmp(&other.id())
590        } else {
591            None
592        }
593    }
594}
595
596impl From<&UntypedHandle> for UntypedAssetId {
597    #[inline]
598    fn from(value: &UntypedHandle) -> Self {
599        value.id()
600    }
601}
602
603// Cross Operations
604
605impl<A: Asset> PartialEq<UntypedHandle> for Handle<A> {
606    #[inline]
607    fn eq(&self, other: &UntypedHandle) -> bool {
608        TypeId::of::<A>() == other.type_id() && self.id() == other.id()
609    }
610}
611
612impl<A: Asset> PartialEq<Handle<A>> for UntypedHandle {
613    #[inline]
614    fn eq(&self, other: &Handle<A>) -> bool {
615        other.eq(self)
616    }
617}
618
619impl<A: Asset> PartialOrd<UntypedHandle> for Handle<A> {
620    #[inline]
621    fn partial_cmp(&self, other: &UntypedHandle) -> Option<core::cmp::Ordering> {
622        if TypeId::of::<A>() != other.type_id() {
623            None
624        } else {
625            self.id().partial_cmp(&other.id())
626        }
627    }
628}
629
630impl<A: Asset> PartialOrd<Handle<A>> for UntypedHandle {
631    #[inline]
632    fn partial_cmp(&self, other: &Handle<A>) -> Option<core::cmp::Ordering> {
633        Some(other.partial_cmp(self)?.reverse())
634    }
635}
636
637impl<A: Asset> From<Handle<A>> for UntypedHandle {
638    fn from(value: Handle<A>) -> Self {
639        match value {
640            Handle::Strong(handle) => UntypedHandle::Strong(handle),
641            Handle::Uuid(uuid, _) => UntypedHandle::Uuid {
642                type_id: TypeId::of::<A>(),
643                uuid,
644            },
645        }
646    }
647}
648
649impl<A: Asset> TryFrom<UntypedHandle> for Handle<A> {
650    type Error = UntypedAssetConversionError;
651
652    fn try_from(value: UntypedHandle) -> Result<Self, Self::Error> {
653        let found = value.type_id();
654        let expected = TypeId::of::<A>();
655
656        if found != expected {
657            return Err(UntypedAssetConversionError::TypeIdMismatch { expected, found });
658        }
659
660        Ok(match value {
661            UntypedHandle::Strong(handle) => Handle::Strong(handle),
662            UntypedHandle::Uuid { uuid, .. } => Handle::Uuid(uuid, PhantomData),
663        })
664    }
665}
666
667/// Creates a [`Handle`] from a string literal containing a UUID.
668///
669/// # Examples
670///
671/// ```
672/// # use bevy_asset::{Handle, uuid_handle};
673/// # type Image = ();
674/// const IMAGE: Handle<Image> = uuid_handle!("1347c9b7-c46a-48e7-b7b8-023a354b7cac");
675/// ```
676#[macro_export]
677macro_rules! uuid_handle {
678    ($uuid:expr) => {{
679        $crate::Handle::Uuid($crate::uuid::uuid!($uuid), ::core::marker::PhantomData)
680    }};
681}
682
683#[deprecated = "Use uuid_handle! instead"]
684#[macro_export]
685macro_rules! weak_handle {
686    ($uuid:expr) => {
687        $crate::uuid_handle!($uuid)
688    };
689}
690
691/// Errors preventing the conversion of to/from an [`UntypedHandle`] and a [`Handle`].
692#[derive(Error, Debug, PartialEq, Clone)]
693#[non_exhaustive]
694pub enum UntypedAssetConversionError {
695    /// Caused when trying to convert an [`UntypedHandle`] into a [`Handle`] of the wrong type.
696    #[error(
697        "This UntypedHandle is for {found:?} and cannot be converted into a Handle<{expected:?}>"
698    )]
699    TypeIdMismatch {
700        /// The expected [`TypeId`] of the [`Handle`] being converted to.
701        expected: TypeId,
702        /// The [`TypeId`] of the [`UntypedHandle`] being converted from.
703        found: TypeId,
704    },
705}
706
707#[cfg(test)]
708mod tests {
709    use alloc::boxed::Box;
710    use bevy_platform::hash::FixedHasher;
711    use bevy_reflect::{FromReflect, PartialReflect};
712    use core::hash::BuildHasher;
713    use uuid::Uuid;
714
715    use crate::{tests::create_app, AssetApp, Assets, VisitAssetDependencies};
716
717    use super::*;
718
719    type TestAsset = ();
720
721    const UUID_1: Uuid = Uuid::from_u128(123);
722    const UUID_2: Uuid = Uuid::from_u128(456);
723
724    /// Simple utility to directly hash a value using a fixed hasher
725    fn hash<T: Hash>(data: &T) -> u64 {
726        FixedHasher.hash_one(data)
727    }
728
729    /// Typed and Untyped `Handles` should be equivalent to each other and themselves
730    #[test]
731    fn equality() {
732        let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
733        let untyped = UntypedHandle::Uuid {
734            type_id: TypeId::of::<TestAsset>(),
735            uuid: UUID_1,
736        };
737
738        assert_eq!(
739            Ok(typed.clone()),
740            Handle::<TestAsset>::try_from(untyped.clone())
741        );
742        assert_eq!(UntypedHandle::from(typed.clone()), untyped);
743        assert_eq!(typed, untyped);
744    }
745
746    /// Typed and Untyped `Handles` should be orderable amongst each other and themselves
747    #[test]
748    #[expect(
749        clippy::cmp_owned,
750        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."
751    )]
752    fn ordering() {
753        assert!(UUID_1 < UUID_2);
754
755        let typed_1 = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
756        let typed_2 = Handle::<TestAsset>::Uuid(UUID_2, PhantomData);
757        let untyped_1 = UntypedHandle::Uuid {
758            type_id: TypeId::of::<TestAsset>(),
759            uuid: UUID_1,
760        };
761        let untyped_2 = UntypedHandle::Uuid {
762            type_id: TypeId::of::<TestAsset>(),
763            uuid: UUID_2,
764        };
765
766        assert!(typed_1 < typed_2);
767        assert!(untyped_1 < untyped_2);
768
769        assert!(UntypedHandle::from(typed_1.clone()) < untyped_2);
770        assert!(untyped_1 < UntypedHandle::from(typed_2.clone()));
771
772        assert!(Handle::<TestAsset>::try_from(untyped_1.clone()).unwrap() < typed_2);
773        assert!(typed_1 < Handle::<TestAsset>::try_from(untyped_2.clone()).unwrap());
774
775        assert!(typed_1 < untyped_2);
776        assert!(untyped_1 < typed_2);
777    }
778
779    /// Typed and Untyped `Handles` should be equivalently hashable to each other and themselves
780    #[test]
781    fn hashing() {
782        let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
783        let untyped = UntypedHandle::Uuid {
784            type_id: TypeId::of::<TestAsset>(),
785            uuid: UUID_1,
786        };
787
788        assert_eq!(
789            hash(&typed),
790            hash(&Handle::<TestAsset>::try_from(untyped.clone()).unwrap())
791        );
792        assert_eq!(hash(&UntypedHandle::from(typed.clone())), hash(&untyped));
793        assert_eq!(hash(&typed), hash(&untyped));
794    }
795
796    /// Typed and Untyped `Handles` should be interchangeable
797    #[test]
798    fn conversion() {
799        let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
800        let untyped = UntypedHandle::Uuid {
801            type_id: TypeId::of::<TestAsset>(),
802            uuid: UUID_1,
803        };
804
805        assert_eq!(typed, Handle::try_from(untyped.clone()).unwrap());
806        assert_eq!(UntypedHandle::from(typed.clone()), untyped);
807    }
808
809    #[test]
810    fn from_uuid() {
811        let uuid = UUID_1;
812        let handle: Handle<TestAsset> = uuid.into();
813
814        assert!(handle.is_uuid());
815        assert_eq!(handle.id(), AssetId::Uuid { uuid });
816    }
817
818    /// `PartialReflect::reflect_clone`/`PartialReflect::to_dynamic` should increase the strong count of a strong handle
819    #[test]
820    fn strong_handle_reflect_clone() {
821        #[derive(Reflect)]
822        struct MyAsset {
823            value: u32,
824        }
825        impl Asset for MyAsset {}
826        impl VisitAssetDependencies for MyAsset {
827            fn visit_dependencies(&self, _visit: &mut impl FnMut(UntypedAssetId)) {}
828        }
829
830        let mut app = create_app().0;
831        app.init_asset::<MyAsset>();
832        let mut assets = app.world_mut().resource_mut::<Assets<MyAsset>>();
833
834        let handle: Handle<MyAsset> = assets.add(MyAsset { value: 1 });
835        match &handle {
836            Handle::Strong(strong) => {
837                assert_eq!(
838                    Arc::strong_count(strong),
839                    1,
840                    "Inserting the asset should result in a strong count of 1"
841                );
842
843                let reflected: &dyn Reflect = &handle;
844                let _cloned_handle: Box<dyn Reflect> = reflected.reflect_clone().unwrap();
845
846                assert_eq!(
847                    Arc::strong_count(strong),
848                    2,
849                    "Cloning the handle with reflect should increase the strong count to 2"
850                );
851
852                let dynamic_handle: Box<dyn PartialReflect> = reflected.to_dynamic().unwrap();
853
854                assert_eq!(
855                    Arc::strong_count(strong),
856                    3,
857                    "Converting the handle to a dynamic should increase the strong count to 3"
858                );
859
860                let from_reflect_handle: Handle<MyAsset> =
861                    FromReflect::from_reflect(&*dynamic_handle).unwrap();
862
863                assert_eq!(Arc::strong_count(strong), 4, "Converting the reflected value back to a handle should increase the strong count to 4");
864                assert!(
865                    from_reflect_handle.is_strong(),
866                    "The cloned handle should still be strong"
867                );
868            }
869            _ => panic!("Expected a strong handle"),
870        }
871    }
872
873    #[test]
874    fn handle_from_reflect_verifies_type_id() {
875        #[derive(Reflect, Asset)]
876        struct A;
877        #[derive(Reflect, Asset)]
878        struct B;
879
880        let mut app = create_app().0;
881        app.init_asset::<A>().init_asset::<B>();
882
883        let mut assets = app.world_mut().resource_mut::<Assets<A>>();
884        let handle_a = assets.add(A);
885
886        let dynamic_handle_a = handle_a.to_dynamic().unwrap();
887        let reflected_handle_a = handle_a.as_partial_reflect();
888
889        let handle_b_from_reflect_dynamic: Option<Handle<B>> =
890            FromReflect::from_reflect(&*dynamic_handle_a);
891        let handle_b_from_reflect: Option<Handle<B>> =
892            FromReflect::from_reflect(reflected_handle_a);
893        let handle_a_from_reflect: Option<Handle<A>> =
894            FromReflect::from_reflect(reflected_handle_a);
895        assert!(
896            handle_b_from_reflect.is_none(),
897            "Handle<B> should not be constructible from reflected Handle<A>"
898        );
899        assert!(
900            handle_b_from_reflect_dynamic.is_none(),
901            "Handle<B> should not be constructible from dynamic Handle<A>"
902        );
903        assert!(
904            handle_a_from_reflect.is_some(),
905            "Handle<A> should be constructible from reflected Handle<A>"
906        );
907    }
908
909    #[test]
910    #[ignore = "Known failure tracked in #24111"]
911    fn handle_try_apply_verifies_type_id() {
912        #[derive(Reflect, Asset)]
913        struct A;
914        #[derive(Reflect, Asset)]
915        struct B;
916
917        let mut app = create_app().0;
918        app.init_asset::<A>().init_asset::<B>();
919
920        let mut assets_a = app.world_mut().resource_mut::<Assets<A>>();
921        let handle_a = assets_a.add(A);
922
923        let reflected_handle_a = handle_a.as_partial_reflect();
924
925        let mut assets_b = app.world_mut().resource_mut::<Assets<B>>();
926        let mut handle_b = assets_b.add(B);
927        assert!(
928            handle_b.try_apply(reflected_handle_a).is_err(),
929            "Handle<A> should not be applicable to Handle<B>"
930        );
931    }
932
933    #[test]
934    fn handle_from_reflect_and_try_apply() {
935        #[derive(Reflect, Asset)]
936        struct A(i32);
937
938        let mut app = create_app().0;
939        app.init_asset::<A>();
940
941        let mut assets = app.world_mut().resource_mut::<Assets<A>>();
942        let handle_1 = assets.add(A(1));
943        let reflected_handle_1 = handle_1.as_partial_reflect();
944
945        let handle_1_from_reflect: Handle<A> =
946            FromReflect::from_reflect(reflected_handle_1).unwrap();
947        assert_eq!(handle_1, handle_1_from_reflect);
948
949        let mut handle_2 = assets.add(A(2));
950        assert_ne!(handle_1, handle_2);
951        handle_2.try_apply(reflected_handle_1).unwrap();
952        assert_eq!(handle_1, handle_2);
953    }
954}