~repos /rust-embed

#rust#proc-macro#http

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.


7a14d89d Wyatt Herkamp

2 years ago
Move Panics to Compile Errors, Update shellexpand, and remove deprecated feature from rustfmt.toml
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"