peacock_crest/lib.rs
1#![doc = include_str!("../README.md")]
2#![allow(warnings)]
3
4mod selector;
5
6pub(crate) mod boo;
7
8pub mod error;
9pub mod source;
10pub mod style;
11pub mod syntax;
12pub mod unit;
13
14use std::collections::HashMap;
15use std::sync::{Arc, RwLock};
16
17pub use selector::{SelectorNode, SelectorNodeType};
18pub use source::SourceInfo;
19pub use style::{CssAttributeValue, CssStyleAttribute, CssStyleProperties, CssValue, Stylesheet};
20pub use unit::Unit;
21
22pub type MatchResult = Option<()>;
23
24/// The generic implementation for document nodes.
25///
26/// Used for selector matching and applying style properties
27pub trait DomElement: Sized {
28 // required functions
29
30 // /// the element's namespace
31 // /// eg: "m" in `<m:math xmlns:m="http://www.w3.org/1998/Math/MathML">...</m:math>`
32 // fn get_namespace(&self) -> &str;
33 // fn get_type(&self) -> &str;
34
35 // /// inline attributes of a document node. **__First value must be the full, unsplit value!__**
36 // /// eg: ("rel": ("nofollow noreferrer", ["nofollow", "noreferrer"]))
37 // fn get_attributes(&self) -> &HashMap<String, &str>;
38
39 // fn get_self(&self) -> Arc<RwLock<Self>>;
40 // fn get_parent(&self) -> Arc<RwLock<Self>>;
41 // fn get_children(&self) -> Box<[Arc<RwLock<Self>>]>;
42
43 /// <button style="color: red;" />
44 fn get_inline_style(&self) -> &style::CssStyleProperties;
45
46 /// This is for applying style properties to the current node immediately
47 fn apply_style_properties(&mut self);
48
49 // optional functions
50
51 // /// whether the element has a specific state (such as "hover", "disabled", etc)
52 // /// default implementation always returns false
53 // fn match_state(&self, state: &str) -> MatchResult {
54 // None
55 // }
56
57 // /// pseudo-elements are for referencing a specific part of an element.
58 // ///
59 // /// eg: using `::selection` to get a reference to the highlighted selection of
60 // /// an element.
61 // fn match_component(&self, state: &str) -> MatchResult {
62 // None
63 // }
64
65 // provided functions
66
67 // /// Does this element have the provided namespace?
68 // /// eg: Does `<m:math xmlns:m="http://www.w3.org/1998/Math/MathML">...</m:math>` have namespace `m`?
69 // ///
70 // /// css selector reference:
71 // /// ```css
72 // /// @namespace m url('http://www.w3.org/1998/Math/MathML');
73 // ///
74 // /// m|* { /* ... */ }
75 // /// ```
76 // fn match_namespace(&self, namespace: &str) -> bool {
77 // self.get_namespace() == namespace
78 // }
79
80 // /// Is this element of the provided type?
81 // /// eg: Is `<div>...</div>` of type `p`?
82 // ///
83 // /// css selector reference:
84 // /// ```css
85 // /// p { /* ... */ }
86 // /// ```
87 // fn match_type(&self, type_name: &str) -> bool {
88 // self.get_type() == type_name
89 // }
90
91 // /// Does this element have the provided id?
92 // /// eg: is the id of `<div id="foo">...</div>` equal to "foo"?
93 // ///
94 // /// css selector reference:
95 // /// ```css
96 // /// div#bar { /* ... */ }
97 // /// ```
98 // fn match_id(&self, id: &str) -> bool {
99 // self.get_attributes()["id"] == id
100 // }
101
102 // /// Does the element have the provided class?
103 // /// eg: does the element `<div class="foo bar baz">...</div>` contain "bar"?
104 // ///
105 // /// css selector reference:
106 // /// ```css
107 // /// div.bar { /* ... */ }
108 // /// ```
109 // fn match_class(&self, class_name: &str) -> bool {
110 // self.get_attributes()["class"]
111 // .split_whitespace()
112 // .any(|class| class == class_name)
113 // }
114
115 // /// Does the element have the provided classes?
116 // /// eg: does the element `<div class="foo bar baz">...</div>` contain BOTH "bar" and "baz"?
117 // ///
118 // /// css selector reference:
119 // /// ```css
120 // /// div.bar.baz { /* ... */ }
121 // /// ```
122 // fn match_classes(&self, class_names: &Vec<String>) -> bool {
123 // let classes = self.get_attributes()["class"]
124 // .split_whitespace()
125 // .collect::<Vec<_>>();
126 // class_names
127 // .iter()
128 // .all(|class_name| classes.contains(&class_name.as_str()))
129 // }
130
131 // /// Does the element have the provided attribute?
132 // /// eg: does the element `<a href="...">...</a>` have the attribute "href"?
133 // ///
134 // /// **CASE-SENSITIVE**
135 // ///
136 // /// css selector reference:
137 // /// ```css
138 // /// a[href] { /* ... */ }
139 // /// ```
140 // fn match_attribute_present(&self, attr_name: &str) -> bool {
141 // self.get_attributes().contains_key(attr_name)
142 // }
143
144 // /// Does the element's attribute have this exact match?
145 // /// eg: does the element `<img alt="A beautiful mountain view">...</img>` have alt text of "A beautiful mountain view"?
146 // ///
147 // /// css selector reference:
148 // /// ```css
149 // /// img[alt="A beautiful mountain view"] { /* ... */ }
150 // /// ```
151 // fn match_attribute_value(
152 // &self,
153 // attr_name: &str,
154 // attr_value: &str,
155 // case_sensitive: bool,
156 // ) -> bool {
157 // if self.match_attribute_present(attr_name) {
158 // let raw_value = *self.get_attributes().get(attr_name).unwrap();
159 // if case_sensitive {
160 // return raw_value == attr_value;
161 // } else {
162 // return raw_value.eq_ignore_ascii_case(attr_value);
163 // }
164 // }
165 // false
166 // }
167
168 // /// Does the element's attribute contain the provided value?
169 // /// eg: does `<a href="..." rel="nofollow noreferrer">...</a>`'s rel attribute contain "noreferrer"?
170 // ///
171 // /// css selector reference:
172 // /// ```css
173 // /// a[rel~="noreferrer"] { /* ... */ }
174 // /// ```
175 // fn match_attribute_contains(
176 // &self,
177 // attr_name: &str,
178 // attr_value: &str,
179 // case_sensitive: bool,
180 // ) -> bool {
181 // if self.match_attribute_present(attr_name) {
182 // let mut own_attrs = self
183 // .get_attributes()
184 // .get(attr_name)
185 // .unwrap()
186 // .split_whitespace();
187 // if case_sensitive {
188 // return own_attrs.any(|attribute_value| attribute_value == attr_value);
189 // } else {
190 // return own_attrs.any(|x| x.eq_ignore_ascii_case(attr_value));
191 // }
192 // }
193 // false
194 // }
195
196 // /// Does the element's attribute start with the provided value?
197 // /// eg: does `<a href="https://example.com/"></a>`'s href attribute start with "https://"?
198 // ///
199 // /// __Case-insensitive matches contain string allocations__.
200 // ///
201 // /// css selector reference:
202 // /// ```css
203 // /// a[href^="https://"] { /* ... */ }
204 // /// ```
205 // fn match_attribute_startswith(
206 // &self,
207 // attr_name: &str,
208 // attr_value_prefix: &str,
209 // case_sensitive: bool,
210 // ) -> bool {
211 // if self.match_attribute_present(attr_name) {
212 // let attr_raw: &str = self.get_attributes().get(attr_name).unwrap();
213 // if case_sensitive {
214 // return attr_raw.starts_with(attr_value_prefix);
215 // } else {
216 // return attr_raw
217 // .to_lowercase()
218 // .starts_with(&attr_value_prefix.to_lowercase());
219 // }
220 // }
221 // false
222 // }
223
224 // /// Does the element's attribute start with the provided value (with an added dash)?
225 // /// eg: does `<p lang="en-US">...</p>`'s lang attribute equal "en" or start with "en-"?
226 // ///
227 // /// css selector reference:
228 // /// ```css
229 // /// p[lang|="en"] { /* ... */ }
230 // /// ```
231 // fn match_attribute_startswith_dash(
232 // &self,
233 // attr_name: &str,
234 // attr_value_prefix: &str,
235 // case_sensitive: bool,
236 // ) -> bool {
237 // if self.match_attribute_value(attr_name, attr_value_prefix, case_sensitive) {
238 // true
239 // } else if self.match_attribute_present(attr_name) {
240 // let prefix_len = attr_value_prefix.len();
241 // (&self.get_attributes()[attr_name][prefix_len + 1..prefix_len + 2]) == "-"
242 // } else {
243 // false
244 // }
245 // }
246
247 // /// Does the element's attribute end with the provided value?
248 // /// eg: does `<a href="document.pdf">...</a>`'s href attribute end with ".pdf"?
249 // ///
250 // /// __Case-insensitive matches contain string allocations__.
251 // ///
252 // /// css selector reference:
253 // /// ```css
254 // /// a[href$=".pdf"] { /* ... */ }
255 // /// ```
256 // fn match_attribute_endswith(
257 // &self,
258 // attr_name: &str,
259 // attr_value_prefix: &str,
260 // case_sensitive: bool,
261 // ) -> bool {
262 // if self.match_attribute_present(attr_name) {
263 // let attr_raw: &str = self.get_attributes().get(attr_name).unwrap();
264 // if case_sensitive {
265 // return attr_raw.starts_with(attr_value_prefix);
266 // } else {
267 // return attr_raw
268 // .to_lowercase()
269 // .ends_with(&attr_value_prefix.to_lowercase());
270 // }
271 // }
272 // false
273 // }
274
275 // /// Does the element's raw attribute contain the provided value?
276 // /// eg: does `<a href="https://example.com/home.htm">...</a>`'s href attribute contain "home"?
277 // ///
278 // /// css selector reference:
279 // /// ```css
280 // /// a[rel~="noreferrer"] { /* ... */ }
281 // /// ```
282 // fn match_attribute_raw_contains(
283 // &self,
284 // attr_name: &str,
285 // attr_value: &str,
286 // case_sensitive: bool,
287 // ) -> bool {
288 // if self.match_attribute_present(attr_name) {
289 // let attr_raw: &str = self.get_attributes().get(attr_name).unwrap();
290 // if case_sensitive {
291 // return attr_raw.contains(&attr_value);
292 // } else {
293 // return attr_raw.to_lowercase().contains(&attr_value.to_lowercase());
294 // }
295 // }
296 // false
297 // }
298
299 // fn match_selector(&self, selector: &selector::SelectorNode) -> MatchResult {
300 // selector.iter().all(|selector_type| match selector_type {
301 // SelectorNodeType::Namespace(ns) => self.match_namespace(ns),
302 // SelectorNodeType::Universal => true,
303 // SelectorNodeType::TypeName(type_name) => self.match_type(type_name),
304 // SelectorNodeType::Id(id) => self.match_id(id),
305 // SelectorNodeType::Class(class) => self.match_class(class),
306 // SelectorNodeType::Attribute {
307 // name,
308 // attr_matcher,
309 // case_sensitive,
310 // } => match attr_matcher {
311 // selector::SelectorAttributeType::Present => self.match_attribute_present(name),
312 // selector::SelectorAttributeType::ExactMatch(against) => {
313 // self.match_attribute_value(name, against, *case_sensitive)
314 // }
315 // selector::SelectorAttributeType::ListContains(against) => {
316 // self.match_attribute_contains(name, against, *case_sensitive)
317 // }
318 // selector::SelectorAttributeType::StartsWith(against) => {
319 // self.match_attribute_startswith(name, against, *case_sensitive)
320 // }
321 // selector::SelectorAttributeType::StartsWithDashed(against) => {
322 // self.match_attribute_startswith_dash(name, against, *case_sensitive)
323 // }
324 // selector::SelectorAttributeType::Endswith(against) => {
325 // self.match_attribute_endswith(name, against, *case_sensitive)
326 // }
327 // selector::SelectorAttributeType::RawContains(against) => {
328 // self.match_attribute_raw_contains(name, against, *case_sensitive)
329 // }
330 // },
331 // SelectorNodeType::NextSibling(selector_node) => {
332 // self.match_next_sibling(selector_node.as_ref())
333 // }
334 // SelectorNodeType::SubsequentSibling(selector_node) => {
335 // self.match_subsequent_siblings(selector_node.as_ref())
336 // }
337 // SelectorNodeType::Child(selector_node) => self.match_child(selector_node.as_ref()),
338 // SelectorNodeType::Descendent(selector_node) => {
339 // self.match_descendent(selector_node.as_ref())
340 // }
341 // SelectorNodeType::PseudoClass(psclass) => self.match_state(psclass as &str).is_some(),
342 // SelectorNodeType::PseudoElement(pselement_name, pselement_arguments) => todo!(),
343 // });
344
345 // None
346 // }
347
348 // /// Is the provided node handle the same as the one for self?
349 // /// A convenience function mostly used for internal identity checks
350 // fn match_self(&self, other: Arc<RwLock<Self>>) -> bool {
351 // Arc::ptr_eq(&self.get_self(), &other)
352 // }
353
354 // fn get_siblings(&self) -> (Box<[Arc<RwLock<Self>>]>, Box<[Arc<RwLock<Self>>]>) {
355 // let mut before: Vec<Arc<RwLock<Self>>> = Vec::new();
356 // let mut after: Vec<Arc<RwLock<Self>>> = Vec::new();
357 // let mut found_self = false;
358 // for sibling in self.get_parent().read().unwrap().get_children().iter() {
359 // if self.match_self(sibling.clone()) {
360 // found_self = true;
361 // } else if !found_self {
362 // before.push(sibling.clone());
363 // } else {
364 // after.push(sibling.clone());
365 // }
366 // }
367
368 // (before.into_boxed_slice(), after.into_boxed_slice())
369 // }
370
371 // fn get_next_sibling(&self) -> Option<Arc<RwLock<Self>>> {
372 // let (prev, subsequent) = self.get_siblings();
373 // if !subsequent.is_empty() {
374 // Some(subsequent[0].clone())
375 // } else {
376 // None
377 // }
378 // }
379
380 // fn get_subsequent_siblings(&self) -> Box<[Arc<RwLock<Self>>]> {
381 // self.get_siblings().1
382 // }
383
384 // fn get_descendents(&self) -> Box<[Arc<RwLock<Self>>]> {
385 // let mut descendants = Vec::new();
386
387 // fn collect_descendants<T: DocumentNode>(
388 // node: &Arc<RwLock<T>>,
389 // descendants: &mut Vec<Arc<RwLock<T>>>,
390 // ) {
391 // for child in node.read().unwrap().get_children().iter() {
392 // descendants.push(child.clone());
393 // collect_descendants(child, descendants);
394 // }
395 // }
396
397 // collect_descendants(&self.get_self(), &mut descendants);
398
399 // descendants.into_boxed_slice()
400 // }
401
402 // fn match_next_sibling(&self, selector: &SelectorNode) -> bool {
403 // if let Some(sibling) = self.get_next_sibling() {
404 // sibling.read().unwrap().match_selector(selector).is_some()
405 // } else {
406 // false
407 // }
408 // }
409
410 // fn match_subsequent_siblings(&self, selector: &SelectorNode) -> bool {
411 // for sibling in self.get_subsequent_siblings().iter() {
412 // if sibling.read().unwrap().match_selector(selector).is_some() {
413 // return true;
414 // }
415 // }
416
417 // false
418 // }
419
420 // fn match_child(&self, selector: &SelectorNode) -> bool {
421 // for child in self.get_children().iter() {
422 // if child.read().unwrap().match_selector(selector).is_some() {
423 // return true;
424 // }
425 // }
426
427 // false
428 // }
429
430 // fn match_descendent(&self, selector: &SelectorNode) -> bool {
431 // for descendent in self.get_descendents().iter() {
432 // if descendent
433 // .read()
434 // .unwrap()
435 // .match_selector(selector)
436 // .is_some()
437 // {
438 // return true;
439 // }
440 // }
441
442 // false
443 // }
444}