~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 (2) hide show
  1. impl/src/lib.rs +16 -11
  2. utils/src/lib.rs +49 -42
impl/src/lib.rs CHANGED
@@ -6,6 +6,7 @@ extern crate proc_macro;
6
6
 
7
7
  use proc_macro::TokenStream;
8
8
  use proc_macro2::TokenStream as TokenStream2;
9
+ use rust_embed_utils::PathMatcher;
9
10
  use std::{
10
11
  collections::BTreeMap,
11
12
  env,
@@ -25,7 +26,8 @@ fn embedded(
25
26
 
26
27
  let includes: Vec<&str> = includes.iter().map(AsRef::as_ref).collect();
27
28
  let excludes: Vec<&str> = excludes.iter().map(AsRef::as_ref).collect();
29
+ let matcher = PathMatcher::new(&includes, &excludes);
28
- for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(absolute_folder_path.clone(), &includes, &excludes) {
30
+ for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(absolute_folder_path.clone(), matcher) {
29
31
  match_values.insert(
30
32
  rel_path.clone(),
31
33
  embed_file(relative_folder_path, ident, &rel_path, &full_canonical_path, metadata_only)?,
@@ -125,8 +127,8 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
125
127
  const EXCLUDES: &[&str] = &[#(#excludes),*];
126
128
  };
127
129
 
128
- // In metadata_only mode, we still need to read file contents to generate the file hash, but
130
+ // In metadata_only mode, we still need to read file contents to generate the
129
- // then we drop the file data.
131
+ // file hash, but then we drop the file data.
130
132
  let strip_contents = metadata_only.then_some(quote! {
131
133
  .map(|mut file| { file.data = ::std::default::Default::default(); file })
132
134
  });
@@ -137,13 +139,18 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
137
139
  quote! {
138
140
  #[cfg(debug_assertions)]
139
141
  impl #ident {
142
+
143
+
144
+ fn matcher() -> ::rust_embed::utils::PathMatcher {
145
+ #declare_includes
146
+ #declare_excludes
147
+ static PATH_MATCHER: ::std::sync::OnceLock<::rust_embed::utils::PathMatcher> = ::std::sync::OnceLock::new();
148
+ PATH_MATCHER.get_or_init(|| rust_embed::utils::PathMatcher::new(INCLUDES, EXCLUDES)).clone()
149
+ }
140
150
  /// Get an embedded file and its metadata.
141
151
  pub fn get(file_path: &str) -> ::std::option::Option<rust_embed::EmbeddedFile> {
142
152
  #handle_prefix
143
153
 
144
- #declare_includes
145
- #declare_excludes
146
-
147
154
  let rel_file_path = file_path.replace("\\", "/");
148
155
  let file_path = ::std::path::Path::new(#folder_path).join(&rel_file_path);
149
156
 
@@ -162,8 +169,8 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
162
169
  return ::std::option::Option::None;
163
170
  }
164
171
  }
165
-
172
+ let path_matcher = Self::matcher();
166
- if rust_embed::utils::is_path_included(&rel_file_path, INCLUDES, EXCLUDES) {
173
+ if path_matcher.is_path_included(&rel_file_path) {
167
174
  rust_embed::utils::read_file_from_fs(&canonical_file_path).ok() #strip_contents
168
175
  } else {
169
176
  ::std::option::Option::None
@@ -174,10 +181,8 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
174
181
  pub fn iter() -> impl ::std::iter::Iterator<Item = ::std::borrow::Cow<'static, str>> {
175
182
  use ::std::path::Path;
176
183
 
177
- #declare_includes
178
- #declare_excludes
179
184
 
180
- rust_embed::utils::get_files(::std::string::String::from(#folder_path), INCLUDES, EXCLUDES)
185
+ rust_embed::utils::get_files(::std::string::String::from(#folder_path), Self::matcher())
181
186
  .map(|e| #map_iter)
182
187
  }
183
188
  }
utils/src/lib.rs CHANGED
@@ -12,47 +12,8 @@ pub struct FileEntry {
12
12
  pub full_canonical_path: String,
13
13
  }
14
14
 
15
- #[cfg(not(feature = "include-exclude"))]
16
- pub fn is_path_included(_path: &str, _includes: &[&str], _excludes: &[&str]) -> bool {
17
- true
18
- }
19
-
20
- #[cfg(feature = "include-exclude")]
21
- pub fn is_path_included(rel_path: &str, includes: &[&str], excludes: &[&str]) -> bool {
22
- use globset::Glob;
23
-
24
- // ignore path matched by exclusion pattern
25
- for exclude in excludes {
26
- let pattern = Glob::new(exclude)
27
- .unwrap_or_else(|_| panic!("invalid exclude pattern '{}'", exclude))
28
- .compile_matcher();
29
-
30
- if pattern.is_match(rel_path) {
31
- return false;
32
- }
33
- }
34
-
35
- // accept path if no includes provided
36
- if includes.is_empty() {
37
- return true;
38
- }
39
-
40
- // accept path if matched by inclusion pattern
41
- for include in includes {
42
- let pattern = Glob::new(include)
43
- .unwrap_or_else(|_| panic!("invalid include pattern '{}'", include))
44
- .compile_matcher();
45
-
46
- if pattern.is_match(rel_path) {
47
- return true;
48
- }
49
- }
50
-
51
- false
52
- }
53
-
54
15
  #[cfg_attr(all(debug_assertions, not(feature = "debug-embed")), allow(unused))]
55
- pub fn get_files<'patterns>(folder_path: String, includes: &'patterns [&str], excludes: &'patterns [&str]) -> impl Iterator<Item = FileEntry> + 'patterns {
16
+ pub fn get_files(folder_path: String, matcher: PathMatcher) -> impl Iterator<Item = FileEntry> {
56
17
  walkdir::WalkDir::new(&folder_path)
57
18
  .follow_links(true)
58
19
  .sort_by_file_name()
@@ -68,8 +29,7 @@ pub fn get_files<'patterns>(folder_path: String, includes: &'patterns [&str], ex
68
29
  } else {
69
30
  rel_path
70
31
  };
71
-
72
- if is_path_included(&rel_path, includes, excludes) {
32
+ if matcher.is_path_included(&rel_path) {
73
33
  Some(FileEntry { rel_path, full_canonical_path })
74
34
  } else {
75
35
  None
@@ -176,3 +136,50 @@ pub fn read_file_from_fs(file_path: &Path) -> io::Result<EmbeddedFile> {
176
136
  fn path_to_str<P: AsRef<std::path::Path>>(p: P) -> String {
177
137
  p.as_ref().to_str().expect("Path does not have a string representation").to_owned()
178
138
  }
139
+
140
+ #[derive(Clone)]
141
+ pub struct PathMatcher {
142
+ #[cfg(feature = "include-exclude")]
143
+ include_matcher: globset::GlobSet,
144
+ #[cfg(feature = "include-exclude")]
145
+ exclude_matcher: globset::GlobSet,
146
+ }
147
+
148
+ #[cfg(feature = "include-exclude")]
149
+ impl PathMatcher {
150
+ pub fn new(includes: &[&str], excludes: &[&str]) -> Self {
151
+ let mut include_matcher = globset::GlobSetBuilder::new();
152
+ for include in includes {
153
+ include_matcher.add(globset::Glob::new(include).unwrap_or_else(|_| panic!("invalid include pattern '{}'", include)));
154
+ }
155
+ let include_matcher = include_matcher
156
+ .build()
157
+ .unwrap_or_else(|_| panic!("Could not compile included patterns matcher"));
158
+
159
+ let mut exclude_matcher = globset::GlobSetBuilder::new();
160
+ for exclude in excludes {
161
+ exclude_matcher.add(globset::Glob::new(exclude).unwrap_or_else(|_| panic!("invalid exclude pattern '{}'", exclude)));
162
+ }
163
+ let exclude_matcher = exclude_matcher
164
+ .build()
165
+ .unwrap_or_else(|_| panic!("Could not compile excluded patterns matcher"));
166
+
167
+ Self {
168
+ include_matcher,
169
+ exclude_matcher,
170
+ }
171
+ }
172
+ pub fn is_path_included(&self, path: &str) -> bool {
173
+ !self.exclude_matcher.is_match(path) && (self.include_matcher.is_empty() || self.include_matcher.is_match(path))
174
+ }
175
+ }
176
+
177
+ #[cfg(not(feature = "include-exclude"))]
178
+ impl PathMatcher {
179
+ pub fn new(_includes: &[&str], _excludes: &[&str]) -> Self {
180
+ Self {}
181
+ }
182
+ pub fn is_path_included(&self, _path: &str) -> bool {
183
+ true
184
+ }
185
+ }