~repos /rust-embed

#rust#proc-macro#http

git clone https://pyrossh.dev/repos/rust-embed.git
Discussions: https://groups.google.com/g/rust-embed-devs

rust macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.


Files changed (3) hide show
  1. impl/Cargo.toml +2 -2
  2. impl/src/lib.rs +38 -24
  3. 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 = "2"
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
- match_values.insert(rel_path.clone(), embed_file(relative_folder_path.clone(), &rel_path, &full_canonical_path));
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.expect("folder_path must be provided under `compression` feature"), rel_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
- _ => panic!("RustEmbed can only be derived for unit structs"),
270
+ _ => return Err(syn::Error::new_spanned(ast, "RustEmbed can only be derived for unit structs")),
267
271
  },
268
- _ => panic!("RustEmbed can only be derived for unit structs"),
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
- panic!("#[derive(RustEmbed)] must contain one attribute like this #[folder = \"examples/public/\"]");
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
- panic!("Please turn on the `include-exclude` feature to use the `include` and `exclude` attributes")
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).unwrap().to_string();
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
- panic!("`folder` must be a relative path under `compression` feature.")
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
- panic!("{}", message);
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: DeriveInput = syn::parse(input).unwrap();
338
+ let ast = parse_macro_input!(input as DeriveInput);
327
- let gen = impl_rust_embed(&ast);
339
+ match impl_rust_embed(&ast) {
328
- gen.into()
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
- fn_args_layout = "Compressed"
5
+ fn_params_layout = "Compressed"
6
6
  max_width = 160
7
7
  tab_spaces = 2
8
8
  indent_style = "Block"