~repos /rust-embed
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.
133ce098
—
Piotr Osiewicz 1 year ago
perf: Do not build glob matchers repeatedly when include-exclude feature is enabled
- impl/src/lib.rs +16 -11
- 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(),
|
|
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
|
|
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
|
|
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),
|
|
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
|
|
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
|
|
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
|
+
}
|