Skip to main content

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}