Skip to main content

typst_batch/html/
element.rs

1//! HTML element wrapper.
2
3use super::node::HtmlNode;
4
5/// An HTML element.
6///
7/// Provides stable access to element tag, attributes, and children
8/// without exposing internal typst types like `PicoStr` or `EcoVec`.
9#[derive(Debug, Clone, Copy)]
10pub struct HtmlElement<'a>(pub(crate) &'a typst_html::HtmlElement);
11
12impl<'a> HtmlElement<'a> {
13    /// Get the element's tag name as an owned String.
14    ///
15    /// # Example
16    ///
17    /// ```ignore
18    /// let tag = elem.tag();
19    /// assert_eq!(tag, "div");
20    /// ```
21    #[inline]
22    pub fn tag(&self) -> String {
23        self.0.tag.resolve().to_string()
24    }
25
26    /// Iterate over the element's attributes as owned (String, String) pairs.
27    ///
28    /// # Example
29    ///
30    /// ```ignore
31    /// for (key, value) in elem.attrs() {
32    ///     println!("{}: {}", key, value);
33    /// }
34    /// ```
35    #[inline]
36    pub fn attrs(&self) -> impl Iterator<Item = (String, String)> + '_ {
37        self.0
38            .attrs
39            .0
40            .iter()
41            .map(|(k, v)| (k.resolve().to_string(), v.to_string()))
42    }
43
44    /// Collect attributes into a Vec for convenience.
45    ///
46    /// This is useful when you need to iterate multiple times or store the attributes.
47    #[inline]
48    pub fn attrs_vec(&self) -> Vec<(String, String)> {
49        self.attrs().collect()
50    }
51
52    /// Get an attribute value by name.
53    ///
54    /// # Example
55    ///
56    /// ```ignore
57    /// if let Some(class) = elem.get_attr("class") {
58    ///     println!("class: {}", class);
59    /// }
60    /// ```
61    pub fn get_attr(&self, name: &str) -> Option<String> {
62        self.0.attrs.0.iter().find_map(|(k, v)| {
63            if k.resolve().as_str() == name {
64                Some(v.to_string())
65            } else {
66                None
67            }
68        })
69    }
70
71    /// Check if the element has an attribute.
72    #[inline]
73    pub fn has_attr(&self, name: &str) -> bool {
74        self.0
75            .attrs
76            .0
77            .iter()
78            .any(|(k, _)| k.resolve().as_str() == name)
79    }
80
81    /// Get the element's id attribute.
82    #[inline]
83    pub fn id(&self) -> Option<String> {
84        self.get_attr("id")
85    }
86
87    /// Get the element's class attribute.
88    #[inline]
89    pub fn class(&self) -> Option<String> {
90        self.get_attr("class")
91    }
92
93    /// Iterate over the element's children.
94    ///
95    /// # Example
96    ///
97    /// ```ignore
98    /// for child in elem.children() {
99    ///     match child.kind() {
100    ///         NodeKind::Element(e) => println!("Child element: {}", e.tag()),
101    ///         NodeKind::Text(t) => println!("Text: {}", t),
102    ///         _ => {}
103    ///     }
104    /// }
105    /// ```
106    #[inline]
107    pub fn children(&self) -> impl Iterator<Item = HtmlNode<'a>> {
108        self.0.children.iter().map(HtmlNode)
109    }
110
111    /// Get the number of children.
112    #[inline]
113    pub fn children_count(&self) -> usize {
114        self.0.children.len()
115    }
116
117    /// Check if the element has no children.
118    #[inline]
119    pub fn is_empty(&self) -> bool {
120        self.0.children.is_empty()
121    }
122}