1#[derive(Default)]
2pub struct Filter(pub Vec<(String, bool)>);
3
4impl Filter {
5 pub fn new(include: &[&str], exclude: &[&str]) -> Self {
6 let mut rules = vec![];
7
8 for include in include {
9 rules.push((include.to_string(), true));
10 }
11
12 for exclude in exclude {
13 rules.push((exclude.to_string(), false));
14 }
15
16 rules.sort_unstable_by(|left, right| {
17 let left = (left.0.len(), !left.1);
18 let right = (right.0.len(), !right.1);
19 left.cmp(&right).reverse()
20 });
21
22 Self(rules)
23 }
24
25 pub fn includes_namespace(&self, namespace: &str) -> bool {
26 if self.0.is_empty() {
27 return true;
28 }
29
30 for rule in &self.0 {
31 if rule.1 {
32 // include
33 if rule.0.starts_with(namespace) {
34 return true;
35 }
36 if namespace.starts_with(&rule.0) {
37 return true;
38 }
39 } else {
40 // exclude
41 if namespace.starts_with(&rule.0) {
42 return false;
43 }
44 }
45 }
46
47 false
48 }
49
50 pub fn includes_type_name(&self, namespace: &str, name: &str) -> bool {
51 if self.0.is_empty() {
52 return true;
53 }
54
55 for rule in &self.0 {
56 if match_type_name(&rule.0, namespace, name) {
57 return rule.1;
58 }
59 }
60
61 false
62 }
63}
64
65fn match_type_name(rule: &str, namespace: &str, name: &str) -> bool {
66 if rule.len() <= namespace.len() {
67 return namespace.starts_with(rule);
68 }
69
70 if !rule.starts_with(namespace) {
71 return false;
72 }
73
74 if rule.as_bytes()[namespace.len()] != b'.' {
75 return false;
76 }
77
78 name == &rule[namespace.len() + 1..]
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 fn includes_type_name(filter: &Filter, full_name: &'static str) -> bool {
86 let type_name = crate::TypeName::parse(full_name);
87 filter.includes_type_name(type_name.namespace, type_name.name)
88 }
89
90 #[test]
91 fn test_namespace() {
92 let include = ["N1.N2"];
93 let exclude = ["N1.N2.N3"];
94 let f = Filter::new(&include, &exclude);
95
96 assert!(f.includes_namespace("N1"));
97 assert!(f.includes_namespace("N1.N2"));
98 assert!(f.includes_namespace("N1.N2.N4"));
99
100 assert!(!f.includes_namespace("N1.N2.N3"));
101 assert!(!f.includes_namespace("N1.N2.N3.N4"));
102 }
103
104 #[test]
105 fn test_simple() {
106 let include = ["N1", "N3", "N3.N4.N5"];
107 let exclude = ["N2", "N3.N4"];
108 let f = Filter::new(&include, &exclude);
109
110 assert!(!includes_type_name(&f, "NN.T"));
111
112 assert!(includes_type_name(&f, "N1.T"));
113 assert!(includes_type_name(&f, "N3.T"));
114
115 assert!(!includes_type_name(&f, "N2.T"));
116 assert!(!includes_type_name(&f, "N3.N4.T"));
117
118 assert!(includes_type_name(&f, "N3.N4.N5.T"));
119 }
120
121 #[test]
122 fn filter_excludes_same_length() {
123 let include = ["N.N1", "N.N2"];
124 let exclude = ["N.N3", "N.N4"];
125 let f = Filter::new(&include, &exclude);
126
127 assert!(includes_type_name(&f, "N.N1.T"));
128 assert!(includes_type_name(&f, "N.N2.T"));
129
130 assert!(!includes_type_name(&f, "N.N3.T"));
131 assert!(!includes_type_name(&f, "N.N4.T"));
132 }
133
134 #[test]
135 fn filter_exclude_include_precedence() {
136 let include = ["N.T"];
137 let exclude = ["N.T"];
138 let f = Filter::new(&include, &exclude);
139
140 assert!(!includes_type_name(&f, "N.T"));
141 }
142}
143