~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.
207d1be6
—
Peter John 2 years ago
Merge pull request #220 from wyatt-herkamp/master
- impl/Cargo.toml +2 -2
- impl/src/lib.rs +38 -24
- rustfmt.toml +1 -1
impl/Cargo.toml
CHANGED
|
@@ -17,13 +17,13 @@ proc-macro = true
|
|
|
17
17
|
[dependencies]
|
|
18
18
|
rust-embed-utils = { version = "8.0.0", path = "../utils"}
|
|
19
19
|
|
|
20
|
-
syn = { version = "2", default-features = false, features = ["derive", "parsing", "proc-macro"] }
|
|
20
|
+
syn = { version = "2", default-features = false, features = ["derive", "parsing", "proc-macro", "printing"] }
|
|
21
21
|
quote = "1"
|
|
22
22
|
proc-macro2 = "1"
|
|
23
23
|
walkdir = "2.3.1"
|
|
24
24
|
|
|
25
25
|
[dependencies.shellexpand]
|
|
26
|
-
version = "
|
|
26
|
+
version = "3"
|
|
27
27
|
optional = true
|
|
28
28
|
|
|
29
29
|
[features]
|
impl/src/lib.rs
CHANGED
|
@@ -12,11 +12,11 @@ use std::{
|
|
|
12
12
|
iter::FromIterator,
|
|
13
13
|
path::{Path, PathBuf},
|
|
14
14
|
};
|
|
15
|
-
use syn::{Data, DeriveInput, Expr, ExprLit, Fields, Lit, Meta, MetaNameValue};
|
|
15
|
+
use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprLit, Fields, Lit, Meta, MetaNameValue};
|
|
16
16
|
|
|
17
17
|
fn embedded(
|
|
18
18
|
ident: &syn::Ident, relative_folder_path: Option<&str>, absolute_folder_path: String, prefix: Option<&str>, includes: &[String], excludes: &[String],
|
|
19
|
-
) -> TokenStream2 {
|
|
19
|
+
) -> syn::Result<TokenStream2> {
|
|
20
20
|
extern crate rust_embed_utils;
|
|
21
21
|
|
|
22
22
|
let mut match_values = BTreeMap::new();
|
|
@@ -25,7 +25,10 @@ fn embedded(
|
|
|
25
25
|
let includes: Vec<&str> = includes.iter().map(AsRef::as_ref).collect();
|
|
26
26
|
let excludes: Vec<&str> = excludes.iter().map(AsRef::as_ref).collect();
|
|
27
27
|
for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(absolute_folder_path.clone(), &includes, &excludes) {
|
|
28
|
+
match_values.insert(
|
|
29
|
+
rel_path.clone(),
|
|
28
|
-
|
|
30
|
+
embed_file(relative_folder_path.clone(), ident, &rel_path, &full_canonical_path)?,
|
|
31
|
+
);
|
|
29
32
|
|
|
30
33
|
list_values.push(if let Some(prefix) = prefix {
|
|
31
34
|
format!("{}{}", prefix, rel_path)
|
|
@@ -66,7 +69,7 @@ fn embedded(
|
|
|
66
69
|
} else {
|
|
67
70
|
quote! {|idx| ENTRIES[idx].1.clone()}
|
|
68
71
|
};
|
|
69
|
-
quote! {
|
|
72
|
+
Ok(quote! {
|
|
70
73
|
#not_debug_attr
|
|
71
74
|
impl #ident {
|
|
72
75
|
/// Get an embedded file and its metadata.
|
|
@@ -100,7 +103,7 @@ fn embedded(
|
|
|
100
103
|
rust_embed::Filenames::Embedded(#ident::names())
|
|
101
104
|
}
|
|
102
105
|
}
|
|
103
|
-
}
|
|
106
|
+
})
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includes: &[String], excludes: &[String]) -> TokenStream2 {
|
|
@@ -178,7 +181,7 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
|
|
|
178
181
|
|
|
179
182
|
fn generate_assets(
|
|
180
183
|
ident: &syn::Ident, relative_folder_path: Option<&str>, absolute_folder_path: String, prefix: Option<String>, includes: Vec<String>, excludes: Vec<String>,
|
|
181
|
-
) -> TokenStream2 {
|
|
184
|
+
) -> syn::Result<TokenStream2> {
|
|
182
185
|
let embedded_impl = embedded(
|
|
183
186
|
ident,
|
|
184
187
|
relative_folder_path,
|
|
@@ -190,16 +193,16 @@ fn generate_assets(
|
|
|
190
193
|
if cfg!(feature = "debug-embed") {
|
|
191
194
|
return embedded_impl;
|
|
192
195
|
}
|
|
193
|
-
|
|
196
|
+
let embedded_impl = embedded_impl?;
|
|
194
197
|
let dynamic_impl = dynamic(ident, absolute_folder_path, prefix.as_deref(), &includes, &excludes);
|
|
195
198
|
|
|
196
|
-
quote! {
|
|
199
|
+
Ok(quote! {
|
|
197
200
|
#embedded_impl
|
|
198
201
|
#dynamic_impl
|
|
199
|
-
}
|
|
202
|
+
})
|
|
200
203
|
}
|
|
201
204
|
|
|
202
|
-
fn embed_file(folder_path: Option<&str>, rel_path: &str, full_canonical_path: &str) -> TokenStream2 {
|
|
205
|
+
fn embed_file(folder_path: Option<&str>, ident: &syn::Ident, rel_path: &str, full_canonical_path: &str) -> syn::Result<TokenStream2> {
|
|
203
206
|
let file = rust_embed_utils::read_file_from_fs(Path::new(full_canonical_path)).expect("File should be readable");
|
|
204
207
|
let hash = file.metadata.sha256_hash();
|
|
205
208
|
let last_modified = match file.metadata.last_modified() {
|
|
@@ -215,8 +218,9 @@ fn embed_file(folder_path: Option<&str>, rel_path: &str, full_canonical_path: &s
|
|
|
215
218
|
let mimetype_tokens = TokenStream2::new();
|
|
216
219
|
|
|
217
220
|
let embedding_code = if cfg!(feature = "compression") {
|
|
221
|
+
let folder_path = folder_path.ok_or(syn::Error::new(ident.span(), "`folder` must be provided under `compression` feature."))?;
|
|
218
222
|
// Print some debugging information
|
|
219
|
-
let full_relative_path = PathBuf::from_iter([folder_path
|
|
223
|
+
let full_relative_path = PathBuf::from_iter([folder_path, rel_path]);
|
|
220
224
|
let full_relative_path = full_relative_path.to_string_lossy();
|
|
221
225
|
quote! {
|
|
222
226
|
rust_embed::flate!(static BYTES: [u8] from #full_relative_path);
|
|
@@ -231,7 +235,7 @@ fn embed_file(folder_path: Option<&str>, rel_path: &str, full_canonical_path: &s
|
|
|
231
235
|
} else {
|
|
232
236
|
quote! {}
|
|
233
237
|
};
|
|
234
|
-
quote! {
|
|
238
|
+
Ok(quote! {
|
|
235
239
|
#closure_args {
|
|
236
240
|
#embedding_code
|
|
237
241
|
|
|
@@ -240,7 +244,7 @@ fn embed_file(folder_path: Option<&str>, rel_path: &str, full_canonical_path: &s
|
|
|
240
244
|
metadata: rust_embed::Metadata::__rust_embed_new([#(#hash),*], #last_modified #mimetype_tokens)
|
|
241
245
|
}
|
|
242
246
|
}
|
|
243
|
-
}
|
|
247
|
+
})
|
|
244
248
|
}
|
|
245
249
|
|
|
246
250
|
/// Find all pairs of the `name = "value"` attribute from the derive input
|
|
@@ -259,18 +263,21 @@ fn find_attribute_values(ast: &syn::DeriveInput, attr_name: &str) -> Vec<String>
|
|
|
259
263
|
.collect()
|
|
260
264
|
}
|
|
261
265
|
|
|
262
|
-
fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
|
|
266
|
+
fn impl_rust_embed(ast: &syn::DeriveInput) -> syn::Result<TokenStream2> {
|
|
263
267
|
match ast.data {
|
|
264
268
|
Data::Struct(ref data) => match data.fields {
|
|
265
269
|
Fields::Unit => {}
|
|
266
|
-
_ =>
|
|
270
|
+
_ => return Err(syn::Error::new_spanned(ast, "RustEmbed can only be derived for unit structs")),
|
|
267
271
|
},
|
|
268
|
-
_ =>
|
|
272
|
+
_ => return Err(syn::Error::new_spanned(ast, "RustEmbed can only be derived for unit structs")),
|
|
269
273
|
};
|
|
270
274
|
|
|
271
275
|
let mut folder_paths = find_attribute_values(ast, "folder");
|
|
272
276
|
if folder_paths.len() != 1 {
|
|
277
|
+
return Err(syn::Error::new_spanned(
|
|
278
|
+
ast,
|
|
273
|
-
|
|
279
|
+
"#[derive(RustEmbed)] must contain one attribute like this #[folder = \"examples/public/\"]",
|
|
280
|
+
));
|
|
274
281
|
}
|
|
275
282
|
let folder_path = folder_paths.remove(0);
|
|
276
283
|
|
|
@@ -280,11 +287,16 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
|
|
|
280
287
|
|
|
281
288
|
#[cfg(not(feature = "include-exclude"))]
|
|
282
289
|
if !includes.is_empty() || !excludes.is_empty() {
|
|
290
|
+
return Err(syn::Error::new_spanned(
|
|
291
|
+
ast,
|
|
283
|
-
|
|
292
|
+
"Please turn on the `include-exclude` feature to use the `include` and `exclude` attributes",
|
|
293
|
+
));
|
|
284
294
|
}
|
|
285
295
|
|
|
286
296
|
#[cfg(feature = "interpolate-folder-path")]
|
|
287
|
-
let folder_path = shellexpand::full(&folder_path)
|
|
297
|
+
let folder_path = shellexpand::full(&folder_path)
|
|
298
|
+
.map_err(|v| syn::Error::new_spanned(ast, v.to_string()))?
|
|
299
|
+
.to_string();
|
|
288
300
|
|
|
289
301
|
// Base relative paths on the Cargo.toml location
|
|
290
302
|
let (relative_path, absolute_folder_path) = if Path::new(&folder_path).is_relative() {
|
|
@@ -296,7 +308,7 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
|
|
|
296
308
|
(Some(folder_path.clone()), absolute_path)
|
|
297
309
|
} else {
|
|
298
310
|
if cfg!(feature = "compression") {
|
|
299
|
-
|
|
311
|
+
return Err(syn::Error::new_spanned(ast, "`folder` must be a relative path under `compression` feature."));
|
|
300
312
|
}
|
|
301
313
|
(None, folder_path)
|
|
302
314
|
};
|
|
@@ -315,7 +327,7 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
|
|
|
315
327
|
when the `interpolate-folder-path` feature is enabled.";
|
|
316
328
|
}
|
|
317
329
|
|
|
318
|
-
|
|
330
|
+
return Err(syn::Error::new_spanned(ast, message));
|
|
319
331
|
};
|
|
320
332
|
|
|
321
333
|
generate_assets(&ast.ident, relative_path.as_deref(), absolute_folder_path, prefix, includes, excludes)
|
|
@@ -323,7 +335,9 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
|
|
|
323
335
|
|
|
324
336
|
#[proc_macro_derive(RustEmbed, attributes(folder, prefix, include, exclude))]
|
|
325
337
|
pub fn derive_input_object(input: TokenStream) -> TokenStream {
|
|
326
|
-
let ast
|
|
338
|
+
let ast = parse_macro_input!(input as DeriveInput);
|
|
327
|
-
|
|
339
|
+
match impl_rust_embed(&ast) {
|
|
328
|
-
|
|
340
|
+
Ok(ok) => ok.into(),
|
|
341
|
+
Err(e) => e.to_compile_error().into(),
|
|
342
|
+
}
|
|
329
343
|
}
|
rustfmt.toml
CHANGED
|
@@ -2,7 +2,7 @@ unstable_features = false
|
|
|
2
2
|
wrap_comments = true
|
|
3
3
|
normalize_comments = true
|
|
4
4
|
merge_derives = true
|
|
5
|
-
|
|
5
|
+
fn_params_layout = "Compressed"
|
|
6
6
|
max_width = 160
|
|
7
7
|
tab_spaces = 2
|
|
8
8
|
indent_style = "Block"
|