1// Copyright (c) 2020 lumi <lumi@pew.im>
2// Copyright (c) 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
3// Copyright (c) 2020 Maxime “pep” Buquet <pep@bouah.net>
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9//! Provides the `Node` struct, which represents a node in the DOM.
10
11use crate::element::{Element, ElementBuilder};
12use crate::error::Result;
13
14use std::io::Write;
15
16use quick_xml::events::{BytesText, Event};
17use quick_xml::Writer as EventWriter;
18
19/// A node in an element tree.
20#[derive(Clone, Debug, Eq)]
21pub enum Node {
22 /// An `Element`.
23 Element(Element),
24 /// A text node.
25 Text(String),
26 #[cfg(feature = "comments")]
27 /// A comment node.
28 Comment(String),
29}
30
31impl Node {
32 /// Turns this into a reference to an `Element` if this is an element node.
33 /// Else this returns `None`.
34 ///
35 /// # Examples
36 ///
37 /// ```rust
38 /// use minidom::Node;
39 ///
40 /// let elm = Node::Element("<meow />".parse().unwrap());
41 /// let txt = Node::Text("meow".to_owned());
42 ///
43 /// assert_eq!(elm.as_element().unwrap().name(), "meow");
44 /// assert_eq!(txt.as_element(), None);
45 /// ```
46 pub fn as_element(&self) -> Option<&Element> {
47 match *self {
48 Node::Element(ref e) => Some(e),
49 Node::Text(_) => None,
50 #[cfg(feature = "comments")]
51 Node::Comment(_) => None,
52 }
53 }
54
55 /// Turns this into a mutable reference of an `Element` if this is an element node.
56 /// Else this returns `None`.
57 ///
58 /// # Examples
59 ///
60 /// ```rust
61 /// use minidom::Node;
62 ///
63 /// let mut elm = Node::Element("<meow />".parse().unwrap());
64 /// let mut txt = Node::Text("meow".to_owned());
65 ///
66 /// assert_eq!(elm.as_element_mut().unwrap().name(), "meow");
67 /// assert_eq!(txt.as_element_mut(), None);
68 /// ```
69 pub fn as_element_mut(&mut self) -> Option<&mut Element> {
70 match *self {
71 Node::Element(ref mut e) => Some(e),
72 Node::Text(_) => None,
73 #[cfg(feature = "comments")]
74 Node::Comment(_) => None,
75 }
76 }
77
78 /// Turns this into an `Element`, consuming self, if this is an element node.
79 /// Else this returns `None`.
80 ///
81 /// # Examples
82 ///
83 /// ```rust
84 /// use minidom::Node;
85 ///
86 /// let elm = Node::Element("<meow />".parse().unwrap());
87 /// let txt = Node::Text("meow".to_owned());
88 ///
89 /// assert_eq!(elm.into_element().unwrap().name(), "meow");
90 /// assert_eq!(txt.into_element(), None);
91 /// ```
92 pub fn into_element(self) -> Option<Element> {
93 match self {
94 Node::Element(e) => Some(e),
95 Node::Text(_) => None,
96 #[cfg(feature = "comments")]
97 Node::Comment(_) => None,
98 }
99 }
100
101 /// Turns this into an `&str` if this is a text node.
102 /// Else this returns `None`.
103 ///
104 /// # Examples
105 ///
106 /// ```rust
107 /// use minidom::Node;
108 ///
109 /// let elm = Node::Element("<meow />".parse().unwrap());
110 /// let txt = Node::Text("meow".to_owned());
111 ///
112 /// assert_eq!(elm.as_text(), None);
113 /// assert_eq!(txt.as_text().unwrap(), "meow");
114 /// ```
115 pub fn as_text(&self) -> Option<&str> {
116 match *self {
117 Node::Element(_) => None,
118 Node::Text(ref s) => Some(s),
119 #[cfg(feature = "comments")]
120 Node::Comment(_) => None,
121 }
122 }
123
124 /// Turns this into an `&mut String` if this is a text node.
125 /// Else this returns `None`.
126 ///
127 /// # Examples
128 ///
129 /// ```rust
130 /// use minidom::Node;
131 ///
132 /// let mut elm = Node::Element("<meow />".parse().unwrap());
133 /// let mut txt = Node::Text("meow".to_owned());
134 ///
135 /// assert_eq!(elm.as_text_mut(), None);
136 /// {
137 /// let text_mut = txt.as_text_mut().unwrap();
138 /// assert_eq!(text_mut, "meow");
139 /// text_mut.push_str("zies");
140 /// assert_eq!(text_mut, "meowzies");
141 /// }
142 /// assert_eq!(txt.as_text().unwrap(), "meowzies");
143 /// ```
144 pub fn as_text_mut(&mut self) -> Option<&mut String> {
145 match *self {
146 Node::Element(_) => None,
147 Node::Text(ref mut s) => Some(s),
148 #[cfg(feature = "comments")]
149 Node::Comment(_) => None,
150 }
151 }
152
153 /// Turns this into an `String`, consuming self, if this is a text node.
154 /// Else this returns `None`.
155 ///
156 /// # Examples
157 ///
158 /// ```rust
159 /// use minidom::Node;
160 ///
161 /// let elm = Node::Element("<meow />".parse().unwrap());
162 /// let txt = Node::Text("meow".to_owned());
163 ///
164 /// assert_eq!(elm.into_text(), None);
165 /// assert_eq!(txt.into_text().unwrap(), "meow");
166 /// ```
167 pub fn into_text(self) -> Option<String> {
168 match self {
169 Node::Element(_) => None,
170 Node::Text(s) => Some(s),
171 #[cfg(feature = "comments")]
172 Node::Comment(_) => None,
173 }
174 }
175
176 #[doc(hidden)]
177 pub(crate) fn write_to_inner<W: Write>(&self, writer: &mut EventWriter<W>) -> Result<()> {
178 match *self {
179 Node::Element(ref elmt) => elmt.write_to_inner(writer)?,
180 Node::Text(ref s) => {
181 writer.write_event(Event::Text(BytesText::from_plain_str(s)))?;
182 }
183 #[cfg(feature = "comments")]
184 Node::Comment(ref s) => {
185 writer.write_event(Event::Comment(BytesText::from_plain_str(s)))?;
186 }
187 }
188
189 Ok(())
190 }
191}
192
193impl<I> From<I> for Node
194where
195 I: Into<Element>,
196{
197 fn from(elm: I) -> Node {
198 Node::Element(elm.into())
199 }
200}
201
202impl From<String> for Node {
203 fn from(s: String) -> Node {
204 Node::Text(s)
205 }
206}
207
208impl<'a> From<&'a str> for Node {
209 fn from(s: &'a str) -> Node {
210 Node::Text(s.to_owned())
211 }
212}
213
214impl From<ElementBuilder> for Node {
215 fn from(builder: ElementBuilder) -> Node {
216 Node::Element(builder.build())
217 }
218}
219
220impl PartialEq for Node {
221 fn eq(&self, other: &Self) -> bool {
222 match (self, other) {
223 (&Node::Element(ref elem1), &Node::Element(ref elem2)) => elem1 == elem2,
224 (&Node::Text(ref text1), &Node::Text(ref text2)) => text1 == text2,
225 #[cfg(feature = "comments")]
226 (&Node::Comment(ref text1), &Node::Comment(ref text2)) => text1 == text2,
227 _ => false,
228 }
229 }
230}