src/file_filter.rs (95 lines of code) (raw):
use regex::Regex;
use std::path::Path;
pub enum FilterType {
Line(u32),
Branch(u32),
Both(u32),
}
#[derive(Default)]
pub struct FileFilter {
excl_line: Option<Regex>,
excl_start: Option<Regex>,
excl_stop: Option<Regex>,
excl_br_line: Option<Regex>,
excl_br_start: Option<Regex>,
excl_br_stop: Option<Regex>,
}
impl FileFilter {
pub fn new(
excl_line: Option<Regex>,
excl_start: Option<Regex>,
excl_stop: Option<Regex>,
excl_br_line: Option<Regex>,
excl_br_start: Option<Regex>,
excl_br_stop: Option<Regex>,
) -> Self {
Self {
excl_line,
excl_start,
excl_stop,
excl_br_line,
excl_br_start,
excl_br_stop,
}
}
pub fn create(&self, file: &Path) -> Vec<FilterType> {
if self.excl_line.is_none()
&& self.excl_start.is_none()
&& self.excl_br_line.is_none()
&& self.excl_br_start.is_none()
{
return Vec::new();
}
let file = std::fs::read_to_string(file);
let file = if let Ok(file) = file {
file
} else {
return Vec::new();
};
let mut ignore_br = false;
let mut ignore = false;
file.split('\n')
.enumerate()
.filter_map(move |(number, line)| {
// Line numbers are 1-based.
let number = (number + 1) as u32;
// The file is split on \n, which may result in a trailing \r
// on Windows. Remove it.
let line = line.strip_suffix('\r').unwrap_or(line);
// End a branch ignore region. Region endings are exclusive.
if ignore_br && self.excl_br_stop.as_ref().is_some_and(|f| f.is_match(line)) {
ignore_br = false
}
// End a line ignore region. Region endings are exclusive.
if ignore && self.excl_stop.as_ref().is_some_and(|f| f.is_match(line)) {
ignore = false
}
// Start a branch ignore region. Region starts are inclusive.
if !ignore_br
&& self
.excl_br_start
.as_ref()
.is_some_and(|f| f.is_match(line))
{
ignore_br = true;
}
// Start a line ignore region. Region starts are inclusive.
if !ignore && self.excl_start.as_ref().is_some_and(|f| f.is_match(line)) {
ignore = true;
}
if ignore_br {
// Consuming code has to eliminate each of these
// individually, so it has to know when both are ignored vs.
// either.
if ignore {
Some(FilterType::Both(number))
} else {
Some(FilterType::Branch(number))
}
} else if ignore {
Some(FilterType::Line(number))
} else if self.excl_br_line.as_ref().is_some_and(|f| f.is_match(line)) {
// Single line exclusion. If single line exclusions occur
// inside a region they are meaningless (would be applied
// anway), so they are lower priority.
if self.excl_line.as_ref().is_some_and(|f| f.is_match(line)) {
Some(FilterType::Both(number))
} else {
Some(FilterType::Branch(number))
}
} else if self.excl_line.as_ref().is_some_and(|f| f.is_match(line)) {
Some(FilterType::Line(number))
} else {
None
}
})
.collect()
}
}