Skip to main content

tl/parser/
tag.rs

1use crate::{
2    inline::{hashmap::InlineHashMap, vec::InlineVec},
3    queryselector::{self, QuerySelectorIterator},
4    Bytes, InnerNodeHandle,
5};
6use std::{borrow::Cow, mem};
7
8use super::{handle::NodeHandle, Parser};
9
10const INLINED_ATTRIBUTES: usize = 2;
11const INLINED_SUBNODES: usize = 2;
12const HTML_VOID_ELEMENTS: [&str; 16] = [
13    "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link",
14    "meta", "param", "source", "track", "wbr",
15];
16
17/// The type of map for "raw" attributes
18pub type RawAttributesMap<'a> = InlineHashMap<Bytes<'a>, Option<Bytes<'a>>, INLINED_ATTRIBUTES>;
19
20/// The type of vector for children of an HTML tag
21pub type RawChildren = InlineVec<NodeHandle, INLINED_SUBNODES>;
22
23/// Stores all attributes of an HTML tag, as well as additional metadata such as `id` and `class`
24#[derive(Debug, Clone)]
25pub struct Attributes<'a> {
26    /// Raw attributes (maps attribute key to attribute value)
27    pub(crate) raw: RawAttributesMap<'a>,
28    /// The ID attribute of this HTML element, if present
29    pub(crate) id: Option<Option<Bytes<'a>>>,
30    /// The class attribute of this HTML element, if present
31    pub(crate) class: Option<Option<Bytes<'a>>>,
32}
33
34impl<'a> Attributes<'a> {
35    /// Creates a new `Attributes
36    pub(crate) fn new() -> Self {
37        Self {
38            raw: InlineHashMap::new(),
39            id: None,
40            class: None,
41        }
42    }
43
44    fn normalize_key<B>(key: B) -> Bytes<'a>
45    where
46        B: Into<Bytes<'a>>,
47    {
48        key.into().into_ascii_lowercase()
49    }
50
51    /// Counts the number of attributes
52    pub fn len(&self) -> usize {
53        let mut raw = self.raw.len();
54        if self.id.is_some() {
55            raw += 1;
56        }
57        if self.class.is_some() {
58            raw += 1;
59        }
60        raw
61    }
62
63    /// Checks whether this collection of attributes is empty
64    pub fn is_empty(&self) -> bool {
65        self.len() == 0
66    }
67
68    /// Checks whether a given string is in the class names list
69    pub fn is_class_member<B: AsRef<[u8]>>(&self, member: B) -> bool {
70        self.class_iter()
71            .is_some_and(|mut i| i.any(|s| s.as_bytes() == member.as_ref()))
72    }
73
74    /// Checks whether this attributes collection contains a given key and returns its value
75    ///
76    /// Attributes that exist in this tag but have no value set will have their inner Option set to None
77    pub fn get<B>(&self, key: B) -> Option<Option<&Bytes<'a>>>
78    where
79        B: Into<Bytes<'a>>,
80    {
81        let key = Self::normalize_key(key);
82
83        match key.as_bytes() {
84            b"id" => self.id.as_ref().map(Option::as_ref),
85            b"class" => self.class.as_ref().map(Option::as_ref),
86            _ => self.raw.get(&key).map(|x| x.as_ref()),
87        }
88    }
89
90    /// Checks whether this attributes collection contains a given key
91    pub fn contains<B>(&self, key: B) -> bool
92    where
93        B: Into<Bytes<'a>>,
94    {
95        self.get(key).is_some()
96    }
97
98    /// Removes an attribute from this collection and returns it.
99    ///
100    /// As with [`Attributes::get()`], the outer Option is set to None if the attribute does not exist.
101    /// The inner option is set to None if the attribute exists but has no value.
102    ///
103    /// # Example
104    /// ```
105    /// let mut dom = tl::parse("<span contenteditable=\"true\"></span>", Default::default()).unwrap();
106    /// let element = dom.nodes_mut()[0].as_tag_mut().unwrap();
107    /// let attributes = element.attributes_mut();
108    ///
109    /// assert_eq!(attributes.remove("contenteditable"), Some(Some("true".into())));
110    /// assert_eq!(attributes.len(), 0);
111    /// ```
112    pub fn remove<B>(&mut self, key: B) -> Option<Option<Bytes<'a>>>
113    where
114        B: Into<Bytes<'a>>,
115    {
116        let key = Self::normalize_key(key);
117
118        match key.as_bytes() {
119            b"id" => self.id.take(),
120            b"class" => self.class.take(),
121            _ => self.raw.remove(&key),
122        }
123    }
124
125    /// Removes the value of an attribute in this collection and returns it.
126    ///
127    /// # Example
128    /// ```
129    /// let mut dom = tl::parse("<span contenteditable=\"true\"></span>", Default::default()).unwrap();
130    /// let element = dom.nodes_mut()[0].as_tag_mut().unwrap();
131    /// let attributes = element.attributes_mut();
132    ///
133    /// assert_eq!(attributes.remove_value("contenteditable"), Some("true".into()));
134    /// assert_eq!(attributes.get("contenteditable"), Some(None));
135    /// ```
136    pub fn remove_value<B>(&mut self, key: B) -> Option<Bytes<'a>>
137    where
138        B: Into<Bytes<'a>>,
139    {
140        let key = Self::normalize_key(key);
141
142        match key.as_bytes() {
143            b"id" => self.id.as_mut().and_then(Option::take),
144            b"class" => self.class.as_mut().and_then(Option::take),
145            _ => self.raw.get_mut(&key).and_then(mem::take),
146        }
147    }
148
149    /// Checks whether this attributes collection contains a given key and returns its value
150    pub fn get_mut<B>(&mut self, key: B) -> Option<Option<&mut Bytes<'a>>>
151    where
152        B: Into<Bytes<'a>>,
153    {
154        let key = Self::normalize_key(key);
155
156        match key.as_bytes() {
157            b"id" => self.id.as_mut().map(Option::as_mut),
158            b"class" => self.class.as_mut().map(Option::as_mut),
159            _ => self.raw.get_mut(&key).map(Option::as_mut),
160        }
161    }
162
163    /// Inserts a new attribute into this attributes collection
164    pub fn insert<K, V>(&mut self, key: K, value: Option<V>)
165    where
166        K: Into<Bytes<'a>>,
167        V: Into<Bytes<'a>>,
168    {
169        let key = Self::normalize_key(key);
170        let value = value.map(Into::into);
171
172        match key.as_bytes() {
173            b"id" => self.id = Some(value),
174            b"class" => self.class = Some(value),
175            _ => self.raw.insert(key, value),
176        };
177    }
178
179    /// Returns an iterator `(attribute_key, attribute_value)` over the attributes of this `HTMLTag`
180    pub fn iter(&self) -> impl Iterator<Item = (Cow<'_, str>, Option<Cow<'_, str>>)> + '_ {
181        self.raw
182            .iter()
183            .map(|(k, v)| {
184                let k = k.as_utf8_str();
185                let v = v.as_ref().map(|x| x.as_utf8_str());
186
187                (Some(k), v)
188            })
189            .chain([
190                (
191                    self.id.is_some().then_some(Cow::Borrowed("id")),
192                    self.id
193                        .as_ref()
194                        .and_then(Option::as_ref)
195                        .map(|x| x.as_utf8_str()),
196                ),
197                (
198                    self.class.is_some().then_some(Cow::Borrowed("class")),
199                    self.class
200                        .as_ref()
201                        .and_then(Option::as_ref)
202                        .map(|x| x.as_utf8_str()),
203                ),
204            ])
205            .flat_map(|(k, v)| k.map(|k| (k, v)))
206    }
207
208    /// Returns the `id` attribute of this HTML tag, if present
209    pub fn id(&self) -> Option<&Bytes<'a>> {
210        self.id.as_ref().and_then(Option::as_ref)
211    }
212
213    /// Returns the `class` attribute of this HTML tag, if present
214    pub fn class(&self) -> Option<&Bytes<'a>> {
215        self.class.as_ref().and_then(Option::as_ref)
216    }
217
218    /// Returns an iterator over all of the class members
219    pub fn class_iter(&self) -> Option<impl Iterator<Item = &'_ str> + '_> {
220        self.class
221            .as_ref()
222            .and_then(Option::as_ref)
223            .and_then(Bytes::try_as_utf8_str)
224            .map(str::split_ascii_whitespace)
225    }
226
227    /// Returns the underlying raw map for attributes
228    ///
229    /// ## A note on stability
230    /// It is not guaranteed for the returned map to include all attributes.
231    /// Some attributes may be stored in `Attributes` itself and not in the raw map.
232    /// For that reason you should prefer to call methods on `Attributes` directly,
233    /// i.e. `Attributes::get()` to lookup an attribute by its key.
234    pub fn unstable_raw(&self) -> &RawAttributesMap<'a> {
235        &self.raw
236    }
237}
238
239/// Represents a single HTML element
240#[derive(Debug, Clone)]
241pub struct HTMLTag<'a> {
242    pub(crate) _name: Bytes<'a>,
243    pub(crate) _attributes: Attributes<'a>,
244    pub(crate) _children: RawChildren,
245    pub(crate) _raw: Bytes<'a>,
246}
247
248impl<'a> HTMLTag<'a> {
249    /// Creates a new HTMLTag
250    #[inline(always)]
251    pub(crate) fn new(
252        name: Bytes<'a>,
253        attr: Attributes<'a>,
254        children: InlineVec<NodeHandle, INLINED_SUBNODES>,
255        raw: Bytes<'a>,
256    ) -> Self {
257        Self {
258            _name: name,
259            _attributes: attr,
260            _children: children,
261            _raw: raw,
262        }
263    }
264
265    /// Returns a wrapper around the children of this HTML tag
266    #[inline]
267    pub fn children(&self) -> Children<'a, '_> {
268        Children(self)
269    }
270
271    /// Returns a mutable wrapper around the children of this HTML tag.
272    pub fn children_mut(&mut self) -> ChildrenMut<'a, '_> {
273        ChildrenMut(self)
274    }
275
276    /// Returns the name of this HTML tag
277    #[inline]
278    pub fn name(&self) -> &Bytes<'a> {
279        &self._name
280    }
281
282    /// Returns a mutable reference to the name of this HTML tag
283    #[inline]
284    pub fn name_mut(&mut self) -> &mut Bytes<'a> {
285        &mut self._name
286    }
287
288    /// Returns attributes of this HTML tag
289    #[inline]
290    pub fn attributes(&self) -> &Attributes<'a> {
291        &self._attributes
292    }
293
294    /// Returns a mutable reference to the attributes of this HTML tag
295    #[inline]
296    pub fn attributes_mut(&mut self) -> &mut Attributes<'a> {
297        &mut self._attributes
298    }
299
300    /// Returns the contained markup
301    ///
302    /// ## Limitations
303    /// - The order of tag attributes is not guaranteed
304    /// - Spaces within the tag are not preserved (i.e. `<img      src="">` may become `<img src="">`)
305    ///
306    /// Equivalent to [Element#outerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML) in browsers.
307    pub fn outer_html<'p>(&'p self, parser: &'p Parser<'a>) -> String {
308        let tag_name = self._name.as_utf8_str();
309        let is_void_element = HTML_VOID_ELEMENTS.contains(&tag_name.as_ref());
310        let mut outer_html = format!("<{}", tag_name);
311
312        #[inline]
313        fn write_attribute(dest: &mut String, k: Cow<str>, v: Option<Cow<str>>) {
314            dest.push(' ');
315
316            dest.push_str(&k);
317
318            if let Some(value) = v {
319                dest.push_str("=\"");
320                dest.push_str(&value);
321                dest.push('"');
322            }
323        }
324
325        let attr = self.attributes();
326
327        for (k, v) in attr.iter() {
328            write_attribute(&mut outer_html, k, v);
329        }
330
331        outer_html.push('>');
332
333        // void elements have neither content nor a closing tag.
334        if is_void_element {
335            return outer_html;
336        }
337
338        // TODO(y21): More of an idea than a TODO, but a potential perf improvement
339        // could be having some kind of internal inner_html function that takes a &mut String
340        // and simply writes to it instead of returning a newly allocated string for every element
341        // and appending it
342        outer_html.push_str(&self.inner_html(parser));
343
344        outer_html.push_str("</");
345        outer_html.push_str(&self._name.as_utf8_str());
346        outer_html.push('>');
347
348        outer_html
349    }
350
351    /// Returns the contained markup
352    ///
353    /// ## Limitations
354    /// - The order of tag attributes is not guaranteed
355    /// - Spaces within the tag are not preserved (i.e. `<img      src="">` may become `<img src="">`)
356    ///
357    /// Equivalent to [Element#innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) in browsers.
358    pub fn inner_html<'p>(&'p self, parser: &'p Parser<'a>) -> String {
359        self.children()
360            .top()
361            .iter()
362            .map(|handle| handle.get(parser).unwrap())
363            .map(|node| node.outer_html(parser))
364            .collect::<String>()
365    }
366
367    /// Returns the raw HTML of this tag.
368    /// This is a cheaper version of `HTMLTag::inner_html` if you never mutate any nodes.
369    ///
370    /// **Note:** Mutating this tag does *not* re-compute the HTML representation of this tag.
371    /// This simply returns a reference to the substring.
372    pub fn raw(&self) -> &Bytes<'a> {
373        &self._raw
374    }
375
376    /// Returns the boundaries/position `(start, end)` of this HTML tag in the source string.
377    ///
378    /// # Example
379    /// ```
380    /// let source = "<p><span>hello</span></p>";
381    /// let dom = tl::parse(source, Default::default()).unwrap();
382    /// let parser = dom.parser();
383    /// let span = dom.nodes().iter().filter_map(|n| n.as_tag()).find(|n| n.name() == "span").unwrap();
384    /// let (start, end) = span.boundaries(parser);
385    /// assert_eq!((start, end), (3, 20));
386    /// assert_eq!(&source[start..=end], "<span>hello</span>");
387    /// ```
388    pub fn boundaries(&self, parser: &Parser<'a>) -> (usize, usize) {
389        let raw = self._raw.as_bytes();
390        let input = parser.stream.data().as_ptr();
391        let start = raw.as_ptr();
392        let offset = start as usize - input as usize;
393        let end = offset + raw.len() - 1;
394        (offset, end)
395    }
396
397    /// Returns the contained text of this element, excluding any markup.
398    /// Equivalent to [Element#innerText](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText) in browsers.
399    /// This function may not allocate memory for a new string as it can just return the part of the tag that doesn't have markup.
400    /// For tags that *do* have more than one subnode, this will allocate memory
401    pub fn inner_text<'p>(&self, parser: &'p Parser<'a>) -> Cow<'p, str> {
402        let len = self._children.len();
403
404        if len == 0 {
405            // If there are no subnodes, we can just return a static, empty, string slice
406            return Cow::Borrowed("");
407        }
408
409        let first = self._children[0].get(parser).unwrap();
410
411        if len == 1 {
412            match &first {
413                Node::Tag(t) => return t.inner_text(parser),
414                Node::Raw(e) => return e.as_utf8_str(),
415                Node::Comment(_) => return Cow::Borrowed(""),
416            }
417        }
418
419        // If there are >1 nodes, we need to allocate a new string and push each inner_text in it
420        // TODO: check if String::with_capacity() is worth it
421        let mut s = String::from(first.inner_text(parser));
422
423        for &id in self._children.iter().skip(1) {
424            let node = id.get(parser).unwrap();
425
426            match &node {
427                Node::Tag(t) => s.push_str(&t.inner_text(parser)),
428                Node::Raw(e) => s.push_str(&e.as_utf8_str()),
429                Node::Comment(_) => { /* no op */ }
430            }
431        }
432
433        Cow::Owned(s)
434    }
435
436    /// Tries to parse the query selector and returns an iterator over elements that match the given query selector.
437    ///
438    /// # Example
439    /// ```
440    /// let dom = tl::parse(r#"
441    ///     <div class="x">
442    ///     <div class="y">
443    ///       <div class="z">MATCH</div>
444    ///       <div class="z">MATCH</div>
445    ///       <div class="z">MATCH</div>
446    ///     </div>
447    ///   </div>
448    ///   <div class="z">NO MATCH</div>
449    ///   <div class="z">NO MATCH</div>
450    ///   <div class="z">NO MATCH</div>
451    /// "#, Default::default()).unwrap();
452    /// let parser = dom.parser();
453    ///
454    /// let outer = dom
455    ///     .get_elements_by_class_name("y")
456    ///     .next()
457    ///     .unwrap()
458    ///     .get(parser)
459    ///     .unwrap()
460    ///     .as_tag()
461    ///     .unwrap();
462    ///
463    /// let inner_z = outer.query_selector(parser, ".z").unwrap();
464    ///
465    /// assert_eq!(inner_z.clone().count(), 3);
466    ///
467    /// for handle in inner_z {
468    ///     let node = handle.get(parser).unwrap().as_tag().unwrap();
469    ///     assert_eq!(node.inner_text(parser), "MATCH");
470    /// }
471    ///
472    /// ```
473    pub fn query_selector<'b>(
474        &'b self,
475        parser: &'b Parser<'a>,
476        selector: &'b str,
477    ) -> Option<QuerySelectorIterator<'a, 'b, Self>> {
478        let selector = crate::parse_query_selector(selector)?;
479        let iter = queryselector::QuerySelectorIterator::new(selector, parser, self);
480        Some(iter)
481    }
482
483    /// Calls the given closure with each tag as parameter
484    ///
485    /// The closure must return a boolean, indicating whether it should stop iterating
486    /// Returning `true` will break the loop
487    pub fn find_node<F>(&self, parser: &Parser<'a>, f: &mut F) -> Option<NodeHandle>
488    where
489        F: FnMut(&Node<'a>) -> bool,
490    {
491        for &id in self._children.iter() {
492            let node = id.get(parser).unwrap();
493
494            if f(node) {
495                return Some(id);
496            }
497        }
498        None
499    }
500}
501
502/// A thin wrapper around the children of [`HTMLTag`]
503#[derive(Debug, Clone)]
504pub struct Children<'a, 'b>(&'b HTMLTag<'a>);
505
506impl<'a, 'b> Children<'a, 'b> {
507    /// Returns the topmost, direct children of this tag.
508    ///
509    /// # Example
510    /// ```
511    /// let dom = tl::parse(r#"
512    ///     <div id="a">
513    ///         <div id="b">
514    ///             <span>Hello</span>
515    ///             <span>World</span>
516    ///             <span>.</span>
517    ///         </div>
518    ///     </div>
519    /// "#, Default::default()).unwrap();
520    ///
521    /// let a = dom.get_element_by_id("a")
522    ///     .unwrap()
523    ///     .get(dom.parser())
524    ///     .unwrap()
525    ///     .as_tag()
526    ///     .unwrap();
527    ///
528    /// // Calling this function on the first div tag (#a) will return a slice containing 3 elements:
529    /// // - whitespaces around (before and after) div#b
530    /// // - div#b itself
531    /// // It does **not** contain the inner span tags
532    /// assert_eq!(a.children().top().len(), 3);
533    /// ```
534    #[inline]
535    pub fn top(&self) -> &RawChildren {
536        &self.0._children
537    }
538
539    /// Returns the starting boundary of the children of this tag.
540    #[inline]
541    pub fn start(&self) -> Option<InnerNodeHandle> {
542        self.0._children.get(0).map(NodeHandle::get_inner)
543    }
544
545    /// Returns the ending boundary of the children of this tag.
546    pub fn end(&self, parser: &Parser<'a>) -> Option<InnerNodeHandle> {
547        find_last_node_handle(self.0, parser).map(|h| h.get_inner())
548    }
549
550    /// Returns the (start, end) boundaries of the children of this tag.
551    #[inline]
552    pub fn boundaries(&self, parser: &Parser<'a>) -> Option<(InnerNodeHandle, InnerNodeHandle)> {
553        self.start().zip(self.end(parser))
554    }
555
556    /// Returns a slice containing all of the children of this [`HTMLTag`],
557    /// including all subnodes of the children.
558    ///
559    /// The difference between `top()` and `all()` is the same as `VDom::children()` and `VDom::nodes()`
560    ///
561    /// # Example
562    /// ```
563    /// let dom = tl::parse(r#"
564    ///     <div id="a"><div id="b"><span>Hello</span><span>World</span><span>!</span></div></div>
565    /// "#, Default::default()).unwrap();
566    ///
567    /// let a = dom.get_element_by_id("a")
568    ///     .unwrap()
569    ///     .get(dom.parser())
570    ///     .unwrap()
571    ///     .as_tag()
572    ///     .unwrap();
573    ///
574    /// // Calling this function on the first div tag (#a) will return a slice containing all of the subnodes:
575    /// // - div#b
576    /// // - span
577    /// // - Hello
578    /// // - span
579    /// // - World
580    /// // - span
581    /// // - !
582    /// assert_eq!(a.children().all(dom.parser()).len(), 7);
583    /// ```
584    pub fn all(&self, parser: &'b Parser<'a>) -> &'b [Node<'a>] {
585        self.boundaries(parser)
586            .map(|(start, end)| &parser.tags[start as usize..=end as usize])
587            .unwrap_or(&[])
588    }
589}
590
591/// A thin mutable wrapper around the children of [`HTMLTag`]
592#[derive(Debug)]
593pub struct ChildrenMut<'a, 'b>(&'b mut HTMLTag<'a>);
594
595impl<'a, 'b> ChildrenMut<'a, 'b> {
596    /// Returns the topmost, direct children of this tag as a mutable slice.
597    ///
598    /// See [`Children::top`] for more details and examples.
599    #[inline]
600    pub fn top_mut(&mut self) -> &mut RawChildren {
601        &mut self.0._children
602    }
603}
604
605/// Attempts to find the very last node handle that is contained in the given tag
606fn find_last_node_handle<'a>(tag: &HTMLTag<'a>, parser: &Parser<'a>) -> Option<NodeHandle> {
607    let last_handle = tag._children.as_slice().last().copied()?;
608
609    let child = last_handle
610        .get(parser)
611        .expect("Failed to get child node, please open a bug report") // this shouldn't happen
612        .as_tag();
613
614    if let Some(child) = child {
615        // Recursively call this function to get to the innermost node
616        find_last_node_handle(child, parser).or(Some(last_handle))
617    } else {
618        Some(last_handle)
619    }
620}
621
622/// An HTML Node
623#[derive(Debug, Clone)]
624pub enum Node<'a> {
625    /// A regular HTML element/tag
626    Tag(HTMLTag<'a>),
627    /// Raw text (no particular HTML element)
628    Raw(Bytes<'a>),
629    /// Comment (<!-- -->)
630    Comment(Bytes<'a>),
631}
632
633impl<'a> Node<'a> {
634    /// Returns the inner text of this node
635    pub fn inner_text<'s, 'p: 's>(&'s self, parser: &'p Parser<'a>) -> Cow<'s, str> {
636        match self {
637            Node::Comment(_) => Cow::Borrowed(""),
638            Node::Raw(r) => r.as_utf8_str(),
639            Node::Tag(t) => t.inner_text(parser),
640        }
641    }
642
643    /// Returns the outer HTML of this node
644    pub fn outer_html<'s>(&'s self, parser: &Parser<'a>) -> Cow<'s, str> {
645        match self {
646            Node::Comment(c) => c.as_utf8_str(),
647            Node::Raw(r) => r.as_utf8_str(),
648            Node::Tag(t) => Cow::Owned(t.outer_html(parser)),
649        }
650    }
651
652    /// Returns the inner HTML of this node
653    pub fn inner_html<'s>(&'s self, parser: &Parser<'a>) -> Cow<'s, str> {
654        match self {
655            Node::Comment(c) => c.as_utf8_str(),
656            Node::Raw(r) => r.as_utf8_str(),
657            Node::Tag(t) => Cow::Owned(t.inner_html(parser)),
658        }
659    }
660
661    /// Returns an iterator over subnodes ("children") of this HTML tag, if this is a tag
662    pub fn children(&self) -> Option<Children<'a, '_>> {
663        match self {
664            Node::Tag(t) => Some(t.children()),
665            _ => None,
666        }
667    }
668
669    /// Calls the given closure with each tag as parameter
670    ///
671    /// The closure must return a boolean, indicating whether it should stop iterating
672    /// Returning `true` will break the loop and return a handle to the node
673    pub fn find_node<F>(&self, parser: &Parser<'a>, f: &mut F) -> Option<NodeHandle>
674    where
675        F: FnMut(&Node<'a>) -> bool,
676    {
677        if let Some(children) = self.children() {
678            for &id in children.top().iter() {
679                let node = id.get(parser).unwrap();
680
681                if f(node) {
682                    return Some(id);
683                }
684
685                let subnode = node.find_node(parser, f);
686                if subnode.is_some() {
687                    return subnode;
688                }
689            }
690        }
691        None
692    }
693
694    /// Tries to coerce this node into a `HTMLTag` variant
695    pub fn as_tag(&self) -> Option<&HTMLTag<'a>> {
696        match self {
697            Self::Tag(tag) => Some(tag),
698            _ => None,
699        }
700    }
701
702    /// Tries to coerce this node into a `HTMLTag` variant
703    pub fn as_tag_mut(&mut self) -> Option<&mut HTMLTag<'a>> {
704        match self {
705            Self::Tag(tag) => Some(tag),
706            _ => None,
707        }
708    }
709
710    /// Tries to coerce this node into a comment, returning the text
711    pub fn as_comment(&self) -> Option<&Bytes<'a>> {
712        match self {
713            Self::Comment(c) => Some(c),
714            _ => None,
715        }
716    }
717
718    /// Tries to coerce this node into a comment, returning the text
719    pub fn as_comment_mut(&mut self) -> Option<&mut Bytes<'a>> {
720        match self {
721            Self::Comment(c) => Some(c),
722            _ => None,
723        }
724    }
725
726    /// Tries to coerce this node into a raw text node, returning the text
727    ///
728    /// "Raw text nodes" are nodes that are not HTML tags, but just text
729    pub fn as_raw(&self) -> Option<&Bytes<'a>> {
730        match self {
731            Self::Raw(r) => Some(r),
732            _ => None,
733        }
734    }
735
736    /// Tries to coerce this node into a mutable raw text node, returning the text
737    ///
738    /// "Raw text nodes" are nodes that are not HTML tags, but just text
739    pub fn as_raw_mut(&mut self) -> Option<&mut Bytes<'a>> {
740        match self {
741            Self::Raw(r) => Some(r),
742            _ => None,
743        }
744    }
745}