diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index fafff560083469..0e0d9961b66593 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -18,7 +18,7 @@ use thiserror::Error; pub enum AssetServerError { #[error("asset folder path is not a directory")] AssetFolderNotADirectory(String), - #[error("no AssetLoader found for the given extension")] + #[error("no AssetLoader found{}", format_missing_asset_ext(.0))] MissingAssetLoader(Option), #[error("the given type does not match the type of the loaded asset")] IncorrectHandleType, @@ -28,6 +28,13 @@ pub enum AssetServerError { PathLoaderError(#[from] AssetIoError), } +fn format_missing_asset_ext(ext: &Option) -> String { + return ext + .as_ref() + .map(|x| format!(" for the following extension(s): {}", x)) + .unwrap_or(String::new()); +} + #[derive(Default)] pub(crate) struct AssetRefCounter { pub(crate) channel: Arc, @@ -132,11 +139,23 @@ impl AssetServer { &self, path: P, ) -> Result>, AssetServerError> { - path.as_ref() - .extension() - .and_then(|e| e.to_str()) - .ok_or(AssetServerError::MissingAssetLoader(None)) - .and_then(|extension| self.get_asset_loader(extension)) + let s = path + .as_ref() + .file_name() + .ok_or(AssetServerError::MissingAssetLoader(None))? + .to_str() + .ok_or(AssetServerError::MissingAssetLoader(None))?; + + let mut exts = Vec::new(); + let mut ext = s; + while let Some(idx) = ext.find('.') { + ext = &ext[idx + 1..]; + exts.push(ext); + if let Ok(loader) = self.get_asset_loader(ext) { + return Ok(loader); + } + } + Err(AssetServerError::MissingAssetLoader(Some(exts.join(", ")))) } pub fn get_handle_path>(&self, handle: H) -> Option> { @@ -444,3 +463,99 @@ impl AssetServer { pub fn free_unused_assets_system(asset_server: Res) { asset_server.free_unused_assets(); } + +#[cfg(test)] +mod test { + use super::*; + use bevy_utils::BoxedFuture; + + struct FakePngLoader; + impl AssetLoader for FakePngLoader { + fn load<'a>( + &'a self, + _: &'a [u8], + _: &'a mut LoadContext, + ) -> BoxedFuture<'a, Result<(), anyhow::Error>> { + Box::pin(async move { Ok(()) }) + } + + fn extensions(&self) -> &[&str] { + &["png"] + } + } + + struct FakeMultipleDotLoader; + impl AssetLoader for FakeMultipleDotLoader { + fn load<'a>( + &'a self, + _: &'a [u8], + _: &'a mut LoadContext, + ) -> BoxedFuture<'a, Result<(), anyhow::Error>> { + Box::pin(async move { Ok(()) }) + } + + fn extensions(&self) -> &[&str] { + &["test.png"] + } + } + + fn setup() -> AssetServer { + use crate::FileAssetIo; + + let asset_server = AssetServer { + server: Arc::new(AssetServerInternal { + loaders: Default::default(), + extension_to_loader_index: Default::default(), + asset_sources: Default::default(), + asset_ref_counter: Default::default(), + handle_to_path: Default::default(), + asset_lifecycles: Default::default(), + task_pool: Default::default(), + asset_io: Box::new(FileAssetIo::new(&".")), + }), + }; + asset_server.add_loader::(FakePngLoader); + asset_server.add_loader::(FakeMultipleDotLoader); + asset_server + } + + #[test] + fn extensions() { + let asset_server = setup(); + let t = asset_server.get_path_asset_loader("test.png"); + assert_eq!(t.unwrap().extensions()[0], "png"); + } + + #[test] + fn no_loader() { + let asset_server = setup(); + let t = asset_server.get_path_asset_loader("test.pong"); + assert!(t.is_err()); + } + + #[test] + fn multiple_extensions_no_loader() { + let asset_server = setup(); + + assert!( + match asset_server.get_path_asset_loader("test.v1.2.3.pong") { + Err(AssetServerError::MissingAssetLoader(Some(ext))) => ext == "v1.2.3.pong, 2.3.pong, 3.pong, pong", + _ => false, + } + ) + } + + #[test] + fn filename_with_dots() { + let asset_server = setup(); + let t = asset_server.get_path_asset_loader("test-v1.2.3.png"); + assert_eq!(t.unwrap().extensions()[0], "png"); + } + + #[test] + fn multiple_extensions() { + let asset_server = setup(); + let t = asset_server.get_path_asset_loader("test.test.png"); + assert_eq!(t.unwrap().extensions()[0], "test.png"); + } +}