html_node_core/
lib.rs

1//! The core crate for [`html-node`](https://docs.rs/html-node).
2
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5/// HTTP Server integrations.
6mod http;
7
8/// [`Node`] variant definitions.
9mod node;
10
11/// Pretty printing utilities.
12#[cfg(feature = "pretty")]
13pub mod pretty;
14
15/// Typed HTML Nodes.
16#[cfg(feature = "typed")]
17pub mod typed;
18
19use std::fmt::{self, Display, Formatter};
20
21pub use self::node::*;
22#[cfg(feature = "typed")]
23use self::typed::TypedElement;
24
25/// An HTML node.
26#[derive(Debug, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub enum Node {
29    /// A comment.
30    ///
31    /// ```html
32    /// <!-- I'm a comment! -->
33    /// ```
34    Comment(Comment),
35
36    /// A doctype.
37    ///
38    /// ```html
39    /// <!DOCTYPE html>
40    /// ```
41    Doctype(Doctype),
42
43    /// A fragment.
44    ///
45    /// ```html
46    /// <>
47    ///     I'm in a fragment!
48    /// </>
49    /// ```
50    Fragment(Fragment),
51
52    /// An element.
53    ///
54    /// ```html
55    /// <div class="container">
56    ///     I'm in an element!
57    /// </div>
58    /// ```
59    Element(Element),
60
61    /// A text node.
62    ///
63    /// ```html
64    /// <div>
65    ///     I'm a text node!
66    /// </div>
67    /// ```
68    Text(Text),
69
70    /// An unsafe text node.
71    ///
72    /// # Warning
73    ///
74    /// [`Node::UnsafeText`] is not escaped when rendered, and as such, can
75    /// allow for XSS attacks. Use with caution!
76    UnsafeText(UnsafeText),
77}
78
79impl Node {
80    /// A [`Node::Fragment`] with no children.
81    pub const EMPTY: Self = Self::Fragment(Fragment::EMPTY);
82
83    /// Create a new [`Node`] from a [`TypedElement`].
84    #[cfg(feature = "typed")]
85    pub fn from_typed<E: TypedElement>(element: E, children: Option<Vec<Self>>) -> Self {
86        element.into_node(children)
87    }
88
89    /// Wrap the node in a pretty-printing wrapper.
90    #[cfg(feature = "pretty")]
91    #[must_use]
92    pub fn pretty(self) -> pretty::Pretty {
93        self.into()
94    }
95
96    /// Borrow the children of the node, if it is an element (with children) or
97    /// a fragment.
98    #[must_use]
99    pub fn as_children(&self) -> Option<&[Self]> {
100        match self {
101            Self::Fragment(fragment) => Some(&fragment.children),
102            Self::Element(element) => element.children.as_deref(),
103            _ => None,
104        }
105    }
106
107    /// Iterate over the children of the node.
108    pub fn children_iter(&self) -> impl Iterator<Item = &Self> {
109        self.as_children().unwrap_or_default().iter()
110    }
111
112    /// The children of the node, if it is an element (with children) or
113    /// a fragment.
114    #[must_use]
115    pub fn children(self) -> Option<Vec<Self>> {
116        match self {
117            Self::Fragment(fragment) => Some(fragment.children),
118            Self::Element(element) => element.children,
119            _ => None,
120        }
121    }
122
123    /// Iterate over the children of the node, consuming it.
124    pub fn into_children(self) -> impl Iterator<Item = Self> {
125        self.children().unwrap_or_default().into_iter()
126    }
127
128    /// Try to get this node as a [`Comment`], if it is one.
129    #[must_use]
130    pub const fn as_comment(&self) -> Option<&Comment> {
131        if let Self::Comment(comment) = self {
132            Some(comment)
133        } else {
134            None
135        }
136    }
137
138    /// Try to get this node as a [`Doctype`], if it is one.
139    #[must_use]
140    pub const fn as_doctype(&self) -> Option<&Doctype> {
141        if let Self::Doctype(doctype) = self {
142            Some(doctype)
143        } else {
144            None
145        }
146    }
147
148    /// Try to get this node as a [`Fragment`], if it is one.
149    #[must_use]
150    pub const fn as_fragment(&self) -> Option<&Fragment> {
151        if let Self::Fragment(fragment) = self {
152            Some(fragment)
153        } else {
154            None
155        }
156    }
157
158    /// Try to get this node as an [`Element`], if it is one.
159    #[must_use]
160    pub const fn as_element(&self) -> Option<&Element> {
161        if let Self::Element(element) = self {
162            Some(element)
163        } else {
164            None
165        }
166    }
167
168    /// Try to get this node as a [`Text`], if it is one.
169    #[must_use]
170    pub const fn as_text(&self) -> Option<&Text> {
171        if let Self::Text(text) = self {
172            Some(text)
173        } else {
174            None
175        }
176    }
177
178    /// Try to get this node as an [`UnsafeText`], if it is one.
179    #[must_use]
180    pub const fn as_unsafe_text(&self) -> Option<&UnsafeText> {
181        if let Self::UnsafeText(text) = self {
182            Some(text)
183        } else {
184            None
185        }
186    }
187}
188
189impl Default for Node {
190    fn default() -> Self {
191        Self::EMPTY
192    }
193}
194
195impl Display for Node {
196    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
197        match &self {
198            Self::Comment(comment) => comment.fmt(f),
199            Self::Doctype(doctype) => doctype.fmt(f),
200            Self::Fragment(fragment) => fragment.fmt(f),
201            Self::Element(element) => element.fmt(f),
202            Self::Text(text) => text.fmt(f),
203            Self::UnsafeText(unsafe_text) => unsafe_text.fmt(f),
204        }
205    }
206}
207
208impl<I, N> From<I> for Node
209where
210    I: IntoIterator<Item = N>,
211    N: Into<Self>,
212{
213    fn from(iter: I) -> Self {
214        Self::Fragment(iter.into())
215    }
216}
217
218impl From<Comment> for Node {
219    fn from(comment: Comment) -> Self {
220        Self::Comment(comment)
221    }
222}
223
224impl From<Doctype> for Node {
225    fn from(doctype: Doctype) -> Self {
226        Self::Doctype(doctype)
227    }
228}
229
230impl From<Fragment> for Node {
231    fn from(fragment: Fragment) -> Self {
232        Self::Fragment(fragment)
233    }
234}
235
236impl From<Element> for Node {
237    fn from(element: Element) -> Self {
238        Self::Element(element)
239    }
240}
241
242impl From<Text> for Node {
243    fn from(text: Text) -> Self {
244        Self::Text(text)
245    }
246}
247
248impl From<UnsafeText> for Node {
249    fn from(text: UnsafeText) -> Self {
250        Self::UnsafeText(text)
251    }
252}