1 | #[derive (Default)] |
2 | pub struct Filter(pub Vec<(String, bool)>); |
3 | |
4 | impl 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 | |
65 | fn 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)] |
82 | mod 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 | |