///|
/// Include/exclude rules for normalized source paths.
///
/// An empty include list accepts every path. Excludes are evaluated after
/// includes and always win.
pub(all) struct PathFilter {
includes : Array[String]
excludes : Array[String]
} derive(Debug, Eq)
///|
/// Construct a reusable path filter.
pub fn PathFilter::new(
includes : Array[String],
excludes : Array[String],
) -> PathFilter {
{ includes, excludes }
}
///|
/// Construct a filter that accepts every path.
pub fn PathFilter::all() -> PathFilter {
{ includes: [], excludes: [] }
}
///|
fn any_glob_matches(patterns : Array[String], path : String) -> Bool {
for pattern in patterns {
if glob_match(pattern, path) {
return true
}
}
false
}
///|
/// Test whether a source path passes this filter.
pub fn PathFilter::allows(self : PathFilter, path : String) -> Bool {
let normalized = normalize_path(path)
let included = self.includes.is_empty() ||
any_glob_matches(self.includes, normalized)
included && !any_glob_matches(self.excludes, normalized)
}
///|
fn copy_file_coverage(file : FileCoverage) -> FileCoverage {
{
path: file.path,
test_name: file.test_name,
lines: [
for line in file.lines => line
],
branches: [
for branch in file.branches => branch
],
functions: [
for function in file.functions => function
],
}
}
///|
/// Select files from a report without mutating or aliasing its arrays.
pub fn filter_report(
report : CoverageReport,
filter : PathFilter,
) -> CoverageReport {
{
files: [
for file in report.files if filter.allows(file.path) => {
copy_file_coverage(file)
}
],
}
}
///|
/// Convenience wrapper for one-off include and exclude lists.
pub fn select_paths(
report : CoverageReport,
includes : Array[String],
excludes : Array[String],
) -> CoverageReport {
filter_report(report, PathFilter::new(includes, excludes))
}