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}