@@ -109,15 +109,55 @@ macro_rules! embedded_path {
109
109
110
110
( $source_path: expr, $path_str: expr) => { {
111
111
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
+ )
118
118
} } ;
119
119
}
120
120
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
+
121
161
/// Creates a new `embedded` asset by embedding the bytes of the given path into the current binary
122
162
/// and registering those bytes with the `embedded` [`AssetSource`].
123
163
///
@@ -250,3 +290,56 @@ macro_rules! load_internal_binary_asset {
250
290
) ;
251
291
} } ;
252
292
}
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