diff --git a/impl/src/lib.rs b/impl/src/lib.rs index 2470c74..49b0f8f 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -10,6 +10,7 @@ use rust_embed_utils::PathMatcher; use std::{ collections::BTreeMap, env, + io::ErrorKind, iter::FromIterator, path::{Path, PathBuf}, }; @@ -135,7 +136,14 @@ fn dynamic( .map(|mut file| { file.data = ::std::default::Default::default(); file }) }); - let canonical_folder_path = Path::new(&folder_path).canonicalize().expect("folder path must resolve to an absolute path"); + let non_canonical_folder_path = Path::new(&folder_path); + let canonical_folder_path = non_canonical_folder_path + .canonicalize() + .or_else(|err| match err { + err if err.kind() == ErrorKind::NotFound => Ok(non_canonical_folder_path.to_owned()), + err => Err(err), + }) + .expect("folder path must resolve to an absolute path"); let canonical_folder_path = canonical_folder_path.to_str().expect("absolute folder path must be valid unicode"); quote! { @@ -339,6 +347,7 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> syn::Result { let includes = find_attribute_values(ast, "include"); let excludes = find_attribute_values(ast, "exclude"); let metadata_only = find_bool_attribute(ast, "metadata_only").unwrap_or(false); + let allow_missing = find_bool_attribute(ast, "allow_missing").unwrap_or(false); #[cfg(not(feature = "include-exclude"))] if !includes.is_empty() || !excludes.is_empty() { @@ -368,7 +377,7 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> syn::Result { (None, folder_path) }; - if !Path::new(&absolute_folder_path).exists() { + if !Path::new(&absolute_folder_path).exists() && !allow_missing { let mut message = format!( "#[derive(RustEmbed)] folder '{}' does not exist. cwd: '{}'", absolute_folder_path, @@ -397,7 +406,7 @@ fn impl_rust_embed(ast: &syn::DeriveInput) -> syn::Result { ) } -#[proc_macro_derive(RustEmbed, attributes(folder, prefix, include, exclude, metadata_only, crate_path))] +#[proc_macro_derive(RustEmbed, attributes(folder, prefix, include, exclude, allow_missing, 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) { diff --git a/readme.md b/readme.md index b90a7af..6d0bed4 100644 --- a/readme.md +++ b/readme.md @@ -93,6 +93,11 @@ be included in the file paths returned by `iter`. You can add `#[metadata_only = true]` to the `RustEmbed` struct to exclude file contents from the binary. Only file paths and metadata will be embedded. +### `allow_missing` + +You can add `#[allow_missing = true]` to the `RustEmbed` struct to allow the embedded folder to be missing. +In that case, RustEmbed will be empty. + ## Features ### `debug-embed` diff --git a/tests/allow_missing.rs b/tests/allow_missing.rs new file mode 100644 index 0000000..39bcd57 --- /dev/null +++ b/tests/allow_missing.rs @@ -0,0 +1,15 @@ +use std::{path::PathBuf, str::FromStr}; + +use rust_embed::Embed; + +#[derive(Embed)] +#[folder = "examples/missing/"] +#[allow_missing = true] +struct Asset; + +#[test] +fn missing_is_empty() { + let path = PathBuf::from_str("./examples/missing").unwrap(); + assert!(!path.exists()); + assert_eq!(Asset::iter().count(), 0); +}