bevy_asset/
event.rs

1use crate::{Asset, AssetId, AssetLoadError, AssetPath, UntypedAssetId};
2use bevy_ecs::event::Event;
3use bevy_reflect::Reflect;
4use core::fmt::Debug;
5
6/// An event emitted when a specific [`Asset`] fails to load.
7///
8/// For an untyped equivalent, see [`UntypedAssetLoadFailedEvent`].
9#[derive(Event, Clone, Debug)]
10pub struct AssetLoadFailedEvent<A: Asset> {
11    /// The stable identifier of the asset that failed to load.
12    pub id: AssetId<A>,
13    /// The asset path that was attempted.
14    pub path: AssetPath<'static>,
15    /// Why the asset failed to load.
16    pub error: AssetLoadError,
17}
18
19impl<A: Asset> AssetLoadFailedEvent<A> {
20    /// Converts this to an "untyped" / "generic-less" asset error event that stores the type information.
21    pub fn untyped(&self) -> UntypedAssetLoadFailedEvent {
22        self.into()
23    }
24}
25
26/// An untyped version of [`AssetLoadFailedEvent`].
27#[derive(Event, Clone, Debug)]
28pub struct UntypedAssetLoadFailedEvent {
29    /// The stable identifier of the asset that failed to load.
30    pub id: UntypedAssetId,
31    /// The asset path that was attempted.
32    pub path: AssetPath<'static>,
33    /// Why the asset failed to load.
34    pub error: AssetLoadError,
35}
36
37impl<A: Asset> From<&AssetLoadFailedEvent<A>> for UntypedAssetLoadFailedEvent {
38    fn from(value: &AssetLoadFailedEvent<A>) -> Self {
39        UntypedAssetLoadFailedEvent {
40            id: value.id.untyped(),
41            path: value.path.clone(),
42            error: value.error.clone(),
43        }
44    }
45}
46
47/// Events that occur for a specific loaded [`Asset`], such as "value changed" events and "dependency" events.
48#[expect(missing_docs, reason = "Documenting the id fields is unhelpful.")]
49#[derive(Event, Reflect)]
50pub enum AssetEvent<A: Asset> {
51    /// Emitted whenever an [`Asset`] is added.
52    Added { id: AssetId<A> },
53    /// Emitted whenever an [`Asset`] value is modified.
54    Modified { id: AssetId<A> },
55    /// Emitted whenever an [`Asset`] is removed.
56    Removed { id: AssetId<A> },
57    /// Emitted when the last [`super::Handle::Strong`] of an [`Asset`] is dropped.
58    Unused { id: AssetId<A> },
59    /// Emitted whenever an [`Asset`] has been fully loaded (including its dependencies and all "recursive dependencies").
60    LoadedWithDependencies { id: AssetId<A> },
61}
62
63impl<A: Asset> AssetEvent<A> {
64    /// Returns `true` if this event is [`AssetEvent::LoadedWithDependencies`] and matches the given `id`.
65    pub fn is_loaded_with_dependencies(&self, asset_id: impl Into<AssetId<A>>) -> bool {
66        matches!(self, AssetEvent::LoadedWithDependencies { id } if *id == asset_id.into())
67    }
68
69    /// Returns `true` if this event is [`AssetEvent::Added`] and matches the given `id`.
70    pub fn is_added(&self, asset_id: impl Into<AssetId<A>>) -> bool {
71        matches!(self, AssetEvent::Added { id } if *id == asset_id.into())
72    }
73
74    /// Returns `true` if this event is [`AssetEvent::Modified`] and matches the given `id`.
75    pub fn is_modified(&self, asset_id: impl Into<AssetId<A>>) -> bool {
76        matches!(self, AssetEvent::Modified { id } if *id == asset_id.into())
77    }
78
79    /// Returns `true` if this event is [`AssetEvent::Removed`] and matches the given `id`.
80    pub fn is_removed(&self, asset_id: impl Into<AssetId<A>>) -> bool {
81        matches!(self, AssetEvent::Removed { id } if *id == asset_id.into())
82    }
83
84    /// Returns `true` if this event is [`AssetEvent::Unused`] and matches the given `id`.
85    pub fn is_unused(&self, asset_id: impl Into<AssetId<A>>) -> bool {
86        matches!(self, AssetEvent::Unused { id } if *id == asset_id.into())
87    }
88}
89
90impl<A: Asset> Clone for AssetEvent<A> {
91    fn clone(&self) -> Self {
92        *self
93    }
94}
95
96impl<A: Asset> Copy for AssetEvent<A> {}
97
98impl<A: Asset> Debug for AssetEvent<A> {
99    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100        match self {
101            Self::Added { id } => f.debug_struct("Added").field("id", id).finish(),
102            Self::Modified { id } => f.debug_struct("Modified").field("id", id).finish(),
103            Self::Removed { id } => f.debug_struct("Removed").field("id", id).finish(),
104            Self::Unused { id } => f.debug_struct("Unused").field("id", id).finish(),
105            Self::LoadedWithDependencies { id } => f
106                .debug_struct("LoadedWithDependencies")
107                .field("id", id)
108                .finish(),
109        }
110    }
111}
112
113impl<A: Asset> PartialEq for AssetEvent<A> {
114    fn eq(&self, other: &Self) -> bool {
115        match (self, other) {
116            (Self::Added { id: l_id }, Self::Added { id: r_id })
117            | (Self::Modified { id: l_id }, Self::Modified { id: r_id })
118            | (Self::Removed { id: l_id }, Self::Removed { id: r_id })
119            | (Self::Unused { id: l_id }, Self::Unused { id: r_id })
120            | (
121                Self::LoadedWithDependencies { id: l_id },
122                Self::LoadedWithDependencies { id: r_id },
123            ) => l_id == r_id,
124            _ => false,
125        }
126    }
127}
128
129impl<A: Asset> Eq for AssetEvent<A> {}