Skip to main content

bevy_asset/
handle.rs

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/// 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, 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/// The internal "strong" [`Asset`] handle storage for [`Handle::Strong`] and [`UntypedHandle::Strong`]. When this is dropped,
82/// the [`Asset`] will be freed. It also stores some asset metadata for easy access from handles.
83#[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    /// Modifies asset meta. This is stored on the handle because it is:
90    /// 1. configuration tied to the lifetime of a specific asset load
91    /// 2. configuration that must be repeatable when the asset is hot-reloaded
92    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/// 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(Debug, Hash, PartialEq, Clone, Handle, from_reflect = false)]
134pub 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// `Handle` needs a custom `FromReflect` to do extra type checking - see the
144// `strong_handle.type_id` check below.
145// `Handle` needs a custom `FromReflect` to do extra type checking - see the
146// `strong_handle.type_id` check below.
147impl<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                // This is necessary as otherwise you could construct Handle<A> via Handle<B>
163                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    /// Returns the [`AssetId`] of this [`Asset`].
191    #[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    /// Returns the path if this is (1) a strong handle and (2) the asset has a path
203    #[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    /// Returns `true` if this is a uuid handle.
212    #[inline]
213    pub fn is_uuid(&self) -> bool {
214        matches!(self, Handle::Uuid(..))
215    }
216
217    /// Returns `true` if this is a strong handle.
218    #[inline]
219    pub fn is_strong(&self) -> bool {
220        matches!(self, Handle::Strong(_))
221    }
222
223    /// Converts this [`Handle`] to an "untyped" / "generic-less" [`UntypedHandle`], which stores the [`Asset`] type information
224    /// _inside_ [`UntypedHandle`]. This will return [`UntypedHandle::Strong`] for [`Handle::Strong`] and [`UntypedHandle::Uuid`] for
225    /// [`Handle::Uuid`].
226    #[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
238// This enables FromTemplate specialization for `Handle<T>` using the
239// ["auto trait specialization" trick](https://github.com/coolcatcoder/rust_techniques/issues/1)
240// This enables Handle to implement Default _and_ implement FromTemplate, without conflicting with the
241// blanket impl of FromTemplate for T: Default + Clone.
242impl<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/// A [`Template`] that produces a [`Handle`].
249///
250/// # How asset paths are resolved in templates
251///
252/// When a type with a [`Handle<T>`] field derives [`FromTemplate`], that field is replaced by its
253/// template type, [`HandleTemplate<T>`], when created via BSN.
254/// We can see that [`HandleTemplate<T>`] has the following trait impl block:
255///
256/// ```rust, ignore
257/// impl<I: Into<AssetPath<'static>>, T: Asset> From<I> for HandleTemplate<T> {
258///     fn from(value: I) -> Self {
259///         Self::Path(value.into())
260///     }
261/// }
262/// ```
263///
264/// [`AssetPath<'static>`] implements [`From<&'static str>`].
265/// Because of that, assigning a string literal to a `Handle<T>` field automatically converts it into
266/// [`HandleTemplate<T>::Path`] with that asset path when used in the `bsn!` macro.
267/// Calls to `bsn!` automatically insert `.into()` conversions, and due to Rust's blanket impl that turns [`From`] trait impls into their [`Into`]
268/// equivalents, the conversion from `&'static str` to `AssetPath<'static>` is handled automatically.
269/// Finally, the [`HandleTemplate<T>::Path`] generated gets converted to a [`Handle<T>`] during scene initialization,
270/// as the asset is loaded from the given path, and the resulting handle is assigned to the field,
271/// pointing to the asset that was found at the file path in our original string.
272#[derive(Reflect)]
273pub enum HandleTemplate<T: Asset> {
274    /// Creates a [`Handle`] by calling [`AssetServer::load`] on the given [`AssetPath`].
275    Path(AssetPath<'static>),
276    /// Creates a [`Handle`] by cloning the given [`Handle`] value.
277    Handle(Handle<T>),
278    /// Creates a [`Handle`] by adding the given asset value using [`AssetServer::add`]. This will
279    /// cache the resulting [`Handle`] on the template and reuse it for future template builds.
280    ///
281    /// This should generally be constructed using [`HandleTemplate::value`] or [`asset_value`].
282    Value(ArcMutexValue<T>),
283}
284
285impl<T: Asset> HandleTemplate<T> {
286    /// This will create a new [`HandleTemplate`] for the given `asset` value. This makes it possible
287    /// to define assets "inline" in templates / scenes that produce a [`Handle`].
288    ///
289    /// This supports [`Into`]
290    /// to automatically convert values that can become `A`.
291    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/// Stores an [`Arc<Mutex<AssetOrHandle<T>>>`].
299///
300/// This intermediary type exists largely to enable reflect(opaque).
301#[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                // This unwrap is ok. If another caller panicked while holding this mutex, then the
349                // program is in an invalid state and this should panic too.
350                let mut value_or_handle = value.0.lock().unwrap();
351                match &mut *value_or_handle {
352                    AssetOrHandle::Value(value) => {
353                        // This unwrap is ok because AssetOrHandle::Value will always either contain a Some Value
354                        // when it is in this state (AssetOrHandle is private).
355                        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
374/// This will create a new [`HandleTemplate`] for the given `asset` value. This makes it possible
375/// to define assets "inline" in templates / scenes that produce a [`Handle`].
376///
377/// This supports [`Into`]
378/// to automatically convert values that can become `A`.
379pub 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
406// Handle uses AssetId when hashing. This enables using AssetId instead of handle with hashsets and hashmaps.
407impl<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/// An untyped variant of [`Handle`], which internally stores the [`Asset`] type information at runtime
470/// as a [`TypeId`] instead of encoding it in the compile-time type. This allows handles across [`Asset`] types
471/// to be stored together and compared.
472///
473/// See [`Handle`] for more information.
474#[derive(Clone, Reflect)]
475pub enum UntypedHandle {
476    /// A strong handle, which will keep the referenced [`Asset`] alive until all strong handles are dropped.
477    Strong(Arc<StrongHandle>),
478    /// A UUID handle, which does not keep the referenced [`Asset`] alive.
479    Uuid {
480        /// An identifier that records the underlying asset type.
481        type_id: TypeId,
482        /// The UUID provided during asset registration.
483        uuid: Uuid,
484    },
485}
486
487impl UntypedHandle {
488    /// Returns the equivalent of [`Handle`]'s default implementation for the given type ID.
489    pub fn default_for_type(type_id: TypeId) -> Self {
490        Self::Uuid {
491            type_id,
492            uuid: AssetId::<()>::DEFAULT_UUID,
493        }
494    }
495
496    /// Returns the [`UntypedAssetId`] for the referenced asset.
497    #[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    /// Returns the path if this is (1) a strong handle and (2) the asset has a path
512    #[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    /// Returns the [`TypeId`] of the referenced [`Asset`].
521    #[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    /// Converts to a typed Handle. This _will not check if the target Handle type matches_.
530    #[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    /// Converts to a typed Handle. This will check the type when compiled with debug asserts, but it
539    ///  _will not check if the target Handle type matches in release builds_. Use this as an optimization
540    /// when you want some degree of validation at dev-time, but you are also very certain that the type
541    /// actually matches.
542    #[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    /// Converts to a typed Handle. This will panic if the internal [`TypeId`] does not match the given asset type `A`
553    #[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    /// Converts to a typed Handle. This will panic if the internal [`TypeId`] does not match the given asset type `A`
566    #[inline]
567    pub fn try_typed<A: Asset>(self) -> Result<Handle<A>, UntypedAssetConversionError> {
568        Handle::try_from(self)
569    }
570
571    /// The "meta transform" for the strong handle. This will only be [`Some`] if the handle is strong and there is a meta transform
572    /// associated with it.
573    #[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
632// Cross Operations
633
634impl<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/// Creates a [`Handle`] from a string literal containing a UUID.
697///
698/// # Examples
699///
700/// ```
701/// # use bevy_asset::{Handle, uuid_handle};
702/// # type Image = ();
703/// const IMAGE: Handle<Image> = uuid_handle!("1347c9b7-c46a-48e7-b7b8-023a354b7cac");
704/// ```
705#[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/// Errors preventing the conversion of to/from an [`UntypedHandle`] and a [`Handle`].
721#[derive(Error, Debug, PartialEq, Clone)]
722#[non_exhaustive]
723pub enum UntypedAssetConversionError {
724    /// Caused when trying to convert an [`UntypedHandle`] into a [`Handle`] of the wrong type.
725    #[error(
726        "This UntypedHandle is for {found:?} and cannot be converted into a Handle<{expected:?}>"
727    )]
728    TypeIdMismatch {
729        /// The expected [`TypeId`] of the [`Handle`] being converted to.
730        expected: TypeId,
731        /// The [`TypeId`] of the [`UntypedHandle`] being converted from.
732        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    /// Simple utility to directly hash a value using a fixed hasher
754    fn hash<T: Hash>(data: &T) -> u64 {
755        FixedHasher.hash_one(data)
756    }
757
758    /// Typed and Untyped `Handles` should be equivalent to each other and themselves
759    #[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    /// Typed and Untyped `Handles` should be orderable amongst each other and themselves
776    #[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    /// Typed and Untyped `Handles` should be equivalently hashable to each other and themselves
809    #[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    /// Typed and Untyped `Handles` should be interchangeable
826    #[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    /// `PartialReflect::reflect_clone`/`PartialReflect::to_dynamic` should increase the strong count of a strong handle
848    #[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}