1use std::{borrow::Cow, error::Error, fmt, io::Cursor, sync::Arc};
4
5use quick_xml::{
6 events::{
7 attributes::Attribute as XmlAttribute, BytesCData, BytesDecl, BytesEnd, BytesPI,
8 BytesStart, BytesText, Event,
9 },
10 name::QName,
11 Writer,
12};
13
14use crate::parser::{parse_document, ParseError, ParseOptions};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct XmlDeclaration {
19 pub version: String,
20 pub encoding: Option<String>,
21 pub standalone: Option<String>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct Attribute {
27 qname: String,
28 prefix: Option<String>,
29 local_name: String,
30 namespace_uri: Option<Arc<str>>,
31 value: String,
32}
33
34impl Attribute {
35 pub(crate) fn parsed(
36 qname: String,
37 prefix: Option<String>,
38 local_name: String,
39 namespace_uri: Option<Arc<str>>,
40 value: String,
41 ) -> Self {
42 Self {
43 qname,
44 prefix,
45 local_name,
46 namespace_uri,
47 value,
48 }
49 }
50
51 #[must_use]
52 pub fn qname(&self) -> &str {
53 &self.qname
54 }
55
56 #[must_use]
57 pub fn prefix(&self) -> Option<&str> {
58 self.prefix.as_deref()
59 }
60
61 #[must_use]
62 pub fn local_name(&self) -> &str {
63 &self.local_name
64 }
65
66 #[must_use]
67 pub fn namespace_uri(&self) -> Option<&str> {
68 self.namespace_uri.as_deref()
69 }
70
71 #[must_use]
72 pub fn value(&self) -> &str {
73 &self.value
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct Element {
80 qname: String,
81 prefix: Option<String>,
82 local_name: String,
83 namespace_uri: Option<Arc<str>>,
84 attributes: Vec<Attribute>,
85 nodes: Vec<Node>,
86 empty_style: bool,
87}
88
89impl Element {
90 pub(crate) fn parsed(
91 qname: String,
92 prefix: Option<String>,
93 local_name: String,
94 namespace_uri: Option<Arc<str>>,
95 attributes: Vec<Attribute>,
96 empty_style: bool,
97 ) -> Self {
98 Self {
99 qname,
100 prefix,
101 local_name,
102 namespace_uri,
103 attributes,
104 nodes: Vec::new(),
105 empty_style,
106 }
107 }
108
109 #[must_use]
110 pub fn qname(&self) -> &str {
111 &self.qname
112 }
113
114 #[must_use]
115 pub fn prefix(&self) -> Option<&str> {
116 self.prefix.as_deref()
117 }
118
119 #[must_use]
120 pub fn local_name(&self) -> &str {
121 &self.local_name
122 }
123
124 #[must_use]
125 pub fn namespace_uri(&self) -> Option<&str> {
126 self.namespace_uri.as_deref()
127 }
128
129 #[must_use]
130 pub fn attributes(&self) -> &[Attribute] {
131 &self.attributes
132 }
133
134 #[must_use]
135 pub fn nodes(&self) -> &[Node] {
136 &self.nodes
137 }
138
139 #[must_use]
141 pub const fn was_empty_element(&self) -> bool {
142 self.empty_style
143 }
144
145 pub fn children(&self) -> impl Iterator<Item = &Element> {
147 self.nodes.iter().filter_map(Node::as_element)
148 }
149
150 #[must_use]
152 pub fn attribute_ns(&self, namespace_uri: Option<&str>, local_name: &str) -> Option<&str> {
153 self.attributes
154 .iter()
155 .find(|attribute| {
156 attribute.namespace_uri() == namespace_uri && attribute.local_name() == local_name
157 })
158 .map(Attribute::value)
159 }
160
161 #[must_use]
163 pub fn direct_text(&self) -> String {
164 let mut result = String::new();
165 for node in &self.nodes {
166 match node {
167 Node::Text(value) | Node::CData(value) => result.push_str(value),
168 _ => {}
169 }
170 }
171 result
172 }
173
174 pub(crate) fn push(&mut self, node: Node) {
175 push_coalescing_text(&mut self.nodes, node);
176 }
177}
178
179pub(crate) fn push_coalescing_text(nodes: &mut Vec<Node>, node: Node) {
180 if let Node::Text(value) = node {
181 if let Some(Node::Text(existing)) = nodes.last_mut() {
182 existing.push_str(&value);
183 } else {
184 nodes.push(Node::Text(value));
185 }
186 } else {
187 nodes.push(node);
188 }
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
193pub enum Node {
194 Element(Element),
195 Text(String),
196 CData(String),
197 Comment(String),
198 ProcessingInstruction(String),
200}
201
202impl Node {
203 #[must_use]
204 pub fn as_element(&self) -> Option<&Element> {
205 match self {
206 Self::Element(element) => Some(element),
207 _ => None,
208 }
209 }
210}
211
212#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct Document {
215 declaration: Option<XmlDeclaration>,
216 prolog: Vec<Node>,
217 root: Element,
218 epilog: Vec<Node>,
219}
220
221impl Document {
222 pub fn parse(xml: &str) -> Result<Self, ParseError> {
224 Self::parse_with_options(xml, ParseOptions::default())
225 }
226
227 pub fn parse_with_options(xml: &str, options: ParseOptions) -> Result<Self, ParseError> {
229 parse_document(xml, options)
230 }
231
232 pub(crate) fn parsed(
233 declaration: Option<XmlDeclaration>,
234 prolog: Vec<Node>,
235 root: Element,
236 epilog: Vec<Node>,
237 ) -> Self {
238 Self {
239 declaration,
240 prolog,
241 root,
242 epilog,
243 }
244 }
245
246 #[must_use]
247 pub const fn declaration(&self) -> Option<&XmlDeclaration> {
248 self.declaration.as_ref()
249 }
250
251 #[must_use]
252 pub fn prolog(&self) -> &[Node] {
253 &self.prolog
254 }
255
256 #[must_use]
257 pub const fn root(&self) -> &Element {
258 &self.root
259 }
260
261 #[must_use]
262 pub fn epilog(&self) -> &[Node] {
263 &self.epilog
264 }
265
266 pub fn to_xml_string(&self) -> Result<String, WriteError> {
268 let mut writer = Writer::new(Cursor::new(Vec::new()));
269 if let Some(declaration) = &self.declaration {
270 writer.write_event(Event::Decl(BytesDecl::new(
271 &declaration.version,
272 declaration.encoding.as_deref(),
273 declaration.standalone.as_deref(),
274 )))?;
275 }
276 for node in &self.prolog {
277 write_node(&mut writer, node)?;
278 }
279 write_element(&mut writer, &self.root)?;
280 for node in &self.epilog {
281 write_node(&mut writer, node)?;
282 }
283 String::from_utf8(writer.into_inner().into_inner()).map_err(WriteError::Utf8)
284 }
285
286 pub(crate) fn take_root(self) -> Element {
287 self.root
288 }
289}
290
291fn write_element(
292 writer: &mut Writer<Cursor<Vec<u8>>>,
293 element: &Element,
294) -> Result<(), WriteError> {
295 let mut start = BytesStart::new(element.qname());
296 for attribute in element.attributes() {
297 start.push_attribute(XmlAttribute {
298 key: QName(attribute.qname().as_bytes()),
299 value: Cow::Owned(escape_attribute_value(attribute.value())),
300 });
301 }
302 if element.empty_style && element.nodes.is_empty() {
303 writer.write_event(Event::Empty(start))?;
304 return Ok(());
305 }
306 writer.write_event(Event::Start(start))?;
307 for node in element.nodes() {
308 write_node(writer, node)?;
309 }
310 writer.write_event(Event::End(BytesEnd::new(element.qname())))?;
311 Ok(())
312}
313
314fn escape_attribute_value(value: &str) -> Vec<u8> {
315 let mut output = String::with_capacity(value.len());
316 for character in value.chars() {
317 match character {
318 '&' => output.push_str("&"),
319 '<' => output.push_str("<"),
320 '"' => output.push_str("""),
321 '\t' => output.push_str("	"),
322 '\n' => output.push_str("
"),
323 '\r' => output.push_str("
"),
324 _ => output.push(character),
325 }
326 }
327 output.into_bytes()
328}
329
330fn escape_text_value(value: &str) -> String {
331 let mut output = String::with_capacity(value.len());
332 for character in value.chars() {
333 match character {
334 '&' => output.push_str("&"),
335 '<' => output.push_str("<"),
336 '>' => output.push_str(">"),
337 '\r' => output.push_str(" "),
338 _ => output.push(character),
339 }
340 }
341 output
342}
343
344fn write_node(writer: &mut Writer<Cursor<Vec<u8>>>, node: &Node) -> Result<(), WriteError> {
345 match node {
346 Node::Element(element) => write_element(writer, element),
347 Node::Text(value) => writer
348 .write_event(Event::Text(BytesText::from_escaped(escape_text_value(
349 value,
350 ))))
351 .map_err(Into::into),
352 Node::CData(value) => writer
353 .write_event(Event::CData(BytesCData::new(value)))
354 .map_err(Into::into),
355 Node::Comment(value) => writer
356 .write_event(Event::Comment(BytesText::new(value)))
357 .map_err(Into::into),
358 Node::ProcessingInstruction(value) => writer
359 .write_event(Event::PI(BytesPI::new(value)))
360 .map_err(Into::into),
361 }
362}
363
364#[derive(Debug)]
366pub enum WriteError {
367 Xml(std::io::Error),
368 Utf8(std::string::FromUtf8Error),
369}
370
371impl fmt::Display for WriteError {
372 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
373 match self {
374 Self::Xml(error) => write!(formatter, "could not write XML: {error}"),
375 Self::Utf8(error) => write!(formatter, "XML writer produced invalid UTF-8: {error}"),
376 }
377 }
378}
379
380impl Error for WriteError {
381 fn source(&self) -> Option<&(dyn Error + 'static)> {
382 match self {
383 Self::Xml(error) => Some(error),
384 Self::Utf8(error) => Some(error),
385 }
386 }
387}
388
389impl From<std::io::Error> for WriteError {
390 fn from(value: std::io::Error) -> Self {
391 Self::Xml(value)
392 }
393}