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