1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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()
}
}