diff --git a/crates/bevy_asset/src/io/embedded/mod.rs b/crates/bevy_asset/src/io/embedded/mod.rs index 0448a7cdd83a7..de583f7367831 100644 --- a/crates/bevy_asset/src/io/embedded/mod.rs +++ b/crates/bevy_asset/src/io/embedded/mod.rs @@ -111,13 +111,30 @@ macro_rules! embedded_path { }}; ($source_path: expr, $path_str: expr) => {{ - let crate_name = module_path!().split(':').next().unwrap(); - let after_src = file!().split($source_path).nth(1).unwrap(); - let file_path = std::path::Path::new(after_src) - .parent() - .unwrap() - .join($path_str); - std::path::Path::new(crate_name).join(file_path) + let Some(crate_name) = module_path!().split(':').next() else { + panic!( + "Could not find crate name. Expected ':' in module path `{}`", + module_path!() + ); + }; + if let Some(after_src) = file!().split($source_path).nth(1) { + let file_path = if let Some(parent) = std::path::Path::new(after_src).parent() { + parent.join($path_str) + } else { + panic!( + "Expected parent path for `{}` derived from `{}`", + after_src, + file!() + ); + }; + std::path::Path::new(crate_name).join(file_path) + } else { + panic!( + "Expected source path `{}` in file path `{}`", + $source_path, + file!() + ); + } }}; } @@ -200,7 +217,11 @@ macro_rules! embedded_asset { .resource_mut::<$crate::io::embedded::EmbeddedAssetRegistry>(); let path = $crate::embedded_path!($source_path, $path); #[cfg(feature = "embedded_watcher")] - let full_path = std::path::Path::new(file!()).parent().unwrap().join($path); + let full_path = if let Some(parent) = std::path::Path::new(file!()).parent() { + parent.join($path) + } else { + panic!("Expected parent path for `{}`", file!()) + }; #[cfg(not(feature = "embedded_watcher"))] let full_path = std::path::PathBuf::new(); embedded.insert_asset(full_path, &path, include_bytes!($path)); @@ -255,3 +276,61 @@ macro_rules! load_internal_binary_asset { ); }}; } + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + #[test] + fn test_embedded_path_no_panics() { + #[cfg(not(windows))] + assert_eq!(file!(), "crates/bevy_asset/src/io/embedded/mod.rs"); + #[cfg(windows)] + assert_eq!(file!(), "crates\\bevy_asset\\src\\io\\embedded\\mod.rs"); + + #[cfg(not(windows))] + let path = PathBuf::from("bevy_asset/io/embedded/embed.png"); + #[cfg(windows)] + let path = PathBuf::from("bevy_asset\\io\\embedded\\embed.png"); + + // One argument for embedded_path! uses '/src/' as its source_path. + assert_eq!(&embedded_path!("embed.png"), &path); + assert_eq!(&embedded_path!("/src/", "embed.png"), &path); + } + + /// Using a slightly different source path parameter causes the crate_name to be omitted. + /// Possible bug. + #[test] + fn test_embedded_path_questionable() { + #[cfg(not(windows))] + let path = PathBuf::from("/io/embedded/embed.png"); + #[cfg(windows)] + let path = PathBuf::from("\\io\\embedded\\embed.png"); + assert_eq!(&embedded_path!("/src", "embed.png"), &path); + assert_eq!(&embedded_path!("src", "embed.png"), &path); + } + + #[test] + #[should_panic( + // Unix + // expected = "Expected source path `NOT-IN-PATH` in file path `crates/bevy_asset/src/io/embedded/mod.rs`" + // Windows + // expected = "Expected source path `NOT-IN-PATH` in file path `crates\\bevy_asset\\src\\io\\embedded\\mod.rs`" + expected = "Expected source path `NOT-IN-PATH` in file path `crates" + )] + fn test_embedded_path_nonexistent_src_path() { + embedded_path!("NOT-IN-PATH", "embed.png"); + } + + #[test] + #[should_panic( + // Unix + // expected = "Expected parent path for `` derived from `crates/bevy_asset/src/io/embedded/mod.rs`" + // Windows + // expected = "Expected parent path for `` derived from `crates\\bevy_asset\\src\\io\\embedded\\mod.rs`" + expected = "Expected parent path for `` derived from `crates" + )] + fn test_embedded_parent_panic() { + embedded_path!("mod.rs", "embed.png"); + } +}