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, ItemWriter};
12use crate::error::Result;
13
14use rxml::writer::Item;
15
16use std::convert::TryInto;
17use std::io::Write;
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}
27
28impl Node {
29 /// Turns this into a reference to an `Element` if this is an element node.
30 /// Else this returns `None`.
31 ///
32 /// # Examples
33 ///
34 /// ```rust
35 /// use minidom::Node;
36 ///
37 /// let elm = Node::Element("<meow xmlns=\"ns1\"/>".parse().unwrap());
38 /// let txt = Node::Text("meow".to_owned());
39 ///
40 /// assert_eq!(elm.as_element().unwrap().name(), "meow");
41 /// assert_eq!(txt.as_element(), None);
42 /// ```
43 pub fn as_element(&self) -> Option<&Element> {
44 match *self {
45 Node::Element(ref e) => Some(e),
46 Node::Text(_) => None,
47 }
48 }
49
50 /// Turns this into a mutable reference of an `Element` if this is an element node.
51 /// Else this returns `None`.
52 ///
53 /// # Examples
54 ///
55 /// ```rust
56 /// use minidom::Node;
57 ///
58 /// let mut elm = Node::Element("<meow xmlns=\"ns1\"/>".parse().unwrap());
59 /// let mut txt = Node::Text("meow".to_owned());
60 ///
61 /// assert_eq!(elm.as_element_mut().unwrap().name(), "meow");
62 /// assert_eq!(txt.as_element_mut(), None);
63 /// ```
64 pub fn as_element_mut(&mut self) -> Option<&mut Element> {
65 match *self {
66 Node::Element(ref mut e) => Some(e),
67 Node::Text(_) => None,
68 }
69 }
70
71 /// Turns this into an `Element`, consuming self, if this is an element node.
72 /// Else this returns `None`.
73 ///
74 /// # Examples
75 ///
76 /// ```rust
77 /// use minidom::Node;
78 ///
79 /// let elm = Node::Element("<meow xmlns=\"ns1\"/>".parse().unwrap());
80 /// let txt = Node::Text("meow".to_owned());
81 ///
82 /// assert_eq!(elm.into_element().unwrap().name(), "meow");
83 /// assert_eq!(txt.into_element(), None);
84 /// ```
85 pub fn into_element(self) -> Option<Element> {
86 match self {
87 Node::Element(e) => Some(e),
88 Node::Text(_) => None,
89 }
90 }
91
92 /// Turns this into an `&str` if this is a text node.
93 /// Else this returns `None`.
94 ///
95 /// # Examples
96 ///
97 /// ```rust
98 /// use minidom::Node;
99 ///
100 /// let elm = Node::Element("<meow xmlns=\"ns1\"/>".parse().unwrap());
101 /// let txt = Node::Text("meow".to_owned());
102 ///
103 /// assert_eq!(elm.as_text(), None);
104 /// assert_eq!(txt.as_text().unwrap(), "meow");
105 /// ```
106 pub fn as_text(&self) -> Option<&str> {
107 match *self {
108 Node::Element(_) => None,
109 Node::Text(ref s) => Some(s),
110 }
111 }
112
113 /// Turns this into an `&mut String` if this is a text node.
114 /// Else this returns `None`.
115 ///
116 /// # Examples
117 ///
118 /// ```rust
119 /// use minidom::Node;
120 ///
121 /// let mut elm = Node::Element("<meow xmlns=\"ns1\"/>".parse().unwrap());
122 /// let mut txt = Node::Text("meow".to_owned());
123 ///
124 /// assert_eq!(elm.as_text_mut(), None);
125 /// {
126 /// let text_mut = txt.as_text_mut().unwrap();
127 /// assert_eq!(text_mut, "meow");
128 /// text_mut.push_str("zies");
129 /// assert_eq!(text_mut, "meowzies");
130 /// }
131 /// assert_eq!(txt.as_text().unwrap(), "meowzies");
132 /// ```
133 pub fn as_text_mut(&mut self) -> Option<&mut String> {
134 match *self {
135 Node::Element(_) => None,
136 Node::Text(ref mut s) => Some(s),
137 }
138 }
139
140 /// Turns this into an `String`, consuming self, if this is a text node.
141 /// Else this returns `None`.
142 ///
143 /// # Examples
144 ///
145 /// ```rust
146 /// use minidom::Node;
147 ///
148 /// let elm = Node::Element("<meow xmlns=\"ns1\"/>".parse().unwrap());
149 /// let txt = Node::Text("meow".to_owned());
150 ///
151 /// assert_eq!(elm.into_text(), None);
152 /// assert_eq!(txt.into_text().unwrap(), "meow");
153 /// ```
154 pub fn into_text(self) -> Option<String> {
155 match self {
156 Node::Element(_) => None,
157 Node::Text(s) => Some(s),
158 }
159 }
160
161 #[doc(hidden)]
162 pub(crate) fn write_to_inner<W: Write>(&self, writer: &mut ItemWriter<W>) -> Result<()> {
163 match *self {
164 Node::Element(ref elmt) => elmt.write_to_inner(writer)?,
165 Node::Text(ref s) => {
166 writer.write(Item::Text((&**s).try_into()?))?;
167 }
168 }
169
170 Ok(())
171 }
172}
173
174impl<I> From<I> for Node
175where
176 I: Into<Element>,
177{
178 fn from(elm: I) -> Node {
179 Node::Element(elm.into())
180 }
181}
182
183impl From<String> for Node {
184 fn from(s: String) -> Node {
185 Node::Text(s)
186 }
187}
188
189impl<'a> From<&'a str> for Node {
190 fn from(s: &'a str) -> Node {
191 Node::Text(s.to_owned())
192 }
193}
194
195impl From<ElementBuilder> for Node {
196 fn from(builder: ElementBuilder) -> Node {
197 Node::Element(builder.build())
198 }
199}
200
201impl PartialEq for Node {
202 fn eq(&self, other: &Self) -> bool {
203 match (self, other) {
204 (&Node::Element(ref elem1), &Node::Element(ref elem2)) => elem1 == elem2,
205 (&Node::Text(ref text1), &Node::Text(ref text2)) => text1 == text2,
206 _ => false,
207 }
208 }
209}