Skip to content

Commit

Permalink
Merge pull request #232 from Wulf/wulf/custom-crate-path
Browse files Browse the repository at this point in the history
Allow users to specify a custom path to the rust_embed crate in generated code
  • Loading branch information
pyrossh authored May 23, 2024
2 parents 0745567 + e1f172e commit 7ec554e
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 27 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ jobs:
cargo test --test mime_guess --features "mime-guess" --release
cargo test --test interpolated_path --features "interpolate-folder-path"
cargo test --test interpolated_path --features "interpolate-folder-path" --release
cargo test --test custom_crate_path
cargo test --test custom_crate_path --release
cargo build --example basic
cargo build --example rocket --features rocket
cargo build --example actix --features actix
Expand Down
65 changes: 38 additions & 27 deletions impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprLit, Fields, Lit, Meta

fn embedded(
ident: &syn::Ident, relative_folder_path: Option<&str>, absolute_folder_path: String, prefix: Option<&str>, includes: &[String], excludes: &[String],
metadata_only: bool,
metadata_only: bool, crate_path: &syn::Path,
) -> syn::Result<TokenStream2> {
extern crate rust_embed_utils;

Expand All @@ -30,7 +30,7 @@ fn embedded(
for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(absolute_folder_path.clone(), matcher) {
match_values.insert(
rel_path.clone(),
embed_file(relative_folder_path, ident, &rel_path, &full_canonical_path, metadata_only)?,
embed_file(relative_folder_path, ident, &rel_path, &full_canonical_path, metadata_only, crate_path)?,
);

list_values.push(if let Some(prefix) = prefix {
Expand Down Expand Up @@ -63,9 +63,9 @@ fn embedded(
}
});
let value_type = if cfg!(feature = "compression") {
quote! { fn() -> rust_embed::EmbeddedFile }
quote! { fn() -> #crate_path::EmbeddedFile }
} else {
quote! { rust_embed::EmbeddedFile }
quote! { #crate_path::EmbeddedFile }
};
let get_value = if cfg!(feature = "compression") {
quote! {|idx| (ENTRIES[idx].1)()}
Expand All @@ -76,7 +76,7 @@ fn embedded(
#not_debug_attr
impl #ident {
/// Get an embedded file and its metadata.
pub fn get(file_path: &str) -> ::std::option::Option<rust_embed::EmbeddedFile> {
pub fn get(file_path: &str) -> ::std::option::Option<#crate_path::EmbeddedFile> {
#handle_prefix
let key = file_path.replace("\\", "/");
const ENTRIES: &'static [(&'static str, #value_type)] = &[
Expand All @@ -98,18 +98,20 @@ fn embedded(
}

#not_debug_attr
impl rust_embed::RustEmbed for #ident {
fn get(file_path: &str) -> ::std::option::Option<rust_embed::EmbeddedFile> {
impl #crate_path::RustEmbed for #ident {
fn get(file_path: &str) -> ::std::option::Option<#crate_path::EmbeddedFile> {
#ident::get(file_path)
}
fn iter() -> rust_embed::Filenames {
rust_embed::Filenames::Embedded(#ident::names())
fn iter() -> #crate_path::Filenames {
#crate_path::Filenames::Embedded(#ident::names())
}
}
})
}

fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includes: &[String], excludes: &[String], metadata_only: bool) -> TokenStream2 {
fn dynamic(
ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includes: &[String], excludes: &[String], metadata_only: bool, crate_path: &syn::Path,
) -> TokenStream2 {
let (handle_prefix, map_iter) = if let ::std::option::Option::Some(prefix) = prefix {
(
quote! { let file_path = file_path.strip_prefix(#prefix)?; },
Expand Down Expand Up @@ -141,14 +143,14 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
impl #ident {


fn matcher() -> ::rust_embed::utils::PathMatcher {
fn matcher() -> #crate_path::utils::PathMatcher {
#declare_includes
#declare_excludes
static PATH_MATCHER: ::std::sync::OnceLock<::rust_embed::utils::PathMatcher> = ::std::sync::OnceLock::new();
PATH_MATCHER.get_or_init(|| rust_embed::utils::PathMatcher::new(INCLUDES, EXCLUDES)).clone()
static PATH_MATCHER: ::std::sync::OnceLock<#crate_path::utils::PathMatcher> = ::std::sync::OnceLock::new();
PATH_MATCHER.get_or_init(|| #crate_path::utils::PathMatcher::new(INCLUDES, EXCLUDES)).clone()
}
/// Get an embedded file and its metadata.
pub fn get(file_path: &str) -> ::std::option::Option<rust_embed::EmbeddedFile> {
pub fn get(file_path: &str) -> ::std::option::Option<#crate_path::EmbeddedFile> {
#handle_prefix

let rel_file_path = file_path.replace("\\", "/");
Expand All @@ -171,7 +173,7 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
}
let path_matcher = Self::matcher();
if path_matcher.is_path_included(&rel_file_path) {
rust_embed::utils::read_file_from_fs(&canonical_file_path).ok() #strip_contents
#crate_path::utils::read_file_from_fs(&canonical_file_path).ok() #strip_contents
} else {
::std::option::Option::None
}
Expand All @@ -182,27 +184,27 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
use ::std::path::Path;


rust_embed::utils::get_files(::std::string::String::from(#folder_path), Self::matcher())
#crate_path::utils::get_files(::std::string::String::from(#folder_path), Self::matcher())
.map(|e| #map_iter)
}
}

#[cfg(debug_assertions)]
impl rust_embed::RustEmbed for #ident {
fn get(file_path: &str) -> ::std::option::Option<rust_embed::EmbeddedFile> {
impl #crate_path::RustEmbed for #ident {
fn get(file_path: &str) -> ::std::option::Option<#crate_path::EmbeddedFile> {
#ident::get(file_path)
}
fn iter() -> rust_embed::Filenames {
fn iter() -> #crate_path::Filenames {
// the return type of iter() is unnamable, so we have to box it
rust_embed::Filenames::Dynamic(::std::boxed::Box::new(#ident::iter()))
#crate_path::Filenames::Dynamic(::std::boxed::Box::new(#ident::iter()))
}
}
}
}

fn generate_assets(
ident: &syn::Ident, relative_folder_path: Option<&str>, absolute_folder_path: String, prefix: Option<String>, includes: Vec<String>, excludes: Vec<String>,
metadata_only: bool,
metadata_only: bool, crate_path: &syn::Path,
) -> syn::Result<TokenStream2> {
let embedded_impl = embedded(
ident,
Expand All @@ -212,20 +214,23 @@ fn generate_assets(
&includes,
&excludes,
metadata_only,
crate_path,
);
if cfg!(feature = "debug-embed") {
return embedded_impl;
}
let embedded_impl = embedded_impl?;
let dynamic_impl = dynamic(ident, absolute_folder_path, prefix.as_deref(), &includes, &excludes, metadata_only);
let dynamic_impl = dynamic(ident, absolute_folder_path, prefix.as_deref(), &includes, &excludes, metadata_only, crate_path);

Ok(quote! {
#embedded_impl
#dynamic_impl
})
}

fn embed_file(folder_path: Option<&str>, ident: &syn::Ident, rel_path: &str, full_canonical_path: &str, metadata_only: bool) -> syn::Result<TokenStream2> {
fn embed_file(
folder_path: Option<&str>, ident: &syn::Ident, rel_path: &str, full_canonical_path: &str, metadata_only: bool, crate_path: &syn::Path,
) -> syn::Result<TokenStream2> {
let file = rust_embed_utils::read_file_from_fs(Path::new(full_canonical_path)).expect("File should be readable");
let hash = file.metadata.sha256_hash();
let last_modified = match file.metadata.last_modified() {
Expand Down Expand Up @@ -254,7 +259,7 @@ fn embed_file(folder_path: Option<&str>, ident: &syn::Ident, rel_path: &str, ful
let full_relative_path = PathBuf::from_iter([folder_path, rel_path]);
let full_relative_path = full_relative_path.to_string_lossy();
quote! {
rust_embed::flate!(static BYTES: [u8] from #full_relative_path);
#crate_path::flate!(static BYTES: [u8] from #full_relative_path);
}
} else {
quote! {
Expand All @@ -270,9 +275,9 @@ fn embed_file(folder_path: Option<&str>, ident: &syn::Ident, rel_path: &str, ful
#closure_args {
#embedding_code

rust_embed::EmbeddedFile {
#crate_path::EmbeddedFile {
data: ::std::borrow::Cow::Borrowed(&BYTES),
metadata: rust_embed::Metadata::__rust_embed_new([#(#hash),*], #last_modified, #created #mimetype_tokens)
metadata: #crate_path::Metadata::__rust_embed_new([#(#hash),*], #last_modified, #created #mimetype_tokens)
}
}
})
Expand Down Expand Up @@ -317,6 +322,11 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> syn::Result<TokenStream2> {
_ => return Err(syn::Error::new_spanned(ast, "RustEmbed can only be derived for unit structs")),
};

let crate_path: syn::Path = find_attribute_values(ast, "crate_path")
.last()
.map(|v| syn::parse_str(&v).unwrap())
.unwrap_or_else(|| syn::parse_str("rust_embed").unwrap());

let mut folder_paths = find_attribute_values(ast, "folder");
if folder_paths.len() != 1 {
return Err(syn::Error::new_spanned(
Expand Down Expand Up @@ -384,10 +394,11 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> syn::Result<TokenStream2> {
includes,
excludes,
metadata_only,
&crate_path,
)
}

#[proc_macro_derive(RustEmbed, attributes(folder, prefix, include, exclude, metadata_only))]
#[proc_macro_derive(RustEmbed, attributes(folder, prefix, include, exclude, metadata_only, crate_path))]
pub fn derive_input_object(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
match impl_rust_embed(&ast) {
Expand Down
20 changes: 20 additions & 0 deletions tests/custom_crate_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/// This test checks that the `crate_path` attribute can be used
/// to specify a custom path to the `rust_embed` crate.

mod custom {
pub mod path {
pub use rust_embed;
}
}

// We introduce a 'rust_embed' module here to break compilation in case
// the `rust_embed` crate is not loaded correctly.
//
// To test this, try commenting out the attribute which specifies the
// the custom crate path -- you should find that the test fails to compile.
mod rust_embed {}

#[derive(custom::path::rust_embed::RustEmbed)]
#[crate_path = "custom::path::rust_embed"]
#[folder = "examples/public/"]
struct Asset;

0 comments on commit 7ec554e

Please sign in to comment.