Skip to content

support glob patterns #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 27, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cpp-linter-lib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ documentation.workspace = true
[dependencies]
chrono = "0.4.38"
clap = "4.5.16"
fast-glob = "0.4.0"
futures = "0.3.30"
git2 = "0.19.0"
lenient_semver = "0.4.2"
4 changes: 2 additions & 2 deletions cpp-linter-lib/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -140,8 +140,8 @@ the current working directory if not using a CI runner).\n\n",
with a `.`) are also ignored automatically.
- Prefix a path with `!` to explicitly not ignore it. This can be
applied to a submodule's path (if desired) but not hidden directories.
- Glob patterns are not supported here. All asterisk characters (`*`)
are literal.\n\n",
- Glob patterns are supported here. Path separators in glob patterns should
use `/` because `\\` represents an escaped literal.\n\n",
),
)
.arg(
77 changes: 39 additions & 38 deletions cpp-linter-lib/src/common_fs/file_filter.rs
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ use std::{
path::{Path, PathBuf},
};

use fast_glob::glob_match;

use super::FileObj;

#[derive(Debug, Clone)]
@@ -76,35 +78,41 @@ impl FileFilter {
}
}

/// Describes if a specified `file_name` is contained within the given `set` of paths.
/// Describes if a specified `file_name` is contained within the specified set of paths.
///
/// The `is_ignored` flag describes which list of paths is used as domains.
/// The `is_ignored` flag describes which set of paths is used as domains.
/// The specified `file_name` can be a direct or distant descendant of any paths in
/// the list.
/// the set.
///
/// Returns a [`Some`] value of the the path/pattern that matches the given `file_name`.
/// If given `file_name` is not in the specified list, then [`None`] is returned.
pub fn is_file_in_list(&self, file_name: &Path, is_ignored: bool) -> Option<String> {
/// Returns a `true` value of the the path/pattern that matches the given `file_name`.
/// If given `file_name` is not in the specified set, then `false` is returned.
pub fn is_file_in_list(&self, file_name: &Path, is_ignored: bool) -> bool {
let file_name = PathBuf::from(format!(
"./{}",
file_name
.as_os_str()
.to_string_lossy()
.to_string()
.replace("\\", "/")
.trim_start_matches("./")
));
let set = if is_ignored {
&self.ignored
} else {
&self.not_ignored
};
for pattern in set {
let glob_matched =
glob_match(pattern, file_name.to_string_lossy().to_string().as_str());
let pat = PathBuf::from(&pattern);
if (pat.is_file() && file_name == pat) || (pat.is_dir() && file_name.starts_with(pat)) {
return Some(pattern.to_owned());
if glob_matched
|| (pat.is_file() && file_name == pat)
|| (pat.is_dir() && file_name.starts_with(pat))
{
return true;
}
}
None
false
}

/// A helper function that checks if `entry` satisfies the following conditions (in
@@ -130,7 +138,7 @@ impl FileFilter {
if !is_ignored {
let is_in_ignored = self.is_file_in_list(entry, true);
let is_in_not_ignored = self.is_file_in_list(entry, false);
if is_in_not_ignored.is_some() || is_in_ignored.is_none() {
if is_in_not_ignored || !is_in_ignored {
return true;
}
}
@@ -145,7 +153,7 @@ impl FileFilter {
pub fn list_source_files(&self, root_path: &str) -> Vec<FileObj> {
let mut files: Vec<FileObj> = Vec::new();
let entries = fs::read_dir(root_path).expect("repo root-path should exist");
for entry in entries.flatten() {
for entry in entries.filter_map(|p| p.ok()) {
let path = entry.path();
if path.is_dir() {
let mut is_hidden = false;
@@ -196,34 +204,31 @@ mod tests {
#[test]
fn ignore_src() {
let file_filter = setup_ignore("src", vec![]);
assert!(file_filter
.is_file_in_list(&PathBuf::from("./src/lib.rs"), true)
.is_some());
assert!(file_filter
.is_file_in_list(&PathBuf::from("./src/lib.rs"), false)
.is_none());
assert!(file_filter.is_file_in_list(&PathBuf::from("./src/lib.rs"), true));
assert!(!file_filter.is_file_in_list(&PathBuf::from("./src/lib.rs"), false));
}

#[test]
fn ignore_root() {
let file_filter = setup_ignore("!src/lib.rs|./", vec![]);
assert!(file_filter
.is_file_in_list(&PathBuf::from("./cargo.toml"), true)
.is_some());
assert!(file_filter
.is_file_in_list(&PathBuf::from("./src/lib.rs"), false)
.is_some());
assert!(file_filter.is_file_in_list(&PathBuf::from("./Cargo.toml"), true));
assert!(file_filter.is_file_in_list(&PathBuf::from("./src/lib.rs"), false));
}

#[test]
fn ignore_root_implicit() {
let file_filter = setup_ignore("!src|", vec![]);
assert!(file_filter
.is_file_in_list(&PathBuf::from("./cargo.toml"), true)
.is_some());
assert!(file_filter
.is_file_in_list(&PathBuf::from("./src/lib.rs"), false)
.is_some());
assert!(file_filter.is_file_in_list(&PathBuf::from("./Cargo.toml"), true));
assert!(file_filter.is_file_in_list(&PathBuf::from("./src/lib.rs"), false));
}

#[test]
fn ignore_glob() {
let file_filter = setup_ignore("!src/**/*", vec![]);
assert!(file_filter.is_file_in_list(&PathBuf::from("./src/lib.rs"), false));
assert!(
file_filter.is_file_in_list(&PathBuf::from("./src/common_fs/file_filter.rs"), false)
);
}

#[test]
@@ -235,17 +240,13 @@ mod tests {
// using Vec::contains() because these files don't actually exist in project files
for ignored_submodule in ["./RF24", "./RF24Network", "./RF24Mesh"] {
assert!(file_filter.ignored.contains(&ignored_submodule.to_string()));
assert!(file_filter
.is_file_in_list(
&PathBuf::from(ignored_submodule.to_string() + "/some_src.cpp"),
true
)
.is_none());
assert!(!file_filter.is_file_in_list(
&PathBuf::from(ignored_submodule.to_string() + "/some_src.cpp"),
true
));
}
assert!(file_filter.not_ignored.contains(&"./pybind11".to_string()));
assert!(file_filter
.is_file_in_list(&PathBuf::from("./pybind11/some_src.cpp"), false)
.is_none());
assert!(!file_filter.is_file_in_list(&PathBuf::from("./pybind11/some_src.cpp"), false));
}

// *********************** tests for recursive path search
1 change: 1 addition & 0 deletions cpp-linter-lib/tests/.hidden/test_asset.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file is here for completeness when testig file filters.