~repos /rust-embed
git clone https://pyrossh.dev/repos/rust-embed.git
rust macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.
e1f172ee
—
Haris Khan 1 year ago
Allow users to specify a custom path to the rust_embed crate in generated code
- .github/workflows/test.yml +2 -0
- impl/src/lib.rs +38 -27
- tests/custom_crate_path.rs +20 -0
.github/workflows/test.yml
CHANGED
|
@@ -37,6 +37,8 @@ jobs:
|
|
|
37
37
|
cargo test --test mime_guess --features "mime-guess" --release
|
|
38
38
|
cargo test --test interpolated_path --features "interpolate-folder-path"
|
|
39
39
|
cargo test --test interpolated_path --features "interpolate-folder-path" --release
|
|
40
|
+
cargo test --test custom_crate_path
|
|
41
|
+
cargo test --test custom_crate_path --release
|
|
40
42
|
cargo build --example basic
|
|
41
43
|
cargo build --example rocket --features rocket
|
|
42
44
|
cargo build --example actix --features actix
|
impl/src/lib.rs
CHANGED
|
@@ -17,7 +17,7 @@ use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprLit, Fields, Lit, Meta
|
|
|
17
17
|
|
|
18
18
|
fn embedded(
|
|
19
19
|
ident: &syn::Ident, relative_folder_path: Option<&str>, absolute_folder_path: String, prefix: Option<&str>, includes: &[String], excludes: &[String],
|
|
20
|
-
metadata_only: bool,
|
|
20
|
+
metadata_only: bool, crate_path: &syn::Path,
|
|
21
21
|
) -> syn::Result<TokenStream2> {
|
|
22
22
|
extern crate rust_embed_utils;
|
|
23
23
|
|
|
@@ -30,7 +30,7 @@ fn embedded(
|
|
|
30
30
|
for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(absolute_folder_path.clone(), matcher) {
|
|
31
31
|
match_values.insert(
|
|
32
32
|
rel_path.clone(),
|
|
33
|
-
embed_file(relative_folder_path, ident, &rel_path, &full_canonical_path, metadata_only)?,
|
|
33
|
+
embed_file(relative_folder_path, ident, &rel_path, &full_canonical_path, metadata_only, crate_path)?,
|
|
34
34
|
);
|
|
35
35
|
|
|
36
36
|
list_values.push(if let Some(prefix) = prefix {
|
|
@@ -63,9 +63,9 @@ fn embedded(
|
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
65
|
let value_type = if cfg!(feature = "compression") {
|
|
66
|
-
quote! { fn() ->
|
|
66
|
+
quote! { fn() -> #crate_path::EmbeddedFile }
|
|
67
67
|
} else {
|
|
68
|
-
quote! {
|
|
68
|
+
quote! { #crate_path::EmbeddedFile }
|
|
69
69
|
};
|
|
70
70
|
let get_value = if cfg!(feature = "compression") {
|
|
71
71
|
quote! {|idx| (ENTRIES[idx].1)()}
|
|
@@ -76,7 +76,7 @@ fn embedded(
|
|
|
76
76
|
#not_debug_attr
|
|
77
77
|
impl #ident {
|
|
78
78
|
/// Get an embedded file and its metadata.
|
|
79
|
-
pub fn get(file_path: &str) -> ::std::option::Option<
|
|
79
|
+
pub fn get(file_path: &str) -> ::std::option::Option<#crate_path::EmbeddedFile> {
|
|
80
80
|
#handle_prefix
|
|
81
81
|
let key = file_path.replace("\\", "/");
|
|
82
82
|
const ENTRIES: &'static [(&'static str, #value_type)] = &[
|
|
@@ -98,18 +98,20 @@ fn embedded(
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
#not_debug_attr
|
|
101
|
-
impl
|
|
101
|
+
impl #crate_path::RustEmbed for #ident {
|
|
102
|
-
fn get(file_path: &str) -> ::std::option::Option<
|
|
102
|
+
fn get(file_path: &str) -> ::std::option::Option<#crate_path::EmbeddedFile> {
|
|
103
103
|
#ident::get(file_path)
|
|
104
104
|
}
|
|
105
|
-
fn iter() ->
|
|
105
|
+
fn iter() -> #crate_path::Filenames {
|
|
106
|
-
|
|
106
|
+
#crate_path::Filenames::Embedded(#ident::names())
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
})
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
fn dynamic(
|
|
112
|
-
|
|
113
|
+
ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includes: &[String], excludes: &[String], metadata_only: bool, crate_path: &syn::Path,
|
|
114
|
+
) -> TokenStream2 {
|
|
113
115
|
let (handle_prefix, map_iter) = if let ::std::option::Option::Some(prefix) = prefix {
|
|
114
116
|
(
|
|
115
117
|
quote! { let file_path = file_path.strip_prefix(#prefix)?; },
|
|
@@ -141,14 +143,14 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
|
|
|
141
143
|
impl #ident {
|
|
142
144
|
|
|
143
145
|
|
|
144
|
-
fn matcher() -> ::
|
|
146
|
+
fn matcher() -> #crate_path::utils::PathMatcher {
|
|
145
147
|
#declare_includes
|
|
146
148
|
#declare_excludes
|
|
147
|
-
static PATH_MATCHER: ::std::sync::OnceLock<::
|
|
149
|
+
static PATH_MATCHER: ::std::sync::OnceLock<#crate_path::utils::PathMatcher> = ::std::sync::OnceLock::new();
|
|
148
|
-
PATH_MATCHER.get_or_init(||
|
|
150
|
+
PATH_MATCHER.get_or_init(|| #crate_path::utils::PathMatcher::new(INCLUDES, EXCLUDES)).clone()
|
|
149
151
|
}
|
|
150
152
|
/// Get an embedded file and its metadata.
|
|
151
|
-
pub fn get(file_path: &str) -> ::std::option::Option<
|
|
153
|
+
pub fn get(file_path: &str) -> ::std::option::Option<#crate_path::EmbeddedFile> {
|
|
152
154
|
#handle_prefix
|
|
153
155
|
|
|
154
156
|
let rel_file_path = file_path.replace("\\", "/");
|
|
@@ -171,7 +173,7 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
|
|
|
171
173
|
}
|
|
172
174
|
let path_matcher = Self::matcher();
|
|
173
175
|
if path_matcher.is_path_included(&rel_file_path) {
|
|
174
|
-
|
|
176
|
+
#crate_path::utils::read_file_from_fs(&canonical_file_path).ok() #strip_contents
|
|
175
177
|
} else {
|
|
176
178
|
::std::option::Option::None
|
|
177
179
|
}
|
|
@@ -182,19 +184,19 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
|
|
|
182
184
|
use ::std::path::Path;
|
|
183
185
|
|
|
184
186
|
|
|
185
|
-
|
|
187
|
+
#crate_path::utils::get_files(::std::string::String::from(#folder_path), Self::matcher())
|
|
186
188
|
.map(|e| #map_iter)
|
|
187
189
|
}
|
|
188
190
|
}
|
|
189
191
|
|
|
190
192
|
#[cfg(debug_assertions)]
|
|
191
|
-
impl
|
|
193
|
+
impl #crate_path::RustEmbed for #ident {
|
|
192
|
-
fn get(file_path: &str) -> ::std::option::Option<
|
|
194
|
+
fn get(file_path: &str) -> ::std::option::Option<#crate_path::EmbeddedFile> {
|
|
193
195
|
#ident::get(file_path)
|
|
194
196
|
}
|
|
195
|
-
fn iter() ->
|
|
197
|
+
fn iter() -> #crate_path::Filenames {
|
|
196
198
|
// the return type of iter() is unnamable, so we have to box it
|
|
197
|
-
|
|
199
|
+
#crate_path::Filenames::Dynamic(::std::boxed::Box::new(#ident::iter()))
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
}
|
|
@@ -202,7 +204,7 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
|
|
|
202
204
|
|
|
203
205
|
fn generate_assets(
|
|
204
206
|
ident: &syn::Ident, relative_folder_path: Option<&str>, absolute_folder_path: String, prefix: Option<String>, includes: Vec<String>, excludes: Vec<String>,
|
|
205
|
-
metadata_only: bool,
|
|
207
|
+
metadata_only: bool, crate_path: &syn::Path,
|
|
206
208
|
) -> syn::Result<TokenStream2> {
|
|
207
209
|
let embedded_impl = embedded(
|
|
208
210
|
ident,
|
|
@@ -212,12 +214,13 @@ fn generate_assets(
|
|
|
212
214
|
&includes,
|
|
213
215
|
&excludes,
|
|
214
216
|
metadata_only,
|
|
217
|
+
crate_path,
|
|
215
218
|
);
|
|
216
219
|
if cfg!(feature = "debug-embed") {
|
|
217
220
|
return embedded_impl;
|
|
218
221
|
}
|
|
219
222
|
let embedded_impl = embedded_impl?;
|
|
220
|
-
let dynamic_impl = dynamic(ident, absolute_folder_path, prefix.as_deref(), &includes, &excludes, metadata_only);
|
|
223
|
+
let dynamic_impl = dynamic(ident, absolute_folder_path, prefix.as_deref(), &includes, &excludes, metadata_only, crate_path);
|
|
221
224
|
|
|
222
225
|
Ok(quote! {
|
|
223
226
|
#embedded_impl
|
|
@@ -225,7 +228,9 @@ fn generate_assets(
|
|
|
225
228
|
})
|
|
226
229
|
}
|
|
227
230
|
|
|
231
|
+
fn embed_file(
|
|
228
|
-
|
|
232
|
+
folder_path: Option<&str>, ident: &syn::Ident, rel_path: &str, full_canonical_path: &str, metadata_only: bool, crate_path: &syn::Path,
|
|
233
|
+
) -> syn::Result<TokenStream2> {
|
|
229
234
|
let file = rust_embed_utils::read_file_from_fs(Path::new(full_canonical_path)).expect("File should be readable");
|
|
230
235
|
let hash = file.metadata.sha256_hash();
|
|
231
236
|
let last_modified = match file.metadata.last_modified() {
|
|
@@ -254,7 +259,7 @@ fn embed_file(folder_path: Option<&str>, ident: &syn::Ident, rel_path: &str, ful
|
|
|
254
259
|
let full_relative_path = PathBuf::from_iter([folder_path, rel_path]);
|
|
255
260
|
let full_relative_path = full_relative_path.to_string_lossy();
|
|
256
261
|
quote! {
|
|
257
|
-
|
|
262
|
+
#crate_path::flate!(static BYTES: [u8] from #full_relative_path);
|
|
258
263
|
}
|
|
259
264
|
} else {
|
|
260
265
|
quote! {
|
|
@@ -270,9 +275,9 @@ fn embed_file(folder_path: Option<&str>, ident: &syn::Ident, rel_path: &str, ful
|
|
|
270
275
|
#closure_args {
|
|
271
276
|
#embedding_code
|
|
272
277
|
|
|
273
|
-
|
|
278
|
+
#crate_path::EmbeddedFile {
|
|
274
279
|
data: ::std::borrow::Cow::Borrowed(&BYTES),
|
|
275
|
-
metadata:
|
|
280
|
+
metadata: #crate_path::Metadata::__rust_embed_new([#(#hash),*], #last_modified, #created #mimetype_tokens)
|
|
276
281
|
}
|
|
277
282
|
}
|
|
278
283
|
})
|
|
@@ -317,6 +322,11 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> syn::Result<TokenStream2> {
|
|
|
317
322
|
_ => return Err(syn::Error::new_spanned(ast, "RustEmbed can only be derived for unit structs")),
|
|
318
323
|
};
|
|
319
324
|
|
|
325
|
+
let crate_path: syn::Path = find_attribute_values(ast, "crate_path")
|
|
326
|
+
.last()
|
|
327
|
+
.map(|v| syn::parse_str(&v).unwrap())
|
|
328
|
+
.unwrap_or_else(|| syn::parse_str("rust_embed").unwrap());
|
|
329
|
+
|
|
320
330
|
let mut folder_paths = find_attribute_values(ast, "folder");
|
|
321
331
|
if folder_paths.len() != 1 {
|
|
322
332
|
return Err(syn::Error::new_spanned(
|
|
@@ -384,10 +394,11 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> syn::Result<TokenStream2> {
|
|
|
384
394
|
includes,
|
|
385
395
|
excludes,
|
|
386
396
|
metadata_only,
|
|
397
|
+
&crate_path,
|
|
387
398
|
)
|
|
388
399
|
}
|
|
389
400
|
|
|
390
|
-
#[proc_macro_derive(RustEmbed, attributes(folder, prefix, include, exclude, metadata_only))]
|
|
401
|
+
#[proc_macro_derive(RustEmbed, attributes(folder, prefix, include, exclude, metadata_only, crate_path))]
|
|
391
402
|
pub fn derive_input_object(input: TokenStream) -> TokenStream {
|
|
392
403
|
let ast = parse_macro_input!(input as DeriveInput);
|
|
393
404
|
match impl_rust_embed(&ast) {
|
tests/custom_crate_path.rs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// This test checks that the `crate_path` attribute can be used
|
|
2
|
+
/// to specify a custom path to the `rust_embed` crate.
|
|
3
|
+
|
|
4
|
+
mod custom {
|
|
5
|
+
pub mod path {
|
|
6
|
+
pub use rust_embed;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// We introduce a 'rust_embed' module here to break compilation in case
|
|
11
|
+
// the `rust_embed` crate is not loaded correctly.
|
|
12
|
+
//
|
|
13
|
+
// To test this, try commenting out the attribute which specifies the
|
|
14
|
+
// the custom crate path -- you should find that the test fails to compile.
|
|
15
|
+
mod rust_embed {}
|
|
16
|
+
|
|
17
|
+
#[derive(custom::path::rust_embed::RustEmbed)]
|
|
18
|
+
#[crate_path = "custom::path::rust_embed"]
|
|
19
|
+
#[folder = "examples/public/"]
|
|
20
|
+
struct Asset;
|