Error info has been added to LoadState::Failed (#12709)
# Objective Fixes #12667. ## Solution - Stored [AssetLoadError](https://docs.rs/bevy/latest/bevy/asset/enum.AssetLoadError.html) inside of [LoadState::Failed](https://docs.rs/bevy/latest/bevy/asset/enum.LoadState.html) as a Box<AssetLoadError> to avoid bloating the size of all variants of LoadState. - Fixed dependent code ## Migration guide Added [AssetLoadError](https://docs.rs/bevy/latest/bevy/asset/enum.AssetLoadError.html) to [LoadState::Failed](https://docs.rs/bevy/latest/bevy/asset/enum.LoadState.html) option Removed `Copy`, `Ord` and `PartialOrd` implementations for [LoadState](https://docs.rs/bevy/latest/bevy/asset/enum.LoadState.html) enum Added `Eq` and `PartialEq` implementations for [MissingAssetSourceError](https://docs.rs/bevy/latest/bevy/asset/io/struct.MissingAssetSourceError.html), [MissingProcessedAssetReaderError](https://docs.rs/bevy/latest/bevy/asset/io/struct.MissingProcessedAssetReaderError.html), [DeserializeMetaError](https://docs.rs/bevy/latest/bevy/asset/enum.DeserializeMetaError.html), [LoadState](https://docs.rs/bevy/latest/bevy/asset/enum.LoadState.html), [AssetLoadError](https://docs.rs/bevy/latest/bevy/asset/enum.AssetLoadError.html), [MissingAssetLoaderForTypeNameError](https://docs.rs/bevy/latest/bevy/asset/struct.MissingAssetLoaderForTypeNameError.html) and [MissingAssetLoaderForTypeIdError](https://docs.rs/bevy/latest/bevy/asset/struct.MissingAssetLoaderForTypeIdError.html)
This commit is contained in:
		
							parent
							
								
									4ca8cf5d66
								
							
						
					
					
						commit
						4da4493449
					
				| @ -51,6 +51,21 @@ pub enum AssetReaderError { | |||||||
|     HttpError(u16), |     HttpError(u16), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl PartialEq for AssetReaderError { | ||||||
|  |     /// Equality comparison for `AssetReaderError::Io` is not full (only through `ErrorKind` of inner error)
 | ||||||
|  |     #[inline] | ||||||
|  |     fn eq(&self, other: &Self) -> bool { | ||||||
|  |         match (self, other) { | ||||||
|  |             (Self::NotFound(path), Self::NotFound(other_path)) => path == other_path, | ||||||
|  |             (Self::Io(error), Self::Io(other_error)) => error.kind() == other_error.kind(), | ||||||
|  |             (Self::HttpError(code), Self::HttpError(other_code)) => code == other_code, | ||||||
|  |             _ => false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Eq for AssetReaderError {} | ||||||
|  | 
 | ||||||
| impl From<std::io::Error> for AssetReaderError { | impl From<std::io::Error> for AssetReaderError { | ||||||
|     fn from(value: std::io::Error) -> Self { |     fn from(value: std::io::Error) -> Self { | ||||||
|         Self::Io(Arc::new(value)) |         Self::Io(Arc::new(value)) | ||||||
|  | |||||||
| @ -584,7 +584,7 @@ impl AssetSources { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An error returned when an [`AssetSource`] does not exist for a given id.
 | /// An error returned when an [`AssetSource`] does not exist for a given id.
 | ||||||
| #[derive(Error, Debug, Clone)] | #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||||
| #[error("Asset Source '{0}' does not exist")] | #[error("Asset Source '{0}' does not exist")] | ||||||
| pub struct MissingAssetSourceError(AssetSourceId<'static>); | pub struct MissingAssetSourceError(AssetSourceId<'static>); | ||||||
| 
 | 
 | ||||||
| @ -594,7 +594,7 @@ pub struct MissingAssetSourceError(AssetSourceId<'static>); | |||||||
| pub struct MissingAssetWriterError(AssetSourceId<'static>); | pub struct MissingAssetWriterError(AssetSourceId<'static>); | ||||||
| 
 | 
 | ||||||
| /// An error returned when a processed [`AssetReader`] does not exist for a given id.
 | /// An error returned when a processed [`AssetReader`] does not exist for a given id.
 | ||||||
| #[derive(Error, Debug, Clone)] | #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||||
| #[error("Asset Source '{0}' does not have a processed AssetReader.")] | #[error("Asset Source '{0}' does not have a processed AssetReader.")] | ||||||
| pub struct MissingProcessedAssetReaderError(AssetSourceId<'static>); | pub struct MissingProcessedAssetReaderError(AssetSourceId<'static>); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1071,13 +1071,13 @@ mod tests { | |||||||
|             let d_id = c_text.dependencies[0].id(); |             let d_id = c_text.dependencies[0].id(); | ||||||
|             let d_text = get::<CoolText>(world, d_id); |             let d_text = get::<CoolText>(world, d_id); | ||||||
|             let (d_load, d_deps, d_rec_deps) = asset_server.get_load_states(d_id).unwrap(); |             let (d_load, d_deps, d_rec_deps) = asset_server.get_load_states(d_id).unwrap(); | ||||||
|             if d_load != LoadState::Failed { |             if !matches!(d_load, LoadState::Failed(_)) { | ||||||
|                 // wait until d has exited the loading state
 |                 // wait until d has exited the loading state
 | ||||||
|                 return None; |                 return None; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             assert!(d_text.is_none()); |             assert!(d_text.is_none()); | ||||||
|             assert_eq!(d_load, LoadState::Failed); |             assert!(matches!(d_load, LoadState::Failed(_))); | ||||||
|             assert_eq!(d_deps, DependencyLoadState::Failed); |             assert_eq!(d_deps, DependencyLoadState::Failed); | ||||||
|             assert_eq!(d_rec_deps, RecursiveDependencyLoadState::Failed); |             assert_eq!(d_rec_deps, RecursiveDependencyLoadState::Failed); | ||||||
| 
 | 
 | ||||||
| @ -1384,7 +1384,7 @@ mod tests { | |||||||
|             // Check what just failed
 |             // Check what just failed
 | ||||||
|             for error in errors.read() { |             for error in errors.read() { | ||||||
|                 let (load_state, _, _) = server.get_load_states(error.id).unwrap(); |                 let (load_state, _, _) = server.get_load_states(error.id).unwrap(); | ||||||
|                 assert_eq!(load_state, LoadState::Failed); |                 assert!(matches!(load_state, LoadState::Failed(_))); | ||||||
|                 assert_eq!(*error.path.source(), AssetSourceId::Name("unstable".into())); |                 assert_eq!(*error.path.source(), AssetSourceId::Name("unstable".into())); | ||||||
|                 match &error.error { |                 match &error.error { | ||||||
|                     AssetLoadError::AssetReaderError(read_error) => match read_error { |                     AssetLoadError::AssetReaderError(read_error) => match read_error { | ||||||
|  | |||||||
| @ -257,7 +257,7 @@ pub struct LoadDirectError { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An error that occurs while deserializing [`AssetMeta`].
 | /// An error that occurs while deserializing [`AssetMeta`].
 | ||||||
| #[derive(Error, Debug, Clone)] | #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||||
| pub enum DeserializeMetaError { | pub enum DeserializeMetaError { | ||||||
|     #[error("Failed to deserialize asset meta: {0:?}")] |     #[error("Failed to deserialize asset meta: {0:?}")] | ||||||
|     DeserializeSettings(#[from] SpannedError), |     DeserializeSettings(#[from] SpannedError), | ||||||
|  | |||||||
| @ -1203,10 +1203,8 @@ impl ProcessorAssetInfos { | |||||||
|             Err(err) => { |             Err(err) => { | ||||||
|                 error!("Failed to process asset {asset_path}: {err}"); |                 error!("Failed to process asset {asset_path}: {err}"); | ||||||
|                 // if this failed because a dependency could not be loaded, make sure it is reprocessed if that dependency is reprocessed
 |                 // if this failed because a dependency could not be loaded, make sure it is reprocessed if that dependency is reprocessed
 | ||||||
|                 if let ProcessError::AssetLoadError(AssetLoadError::AssetLoaderError { |                 if let ProcessError::AssetLoadError(AssetLoadError::AssetLoaderError(dependency)) = | ||||||
|                     path: dependency, |                     err | ||||||
|                     .. |  | ||||||
|                 }) = err |  | ||||||
|                 { |                 { | ||||||
|                     let info = self.get_mut(&asset_path).expect("info should exist"); |                     let info = self.get_mut(&asset_path).expect("info should exist"); | ||||||
|                     info.processed_info = Some(ProcessedInfo { |                     info.processed_info = Some(ProcessedInfo { | ||||||
| @ -1214,7 +1212,7 @@ impl ProcessorAssetInfos { | |||||||
|                         full_hash: AssetHash::default(), |                         full_hash: AssetHash::default(), | ||||||
|                         process_dependencies: vec![], |                         process_dependencies: vec![], | ||||||
|                     }); |                     }); | ||||||
|                     self.add_dependant(&dependency, asset_path.to_owned()); |                     self.add_dependant(dependency.path(), asset_path.to_owned()); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 let info = self.get_mut(&asset_path).expect("info should exist"); |                 let info = self.get_mut(&asset_path).expect("info should exist"); | ||||||
|  | |||||||
| @ -212,8 +212,7 @@ impl AssetInfos { | |||||||
|                 let mut should_load = false; |                 let mut should_load = false; | ||||||
|                 if loading_mode == HandleLoadingMode::Force |                 if loading_mode == HandleLoadingMode::Force | ||||||
|                     || (loading_mode == HandleLoadingMode::Request |                     || (loading_mode == HandleLoadingMode::Request | ||||||
|                         && (info.load_state == LoadState::NotLoaded |                         && matches!(info.load_state, LoadState::NotLoaded | LoadState::Failed(_))) | ||||||
|                             || info.load_state == LoadState::Failed)) |  | ||||||
|                 { |                 { | ||||||
|                     info.load_state = LoadState::Loading; |                     info.load_state = LoadState::Loading; | ||||||
|                     info.dep_load_state = DependencyLoadState::Loading; |                     info.dep_load_state = DependencyLoadState::Loading; | ||||||
| @ -412,7 +411,7 @@ impl AssetInfos { | |||||||
|                         // If dependency is loaded, reduce our count by one
 |                         // If dependency is loaded, reduce our count by one
 | ||||||
|                         false |                         false | ||||||
|                     } |                     } | ||||||
|                     LoadState::Failed => { |                     LoadState::Failed(_) => { | ||||||
|                         failed_deps.insert(*dep_id); |                         failed_deps.insert(*dep_id); | ||||||
|                         false |                         false | ||||||
|                     } |                     } | ||||||
| @ -582,12 +581,12 @@ impl AssetInfos { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub(crate) fn process_asset_fail(&mut self, failed_id: UntypedAssetId) { |     pub(crate) fn process_asset_fail(&mut self, failed_id: UntypedAssetId, error: AssetLoadError) { | ||||||
|         let (dependants_waiting_on_load, dependants_waiting_on_rec_load) = { |         let (dependants_waiting_on_load, dependants_waiting_on_rec_load) = { | ||||||
|             let info = self |             let info = self | ||||||
|                 .get_mut(failed_id) |                 .get_mut(failed_id) | ||||||
|                 .expect("Asset info should always exist at this point"); |                 .expect("Asset info should always exist at this point"); | ||||||
|             info.load_state = LoadState::Failed; |             info.load_state = LoadState::Failed(Box::new(error)); | ||||||
|             info.dep_load_state = DependencyLoadState::Failed; |             info.dep_load_state = DependencyLoadState::Failed; | ||||||
|             info.rec_dep_load_state = RecursiveDependencyLoadState::Failed; |             info.rec_dep_load_state = RecursiveDependencyLoadState::Failed; | ||||||
|             ( |             ( | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ use futures_lite::StreamExt; | |||||||
| use info::*; | use info::*; | ||||||
| use loaders::*; | use loaders::*; | ||||||
| use parking_lot::RwLock; | use parking_lot::RwLock; | ||||||
| use std::path::PathBuf; | use std::{any::Any, path::PathBuf}; | ||||||
| use std::{any::TypeId, path::Path, sync::Arc}; | use std::{any::TypeId, path::Path, sync::Arc}; | ||||||
| use thiserror::Error; | use thiserror::Error; | ||||||
| 
 | 
 | ||||||
| @ -754,7 +754,7 @@ impl AssetServer { | |||||||
|             .infos |             .infos | ||||||
|             .read() |             .read() | ||||||
|             .get(id.into()) |             .get(id.into()) | ||||||
|             .map(|i| (i.load_state, i.dep_load_state, i.rec_dep_load_state)) |             .map(|i| (i.load_state.clone(), i.dep_load_state, i.rec_dep_load_state)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Retrieves the main [`LoadState`] of a given asset `id`.
 |     /// Retrieves the main [`LoadState`] of a given asset `id`.
 | ||||||
| @ -762,7 +762,11 @@ impl AssetServer { | |||||||
|     /// Note that this is "just" the root asset load state. To check if an asset _and_ its recursive
 |     /// Note that this is "just" the root asset load state. To check if an asset _and_ its recursive
 | ||||||
|     /// dependencies have loaded, see [`AssetServer::is_loaded_with_dependencies`].
 |     /// dependencies have loaded, see [`AssetServer::is_loaded_with_dependencies`].
 | ||||||
|     pub fn get_load_state(&self, id: impl Into<UntypedAssetId>) -> Option<LoadState> { |     pub fn get_load_state(&self, id: impl Into<UntypedAssetId>) -> Option<LoadState> { | ||||||
|         self.data.infos.read().get(id.into()).map(|i| i.load_state) |         self.data | ||||||
|  |             .infos | ||||||
|  |             .read() | ||||||
|  |             .get(id.into()) | ||||||
|  |             .map(|i| i.load_state.clone()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Retrieves the [`RecursiveDependencyLoadState`] of a given asset `id`.
 |     /// Retrieves the [`RecursiveDependencyLoadState`] of a given asset `id`.
 | ||||||
| @ -1041,11 +1045,11 @@ impl AssetServer { | |||||||
|         let load_context = |         let load_context = | ||||||
|             LoadContext::new(self, asset_path.clone(), load_dependencies, populate_hashes); |             LoadContext::new(self, asset_path.clone(), load_dependencies, populate_hashes); | ||||||
|         loader.load(reader, meta, load_context).await.map_err(|e| { |         loader.load(reader, meta, load_context).await.map_err(|e| { | ||||||
|             AssetLoadError::AssetLoaderError { |             AssetLoadError::AssetLoaderError(AssetLoaderError { | ||||||
|                 path: asset_path.clone_owned(), |                 path: asset_path.clone_owned(), | ||||||
|                 loader_name: loader.type_name(), |                 loader_name: loader.type_name(), | ||||||
|                 error: e.into(), |                 error: e.into(), | ||||||
|             } |             }) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -1073,7 +1077,7 @@ pub fn handle_internal_asset_events(world: &mut World) { | |||||||
|                     sender(world, id); |                     sender(world, id); | ||||||
|                 } |                 } | ||||||
|                 InternalAssetEvent::Failed { id, path, error } => { |                 InternalAssetEvent::Failed { id, path, error } => { | ||||||
|                     infos.process_asset_fail(id); |                     infos.process_asset_fail(id, error.clone()); | ||||||
| 
 | 
 | ||||||
|                     // Send untyped failure event
 |                     // Send untyped failure event
 | ||||||
|                     untyped_failures.push(UntypedAssetLoadFailedEvent { |                     untyped_failures.push(UntypedAssetLoadFailedEvent { | ||||||
| @ -1190,7 +1194,7 @@ pub(crate) enum InternalAssetEvent { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// The load state of an asset.
 | /// The load state of an asset.
 | ||||||
| #[derive(Component, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] | #[derive(Component, Clone, Debug, PartialEq, Eq)] | ||||||
| pub enum LoadState { | pub enum LoadState { | ||||||
|     /// The asset has not started loading yet
 |     /// The asset has not started loading yet
 | ||||||
|     NotLoaded, |     NotLoaded, | ||||||
| @ -1199,7 +1203,7 @@ pub enum LoadState { | |||||||
|     /// The asset has been loaded and has been added to the [`World`]
 |     /// The asset has been loaded and has been added to the [`World`]
 | ||||||
|     Loaded, |     Loaded, | ||||||
|     /// The asset failed to load.
 |     /// The asset failed to load.
 | ||||||
|     Failed, |     Failed(Box<AssetLoadError>), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// The load state of an asset's dependencies.
 | /// The load state of an asset's dependencies.
 | ||||||
| @ -1229,7 +1233,7 @@ pub enum RecursiveDependencyLoadState { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An error that occurs during an [`Asset`] load.
 | /// An error that occurs during an [`Asset`] load.
 | ||||||
| #[derive(Error, Debug, Clone)] | #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||||
| pub enum AssetLoadError { | pub enum AssetLoadError { | ||||||
|     #[error("Requested handle of type {requested:?} for asset '{path}' does not match actual asset type '{actual_asset_name}', which used loader '{loader_name}'")] |     #[error("Requested handle of type {requested:?} for asset '{path}' does not match actual asset type '{actual_asset_name}', which used loader '{loader_name}'")] | ||||||
|     RequestedHandleTypeMismatch { |     RequestedHandleTypeMismatch { | ||||||
| @ -1268,12 +1272,8 @@ pub enum AssetLoadError { | |||||||
|     CannotLoadProcessedAsset { path: AssetPath<'static> }, |     CannotLoadProcessedAsset { path: AssetPath<'static> }, | ||||||
|     #[error("Asset '{path}' is configured to be ignored. It cannot be loaded.")] |     #[error("Asset '{path}' is configured to be ignored. It cannot be loaded.")] | ||||||
|     CannotLoadIgnoredAsset { path: AssetPath<'static> }, |     CannotLoadIgnoredAsset { path: AssetPath<'static> }, | ||||||
|     #[error("Failed to load asset '{path}' with asset loader '{loader_name}': {error}")] |     #[error(transparent)] | ||||||
|     AssetLoaderError { |     AssetLoaderError(#[from] AssetLoaderError), | ||||||
|         path: AssetPath<'static>, |  | ||||||
|         loader_name: &'static str, |  | ||||||
|         error: Arc<dyn std::error::Error + Send + Sync + 'static>, |  | ||||||
|     }, |  | ||||||
|     #[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, | ||||||
| @ -1286,22 +1286,48 @@ pub enum AssetLoadError { | |||||||
|     }, |     }, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An error that occurs when an [`AssetLoader`] is not registered for a given extension.
 |  | ||||||
| #[derive(Error, Debug, Clone)] | #[derive(Error, Debug, Clone)] | ||||||
|  | #[error("Failed to load asset '{path}' with asset loader '{loader_name}': {error}")] | ||||||
|  | pub struct AssetLoaderError { | ||||||
|  |     path: AssetPath<'static>, | ||||||
|  |     loader_name: &'static str, | ||||||
|  |     error: Arc<dyn std::error::Error + Send + Sync + 'static>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PartialEq for AssetLoaderError { | ||||||
|  |     /// Equality comparison for `AssetLoaderError::error` is not full (only through `TypeId`)
 | ||||||
|  |     #[inline] | ||||||
|  |     fn eq(&self, other: &Self) -> bool { | ||||||
|  |         self.path == other.path | ||||||
|  |             && self.loader_name == other.loader_name | ||||||
|  |             && self.error.type_id() == other.error.type_id() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Eq for AssetLoaderError {} | ||||||
|  | 
 | ||||||
|  | impl AssetLoaderError { | ||||||
|  |     pub fn path(&self) -> &AssetPath<'static> { | ||||||
|  |         &self.path | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// An error that occurs when an [`AssetLoader`] is not registered for a given extension.
 | ||||||
|  | #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||||
| #[error("no `AssetLoader` found{}", format_missing_asset_ext(.extensions))] | #[error("no `AssetLoader` found{}", format_missing_asset_ext(.extensions))] | ||||||
| pub struct MissingAssetLoaderForExtensionError { | pub struct MissingAssetLoaderForExtensionError { | ||||||
|     extensions: Vec<String>, |     extensions: Vec<String>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An error that occurs when an [`AssetLoader`] is not registered for a given [`std::any::type_name`].
 | /// An error that occurs when an [`AssetLoader`] is not registered for a given [`std::any::type_name`].
 | ||||||
| #[derive(Error, Debug, Clone)] | #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||||
| #[error("no `AssetLoader` found with the name '{type_name}'")] | #[error("no `AssetLoader` found with the name '{type_name}'")] | ||||||
| pub struct MissingAssetLoaderForTypeNameError { | pub struct MissingAssetLoaderForTypeNameError { | ||||||
|     type_name: String, |     type_name: String, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An error that occurs when an [`AssetLoader`] is not registered for a given [`Asset`] [`TypeId`].
 | /// An error that occurs when an [`AssetLoader`] is not registered for a given [`Asset`] [`TypeId`].
 | ||||||
| #[derive(Error, Debug, Clone)] | #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||||
| #[error("no `AssetLoader` found with the ID '{type_id:?}'")] | #[error("no `AssetLoader` found with the ID '{type_id:?}'")] | ||||||
| pub struct MissingAssetLoaderForTypeIdError { | pub struct MissingAssetLoaderForTypeIdError { | ||||||
|     pub type_id: TypeId, |     pub type_id: TypeId, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Vitaliy Sapronenko
						Vitaliy Sapronenko