Skip to content

Commit bc097db

Browse files
committed
fix: support running embedded_path! from a "remote" crate
This includes if cargo is building a crate that depends on some crates.io or git repo crate that contains embedded assets.
1 parent 21eb807 commit bc097db

File tree

1 file changed

+99
-6
lines changed
  • crates/bevy_asset/src/io/embedded

1 file changed

+99
-6
lines changed

crates/bevy_asset/src/io/embedded/mod.rs

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,55 @@ macro_rules! embedded_path {
109109

110110
($source_path: expr, $path_str: expr) => {{
111111
let crate_name = module_path!().split(':').next().unwrap();
112-
let file_path = std::path::Path::new(file!());
113-
let after_src = file_path
114-
.strip_prefix($source_path)
115-
.unwrap_or_else(|_| panic!("{file_path:?} does not have prefix {}", $source_path));
116-
let file_path = after_src.parent().unwrap().join($path_str);
117-
std::path::Path::new(crate_name).join(file_path)
112+
$crate::io::embedded::_embedded_asset_path(
113+
crate_name,
114+
$source_path.as_ref(),
115+
file!().as_ref(),
116+
$path_str.as_ref(),
117+
)
118118
}};
119119
}
120120

121+
/// Implementation detail of `embedded_path`, do not use this!
122+
///
123+
/// Returns an embedded asset path, given:
124+
/// - `crate_name`: name of the crate where the asset is embedded
125+
/// - `src_prefix`: path prefix of the crate's source directory, relative to the workspace root
126+
/// - `file_path`: `std::file!()` path of the source file where `embedded_path!` is called
127+
/// - `asset_path`: path of the embedded asset relative to `file_path`
128+
#[doc(hidden)]
129+
pub fn _embedded_asset_path(
130+
crate_name: &str,
131+
src_prefix: &Path,
132+
file_path: &Path,
133+
asset_path: &Path,
134+
) -> PathBuf {
135+
let after_src = if file_path.is_absolute() {
136+
// Absolute paths show up if this macro is being invoked by an external
137+
// dependency, e.g. one that's being checked out from a crates repo
138+
// or git.
139+
let mut maybe_parent = file_path.parent();
140+
loop {
141+
let Some(parent) = maybe_parent else {
142+
panic!("Failed to find src_prefix {src_prefix:?} in {file_path:?}")
143+
};
144+
if parent.ends_with(src_prefix) {
145+
break file_path.strip_prefix(parent).unwrap();
146+
}
147+
maybe_parent = parent.parent();
148+
}
149+
} else {
150+
// Relative paths show up if this macro is being invoked by a local
151+
// crate. In this case we know the relative path is a sub- path of the
152+
// workspace root.
153+
file_path
154+
.strip_prefix(src_prefix)
155+
.unwrap_or_else(|_| panic!("{file_path:?} does not have src_prefix {src_prefix:?}"))
156+
};
157+
let asset_path = after_src.parent().unwrap().join(asset_path);
158+
Path::new(crate_name).join(asset_path)
159+
}
160+
121161
/// Creates a new `embedded` asset by embedding the bytes of the given path into the current binary
122162
/// and registering those bytes with the `embedded` [`AssetSource`].
123163
///
@@ -250,3 +290,56 @@ macro_rules! load_internal_binary_asset {
250290
);
251291
}};
252292
}
293+
294+
#[cfg(test)]
295+
mod tests {
296+
use super::_embedded_asset_path;
297+
use std::path::Path;
298+
299+
#[test]
300+
fn embedded_asset_path_from_local_crate() {
301+
let asset_path = _embedded_asset_path(
302+
"my_crate",
303+
"src".as_ref(),
304+
"src/foo/plugin.rs".as_ref(),
305+
"the/asset.png".as_ref(),
306+
);
307+
assert_eq!(asset_path, Path::new("my_crate/foo/the/asset.png"));
308+
}
309+
310+
#[test]
311+
fn embedded_asset_path_from_local_example_crate() {
312+
let asset_path = _embedded_asset_path(
313+
"example_name",
314+
"examples/foo".as_ref(),
315+
"examples/foo/example.rs".as_ref(),
316+
"the/asset.png".as_ref(),
317+
);
318+
assert_eq!(asset_path, Path::new("example_name/the/asset.png"));
319+
}
320+
321+
#[test]
322+
fn embedded_asset_path_from_external_crate() {
323+
let asset_path = _embedded_asset_path(
324+
"my_crate",
325+
"src".as_ref(),
326+
"/path/to/crate/src/foo/plugin.rs".as_ref(),
327+
"the/asset.png".as_ref(),
328+
);
329+
assert_eq!(asset_path, Path::new("my_crate/foo/the/asset.png"));
330+
}
331+
332+
// We don't handle this edge case because it is ambiguous with the
333+
// information currently available to the embedded_path macro.
334+
#[test]
335+
fn embedded_asset_path_from_external_crate_is_ambiguous() {
336+
let asset_path = _embedded_asset_path(
337+
"my_crate",
338+
"src".as_ref(),
339+
"/path/to/.cargo/registry/src/crate/src/src/plugin.rs".as_ref(),
340+
"the/asset.png".as_ref(),
341+
);
342+
// Really, should be "my_crate/src/the/asset.png"
343+
assert_eq!(asset_path, Path::new("my_crate/the/asset.png"));
344+
}
345+
}

0 commit comments

Comments
 (0)