use std::path::Path;
use globset::GlobBuilder;
pub fn matches_any(pattern: &str, files: &[&str]) -> bool {
let is_basename = !pattern.contains('/');
let glob = match GlobBuilder::new(pattern).literal_separator(true).build() {
Ok(g) => g,
Err(_) => return false,
};
let matcher = glob.compile_matcher();
if is_basename {
files.iter().any(|f| {
Path::new(f)
.file_name()
.is_some_and(|name| matcher.is_match(name))
})
} else {
files.iter().any(|f| matcher.is_match(f))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_rust_files() {
let files = vec!["src/main.rs", "src/lib.rs", "README.md"];
assert!(matches_any("*.rs", &files));
}
#[test]
fn no_match_returns_false() {
let files = vec!["src/main.rs", "README.md"];
assert!(!matches_any("*.kt", &files));
}
#[test]
fn empty_file_list_returns_false() {
let files: Vec<&str> = vec![];
assert!(!matches_any("*.rs", &files));
}
#[test]
fn star_pattern_matches_nested_files() {
let files = vec!["crates/core/src/lib.rs"];
assert!(matches_any("*.rs", &files));
}
#[test]
fn double_star_glob() {
let files = vec![
"modules/auth/Login.kt",
"modules/core/Base.kt",
"build.gradle",
];
assert!(matches_any("modules/**/*.kt", &files));
assert!(!matches_any("modules/**/*.rs", &files));
}
#[test]
fn single_directory_glob() {
let files = vec!["src/main.rs", "tests/integration.rs"];
assert!(matches_any("src/*.rs", &files));
assert!(!matches_any("lib/*.rs", &files));
}
#[test]
fn exact_filename() {
let files = vec!["Cargo.toml", "src/main.rs"];
assert!(matches_any("Cargo.toml", &files));
assert!(!matches_any("package.json", &files));
}
#[test]
fn pattern_with_leading_dot() {
let files = vec![".github/workflows/ci.yml", "src/main.rs"];
assert!(matches_any(".github/**/*.yml", &files));
}
#[test]
fn multiple_extensions() {
let files = vec!["app.ts", "utils.tsx", "style.css"];
assert!(matches_any("*.ts", &files));
assert!(matches_any("*.tsx", &files));
}
#[test]
fn deeply_nested_match() {
let files = vec!["a/b/c/d/e/file.py"];
assert!(matches_any("*.py", &files));
assert!(matches_any("a/**/*.py", &files));
}
#[test]
fn slash_in_pattern_restricts_to_path() {
let files = vec!["src/sub/deep.rs"];
assert!(!matches_any("src/*.rs", &files));
assert!(matches_any("src/**/*.rs", &files));
}
}