1use super::*;
2
3#[derive(Debug, Default)]
4pub struct Filter(Vec<(String, bool)>);
5
6impl Filter {
7 pub fn new(reader: &Reader, include: &[&str], exclude: &[&str]) -> Self {
8 let mut rules = vec![];
9
10 for filter in include {
11 push_filter(reader, &mut rules, filter, true);
12 }
13
14 for filter in exclude {
15 push_filter(reader, &mut rules, filter, false)
16 }
17
18 debug_assert!(!rules.is_empty());
19
20 rules.sort_unstable_by(|left, right| {
21 let left = (left.0.len(), !left.1);
22 let right = (right.0.len(), !right.1);
23 left.cmp(&right).reverse()
24 });
25
26 Self(rules)
27 }
28
29 pub fn includes_namespace(&self, namespace: &str) -> bool {
30 for rule in &self.0 {
31 if rule.1 {
32 // include
33 if namespace_starts_with(&rule.0, namespace) {
34 return true;
35 }
36 if namespace_starts_with(namespace, &rule.0) {
37 return true;
38 }
39 } else {
40 // exclude
41 if namespace_starts_with(namespace, &rule.0) {
42 return false;
43 }
44 }
45 }
46
47 false
48 }
49
50 pub fn includes_type_name(&self, name: TypeName) -> bool {
51 for rule in &self.0 {
52 if match_type_name(&rule.0, name.namespace(), name.name()) {
53 return rule.1;
54 }
55 }
56
57 false
58 }
59
60 pub fn excludes_type_name(&self, name: TypeName) -> bool {
61 for rule in &self.0 {
62 if match_type_name(&rule.0, name.namespace(), name.name()) {
63 return !rule.1;
64 }
65 }
66
67 false
68 }
69}
70
71fn push_filter(reader: &Reader, rules: &mut Vec<(String, bool)>, filter: &str, include: bool) {
72 if reader.contains_key(filter) {
73 rules.push((filter.to_string(), include));
74 return;
75 }
76
77 if let Some((namespace, name)) = filter.rsplit_once('.') {
78 if reader.with_full_name(namespace, name).next().is_some() {
79 rules.push((filter.to_string(), include));
80 return;
81 }
82 }
83
84 let mut pushed = false;
85
86 for (namespace, types) in reader.iter() {
87 if types.get(filter).is_some() {
88 rules.push((format!("{namespace}.{filter}"), include));
89 pushed = true;
90 }
91 }
92
93 if pushed {
94 return;
95 }
96
97 if reader
98 .keys()
99 .any(|namespace| namespace_starts_with(namespace, filter))
100 {
101 rules.push((filter.to_string(), include));
102 return;
103 }
104
105 panic!("type not found: `{filter}`");
106}
107
108fn match_type_name(rule: &str, namespace: &str, name: &str) -> bool {
109 if rule.len() <= namespace.len() {
110 return namespace.starts_with(rule);
111 }
112
113 if !rule.starts_with(namespace) {
114 return false;
115 }
116
117 if rule.as_bytes()[namespace.len()] != b'.' {
118 return false;
119 }
120
121 name == &rule[namespace.len() + 1..]
122}
123