Improve error handling for AssetServer::add_async (#13745)

# Objective

The method `AssetServer::add_async` (added in
https://github.com/bevyengine/bevy/pull/13700) requires a future that
returns an `AssetLoadError` error, which was a bit of an oversight on my
part, as that type of error only really makes sense in the context of
bevy's own asset loader -- returning it from user-defined futures isn't
very useful.

## Solution

Allow passing custom error types to `add_async`, which get cast into a
trait object matching the form of `AssetLoader::load`. If merged before
the next release this will not be a breaking change
This commit is contained in:
Joseph 2024-06-10 05:56:21 -07:00 committed by François
parent a2c5b0d415
commit 1ae616fef1
No known key found for this signature in database

View File

@ -718,9 +718,9 @@ impl AssetServer {
/// ///
/// After the asset has been fully loaded, it will show up in the relevant [`Assets`] storage. /// After the asset has been fully loaded, it will show up in the relevant [`Assets`] storage.
#[must_use = "not using the returned strong handle may result in the unexpected release of the asset"] #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
pub fn add_async<A: Asset>( pub fn add_async<A: Asset, E: std::error::Error + Send + Sync + 'static>(
&self, &self,
future: impl Future<Output = Result<A, AssetLoadError>> + Send + 'static, future: impl Future<Output = Result<A, E>> + Send + 'static,
) -> Handle<A> { ) -> Handle<A> {
let handle = self let handle = self
.data .data
@ -741,12 +741,15 @@ impl AssetServer {
.unwrap(); .unwrap();
} }
Err(error) => { Err(error) => {
let error = AddAsyncError {
error: Arc::new(error),
};
error!("{error}"); error!("{error}");
event_sender event_sender
.send(InternalAssetEvent::Failed { .send(InternalAssetEvent::Failed {
id, id,
path: Default::default(), path: Default::default(),
error, error: AssetLoadError::AddAsyncError(error),
}) })
.unwrap(); .unwrap();
} }
@ -1403,6 +1406,8 @@ pub enum AssetLoadError {
CannotLoadIgnoredAsset { path: AssetPath<'static> }, CannotLoadIgnoredAsset { path: AssetPath<'static> },
#[error(transparent)] #[error(transparent)]
AssetLoaderError(#[from] AssetLoaderError), AssetLoaderError(#[from] AssetLoaderError),
#[error(transparent)]
AddAsyncError(#[from] AddAsyncError),
#[error("The file at '{}' does not contain the labeled asset '{}'; it contains the following {} assets: {}", #[error("The file at '{}' does not contain the labeled asset '{}'; it contains the following {} assets: {}",
base_path, base_path,
label, label,
@ -1441,6 +1446,22 @@ impl AssetLoaderError {
} }
} }
#[derive(Error, Debug, Clone)]
#[error("An error occurred while resolving an asset added by `add_async`: {error}")]
pub struct AddAsyncError {
error: Arc<dyn std::error::Error + Send + Sync + 'static>,
}
impl PartialEq for AddAsyncError {
/// Equality comparison is not full (only through `TypeId`)
#[inline]
fn eq(&self, other: &Self) -> bool {
self.error.type_id() == other.error.type_id()
}
}
impl Eq for AddAsyncError {}
/// An error that occurs when an [`AssetLoader`] is not registered for a given extension. /// An error that occurs when an [`AssetLoader`] is not registered for a given extension.
#[derive(Error, Debug, Clone, PartialEq, Eq)] #[derive(Error, Debug, Clone, PartialEq, Eq)]
#[error("no `AssetLoader` found{}", format_missing_asset_ext(.extensions))] #[error("no `AssetLoader` found{}", format_missing_asset_ext(.extensions))]