html_filter/filter/
element.rs1use std::collections::HashMap;
6
7use crate::types::tag::Attribute;
8
9#[derive(Debug)]
18pub struct BlackWhiteList {
19 default: bool,
23 items: HashMap<String, bool>,
28 whitelist_empty: bool,
30}
31
32impl BlackWhiteList {
33 pub fn check(&self, name: &str) -> ElementState {
35 self.items.get(name).map_or_else(
36 || {
37 if self.is_empty() && self.default {
38 ElementState::NotSpecified
39 } else {
40 ElementState::BlackListed
41 }
42 },
43 |keep| match keep {
44 true => ElementState::WhiteListed,
45 false => ElementState::BlackListed,
46 },
47 )
48 }
49
50 pub const fn is_empty(&self) -> bool {
52 self.whitelist_empty
53 }
54
55 pub fn is_explicitly_blacklisted(&self, name: &str) -> bool {
57 self.items.get(name).map_or_else(|| false, |keep| !*keep)
58 }
59
60 pub fn push(&mut self, name: String, keep: bool) -> Result<(), ()> {
62 if keep {
63 self.whitelist_empty = false;
64 }
65 let old = self.items.insert(name, keep);
66 if old.is_some_and(|inner| inner != keep) {
67 Err(())
68 } else {
69 Ok(())
70 }
71 }
72
73 pub const fn set_default(&mut self, default: bool) {
77 self.default = default;
78 }
79}
80
81impl Default for BlackWhiteList {
82 fn default() -> Self {
83 Self { items: HashMap::new(), whitelist_empty: true, default: true }
84 }
85}
86
87#[derive(Debug)]
92pub enum ElementState {
93 BlackListed,
95 NotSpecified,
97 WhiteListed,
99}
100
101impl ElementState {
102 pub const fn and(&self, other: &Self) -> Self {
106 match (self, other) {
107 (Self::BlackListed, _) | (_, Self::BlackListed) => Self::BlackListed,
108 (Self::NotSpecified, Self::NotSpecified) => Self::NotSpecified,
109 (Self::WhiteListed | Self::NotSpecified, Self::WhiteListed | Self::NotSpecified) =>
111 Self::WhiteListed,
112 }
113 }
114
115 pub const fn is_allowed_or(&self, default: bool) -> bool {
117 match self {
118 Self::BlackListed => false,
119 Self::NotSpecified => default,
120 Self::WhiteListed => true,
121 }
122 }
123}
124
125#[derive(Default, Debug)]
128pub struct ValueAssociateHash {
129 blacklist: Vec<(String, Option<String>)>,
131 whitelist: Vec<(String, Option<String>)>,
133}
134
135impl ValueAssociateHash {
136 pub fn check(&self, attrs: &[Attribute]) -> ElementState {
138 let attrs_map: HashMap<_, _> = attrs
139 .iter()
140 .map(|attr| (attr.as_name().to_string(), attr.as_value()))
141 .collect();
142 for (wanted_name, wanted_value) in &self.whitelist {
143 match attrs_map.get(wanted_name) {
144 None => return ElementState::BlackListed,
145 Some(found_value) if *found_value != wanted_value.as_ref() =>
146 return ElementState::BlackListed,
147 Some(_) => (),
148 }
149 }
150 for (wanted_name, wanted_value) in &self.blacklist {
151 match attrs_map.get(wanted_name) {
152 Some(found_value) if *found_value == wanted_value.as_ref() =>
153 return ElementState::BlackListed,
154 Some(_) | None => (),
155 }
156 }
157 if self.is_empty() {
158 ElementState::NotSpecified
159 } else {
160 ElementState::WhiteListed
161 }
162 }
163
164 pub const fn is_empty(&self) -> bool {
166 self.whitelist.is_empty() && self.blacklist.is_empty()
167 }
168
169 pub fn is_explicitly_blacklisted(&self, attrs: &[Attribute]) -> bool {
171 let blacklist = self
172 .blacklist
173 .iter()
174 .map(|(name, value)| (name, value))
175 .collect::<HashMap<_, _>>();
176 for attr in attrs {
177 if let Some(value) = blacklist.get(&attr.as_name().to_string()) {
178 if attr.as_value() == value.as_ref() {
179 return true;
180 }
181 }
182 }
183 false
184 }
185
186 pub fn push(&mut self, name: String, value: Option<String>, keep: bool) {
188 let () = if keep {
189 self.whitelist.push((name, value));
190 } else {
191 self.blacklist.push((name, value));
192 };
193 }
194}