summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Cantrell <david@cantrell.org.uk>2022-07-31 15:29:48 +0100
committerGitHub <noreply@github.com>2022-07-31 16:29:48 +0200
commitdd73447329e637ee207b1103ecb6a4bdbdc89324 (patch)
tree642d6e56b5d8250f59c3bd52babb45cb2d7c5eed /src
parentb75677ab59038f51e79a1bc2b9dbba8d6fc179f8 (diff)
feat: Add the ability to have some file extensions *prevent* a module from triggering (#4043)
* test that we can match a multi-part file extension such as in foo.tar.gz * now we can match multi-part file extensions like on foo.tar.gz * add a test that a !ext is a negative match and over-rides any positive match * test that negative extensions that don't match any file have no effect * fail the match if any negative extensions exist * cargo fmt I'm not happy with this, in particular it's made the structures of has_any_positive_extension and has_no_negative_extension look different, and the logic in is_match is harder to follow * placate clippy * documentation for multi-part extensions and negative extensions * get rid of an unnecessary .to_string() and comment the necessary but weird-looking invocations of .to_string_lossy().to_string() * tests for negative matching of files and folders * fail the match is any negative files/folders match * document file/folder negative matching; be less prolix * suppress Nodejs if Deno files are present (#2627) * Revert "suppress Nodejs if Deno files are present (#2627)" This reverts commit c1394fd7b37bb0bf06b1449e074020a2e16bfa04. This was a terrible way of doing this, there's got to be a better way!
Diffstat (limited to 'src')
-rw-r--r--src/context.rs117
-rw-r--r--src/modules/directory.rs2
2 files changed, 106 insertions, 13 deletions
diff --git a/src/context.rs b/src/context.rs
index 743c242fd..404cf5aaf 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -397,10 +397,27 @@ impl DirContents {
folders.insert(path);
} else {
if !path.to_string_lossy().starts_with('.') {
+ // Extract the file extensions (yes, that's plural) from a filename.
+ // Why plural? Consider the case of foo.tar.gz. It's a compressed
+ // tarball (tar.gz), and it's a gzipped file (gz). We should be able
+ // to match both.
+
+ // find the minimal extension on a file. ie, the gz in foo.tar.gz
+ // NB the .to_string_lossy().to_string() here looks weird but is
+ // required to convert it from a Cow.
path.extension()
.map(|ext| extensions.insert(ext.to_string_lossy().to_string()));
+
+ // find the full extension on a file. ie, the tar.gz in foo.tar.gz
+ path.file_name().map(|file_name| {
+ file_name
+ .to_string_lossy()
+ .split_once('.')
+ .map(|(_, after)| extensions.insert(after.to_string()))
+ });
}
if let Some(file_name) = path.file_name() {
+ // this .to_string_lossy().to_string() is also required
file_names.insert(file_name.to_string_lossy().to_string());
}
files.insert(path);
@@ -432,24 +449,47 @@ impl DirContents {
self.file_names.contains(name)
}
- pub fn has_any_file_name(&self, names: &[&str]) -> bool {
- names.iter().any(|name| self.has_file_name(name))
- }
-
pub fn has_folder(&self, path: &str) -> bool {
self.folders.contains(Path::new(path))
}
- pub fn has_any_folder(&self, paths: &[&str]) -> bool {
- paths.iter().any(|path| self.has_folder(path))
- }
-
pub fn has_extension(&self, ext: &str) -> bool {
self.extensions.contains(ext)
}
- pub fn has_any_extension(&self, exts: &[&str]) -> bool {
- exts.iter().any(|ext| self.has_extension(ext))
+ pub fn has_any_positive_file_name(&self, names: &[&str]) -> bool {
+ names
+ .iter()
+ .any(|name| !name.starts_with('!') && self.has_file_name(name))
+ }
+
+ pub fn has_any_positive_folder(&self, paths: &[&str]) -> bool {
+ paths
+ .iter()
+ .any(|path| !path.starts_with('!') && self.has_folder(path))
+ }
+
+ pub fn has_any_positive_extension(&self, exts: &[&str]) -> bool {
+ exts.iter()
+ .any(|ext| !ext.starts_with('!') && self.has_extension(ext))
+ }
+
+ pub fn has_no_negative_file_name(&self, names: &[&str]) -> bool {
+ !names
+ .iter()
+ .any(|name| name.starts_with('!') && self.has_file_name(&name[1..]))
+ }
+
+ pub fn has_no_negative_folder(&self, paths: &[&str]) -> bool {
+ !paths
+ .iter()
+ .any(|path| path.starts_with('!') && self.has_folder(&path[1..]))
+ }
+
+ pub fn has_no_negative_extension(&self, exts: &[&str]) -> bool {
+ !exts
+ .iter()
+ .any(|ext| ext.starts_with('!') && self.has_extension(&ext[1..]))
}
}
@@ -516,9 +556,16 @@ impl<'a> ScanDir<'a> {
/// based on the current `PathBuf` check to see
/// if any of this criteria match or exist and returning a boolean
pub fn is_match(&self) -> bool {
- self.dir_contents.has_any_extension(self.extensions)
- || self.dir_contents.has_any_folder(self.folders)
- || self.dir_contents.has_any_file_name(self.files)
+ // if there exists a file with a file/folder/ext we've said we don't want,
+ // fail the match straight away
+ self.dir_contents.has_no_negative_extension(self.extensions)
+ && self.dir_contents.has_no_negative_file_name(self.files)
+ && self.dir_contents.has_no_negative_folder(self.folders)
+ && (self
+ .dir_contents
+ .has_any_positive_extension(self.extensions)
+ || self.dir_contents.has_any_positive_file_name(self.files)
+ || self.dir_contents.has_any_positive_folder(self.folders))
}
}
@@ -726,6 +773,50 @@ mod tests {
.is_match());
node.close()?;
+ let tarballs = testdir(&["foo.tgz", "foo.tar.gz"])?;
+ let tarballs_dc = DirContents::from_path(tarballs.path())?;
+ assert!(ScanDir {
+ dir_contents: &tarballs_dc,
+ files: &[],
+ extensions: &["tar.gz"],
+ folders: &[],
+ }
+ .is_match());
+ tarballs.close()?;
+
+ let dont_match_ext = testdir(&["foo.js", "foo.ts"])?;
+ let dont_match_ext_dc = DirContents::from_path(dont_match_ext.path())?;
+ assert!(!ScanDir {
+ dir_contents: &dont_match_ext_dc,
+ files: &[],
+ extensions: &["js", "!notfound", "!ts"],
+ folders: &[],
+ }
+ .is_match());
+ dont_match_ext.close()?;
+
+ let dont_match_file = testdir(&["goodfile", "evilfile"])?;
+ let dont_match_file_dc = DirContents::from_path(dont_match_file.path())?;
+ assert!(!ScanDir {
+ dir_contents: &dont_match_file_dc,
+ files: &["goodfile", "!notfound", "!evilfile"],
+ extensions: &[],
+ folders: &[],
+ }
+ .is_match());
+ dont_match_file.close()?;
+
+ let dont_match_folder = testdir(&["gooddir/somefile", "evildir/somefile"])?;
+ let dont_match_folder_dc = DirContents::from_path(dont_match_folder.path())?;
+ assert!(!ScanDir {
+ dir_contents: &dont_match_folder_dc,
+ files: &[],
+ extensions: &[],
+ folders: &["gooddir", "!notfound", "!evildir"],
+ }
+ .is_match());
+ dont_match_folder.close()?;
+
Ok(())
}
diff --git a/src/modules/directory.rs b/src/modules/directory.rs
index 109a9176c..29dc94bd1 100644
--- a/src/modules/directory.rs
+++ b/src/modules/directory.rs
@@ -498,6 +498,8 @@ mod tests {
fn make_known_tempdir(root: &Path) -> io::Result<(TempDir, String)> {
fs::create_dir_all(root)?;
let dir = TempDir::new_in(root)?;
+ // the .to_string_lossy().to_string() here looks weird but is required
+ // to convert it from a Cow.
let path = dir
.path()
.file_name()