#![cfg_attr(coverage_nightly, coverage(off))]
use super::pattern_helpers::{expand_patterns, validate_patterns};
use anyhow::Result;
use globset::{Glob, GlobSet, GlobSetBuilder};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Default)]
pub struct FileFilter {
include_set: Option<GlobSet>,
exclude_set: Option<GlobSet>,
}
impl FileFilter {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new(include_patterns: Vec<String>, exclude_patterns: Vec<String>) -> Result<Self> {
let expanded_include = expand_patterns(&include_patterns);
let expanded_exclude = expand_patterns(&exclude_patterns);
validate_patterns(&expanded_include)?;
validate_patterns(&expanded_exclude)?;
let include_set = if expanded_include.is_empty() {
None
} else {
let mut builder = GlobSetBuilder::new();
for pattern in expanded_include {
builder.add(Glob::new(&pattern)?);
}
Some(builder.build()?)
};
let exclude_set = if expanded_exclude.is_empty() {
None
} else {
let mut builder = GlobSetBuilder::new();
for pattern in expanded_exclude {
builder.add(Glob::new(&pattern)?);
}
Some(builder.build()?)
};
Ok(Self {
include_set,
exclude_set,
})
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn from_optional(include: &Option<String>, exclude: &Option<String>) -> Result<Self> {
use super::pattern_helpers::normalize_patterns;
let (include_vec, exclude_vec) = normalize_patterns(include, exclude);
Self::new(include_vec, exclude_vec)
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn should_include(&self, path: &Path) -> bool {
if let Some(ref exclude_set) = self.exclude_set {
if exclude_set.is_match(path) {
return false;
}
}
if let Some(ref include_set) = self.include_set {
return include_set.is_match(path);
}
true
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn filter_paths(&self, paths: Vec<PathBuf>) -> Vec<PathBuf> {
paths
.into_iter()
.filter(|path| self.should_include(path))
.collect()
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn has_filters(&self) -> bool {
self.include_set.is_some() || self.exclude_set.is_some()
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_include_patterns() {
let filter = FileFilter::new(vec!["src/**/*.rs".to_string()], vec![]).unwrap();
assert!(filter.should_include(Path::new("src/main.rs")));
assert!(filter.should_include(Path::new("src/lib/mod.rs")));
assert!(!filter.should_include(Path::new("tests/test.rs")));
assert!(!filter.should_include(Path::new("src/main.toml")));
}
#[test]
fn test_exclude_patterns() {
let filter =
FileFilter::new(vec![], vec!["tests/**".to_string(), "*.tmp".to_string()]).unwrap();
assert!(filter.should_include(Path::new("src/main.rs")));
assert!(!filter.should_include(Path::new("tests/test.rs")));
assert!(!filter.should_include(Path::new("file.tmp")));
}
#[test]
fn test_combined_patterns() {
let filter =
FileFilter::new(vec!["**/*.rs".to_string()], vec!["tests/**".to_string()]).unwrap();
assert!(filter.should_include(Path::new("src/main.rs")));
assert!(!filter.should_include(Path::new("tests/test.rs")));
assert!(!filter.should_include(Path::new("src/config.toml")));
}
#[test]
fn test_filter_paths() {
let filter = FileFilter::new(
vec!["src/**/*.rs".to_string()],
vec!["src/generated/**".to_string()],
)
.unwrap();
let paths = vec![
PathBuf::from("src/main.rs"),
PathBuf::from("src/lib.rs"),
PathBuf::from("src/generated/code.rs"),
PathBuf::from("tests/test.rs"),
PathBuf::from("README.md"),
];
let filtered = filter.filter_paths(paths);
assert_eq!(filtered.len(), 2);
assert!(filtered.contains(&PathBuf::from("src/main.rs")));
assert!(filtered.contains(&PathBuf::from("src/lib.rs")));
}
#[test]
fn test_no_filters() {
let filter = FileFilter::default();
assert!(!filter.has_filters());
assert!(filter.should_include(Path::new("any/path.rs")));
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}