use glob::Pattern;
pub struct ScopeFilter {
include: Vec<Pattern>,
exclude: Vec<Pattern>,
}
impl ScopeFilter {
pub fn new(include: &[String], exclude: &[String]) -> Result<Self, glob::PatternError> {
let include = include
.iter()
.map(|p| Pattern::new(p))
.collect::<Result<Vec<_>, _>>()?;
let exclude = exclude
.iter()
.map(|p| Pattern::new(p))
.collect::<Result<Vec<_>, _>>()?;
Ok(Self { include, exclude })
}
pub fn matches(&self, path: &str) -> bool {
let included = if self.include.is_empty() {
true
} else {
self.include.iter().any(|p| p.matches(path))
};
if !included {
return false;
}
!self.exclude.iter().any(|p| p.matches(path))
}
pub fn filter_paths<'a>(&self, paths: &'a [String]) -> Vec<&'a str> {
paths
.iter()
.filter(|p| self.matches(p))
.map(|p| p.as_str())
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn include_only() {
let filter =
ScopeFilter::new(&["src/payments/**".into(), "src/portal/**".into()], &[]).unwrap();
assert!(filter.matches("src/payments/cta.rs"));
assert!(filter.matches("src/portal/entry.rs"));
assert!(!filter.matches("src/onboarding/flow.rs"));
assert!(!filter.matches("README.md"));
}
#[test]
fn exclude_only() {
let filter = ScopeFilter::new(&[], &["src/onboarding/**".into(), "*.md".into()]).unwrap();
assert!(filter.matches("src/payments/cta.rs"));
assert!(!filter.matches("src/onboarding/flow.rs"));
assert!(!filter.matches("README.md"));
}
#[test]
fn include_and_exclude() {
let filter = ScopeFilter::new(&["src/**".into()], &["src/tests/**".into()]).unwrap();
assert!(filter.matches("src/payments/cta.rs"));
assert!(!filter.matches("src/tests/payment_test.rs"));
assert!(!filter.matches("docs/readme.md"));
}
#[test]
fn exclude_wins_over_include() {
let filter = ScopeFilter::new(
&["src/payments/**".into()],
&["src/payments/legacy/**".into()],
)
.unwrap();
assert!(filter.matches("src/payments/cta.rs"));
assert!(!filter.matches("src/payments/legacy/old.rs"));
}
#[test]
fn empty_filter_matches_all() {
let filter = ScopeFilter::new(&[], &[]).unwrap();
assert!(filter.matches("anything/goes.rs"));
}
}