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}