1use std::{borrow::Cow, error::Error, fmt, io::Cursor, sync::Arc};
14
15use quick_xml::{
16 events::{
17 attributes::Attribute as XmlAttribute, BytesCData, BytesDecl, BytesEnd, BytesPI,
18 BytesStart, BytesText, Event,
19 },
20 name::QName,
21 Writer,
22};
23
24use crate::parser::{parse_document, ParseError, ParseOptions};
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub(crate) struct Span {
33 pub(crate) start: u64,
34 pub(crate) end: u64,
35}
36
37impl Span {
38 pub(crate) const fn new(start: u64, end: u64) -> Self {
39 Self { start, end }
40 }
41
42 pub(crate) const fn union(self, other: Self) -> Self {
46 Self {
47 start: if self.start < other.start {
48 self.start
49 } else {
50 other.start
51 },
52 end: if self.end > other.end {
53 self.end
54 } else {
55 other.end
56 },
57 }
58 }
59
60 fn slice(self, source: &str) -> Option<&str> {
61 let start = usize::try_from(self.start).ok()?;
62 let end = usize::try_from(self.end).ok()?;
63 source.get(start..end)
64 }
65}
66
67#[derive(Debug, Clone, Default, Eq)]
76pub(crate) struct NodeList {
77 nodes: Vec<Node>,
78 spans: Vec<Option<Span>>,
79}
80
81impl PartialEq for NodeList {
82 fn eq(&self, other: &Self) -> bool {
83 self.nodes == other.nodes
84 }
85}
86
87impl NodeList {
88 pub(crate) const fn new() -> Self {
89 Self {
90 nodes: Vec::new(),
91 spans: Vec::new(),
92 }
93 }
94
95 pub(crate) fn nodes(&self) -> &[Node] {
96 &self.nodes
97 }
98
99 fn spans(&self) -> &[Option<Span>] {
100 &self.spans
101 }
102
103 fn is_empty(&self) -> bool {
104 self.nodes.is_empty()
105 }
106
107 pub(crate) fn push(&mut self, node: Node, span: Option<Span>) {
114 if let Node::Text(value) = node {
115 if let Some(Node::Text(existing)) = self.nodes.last_mut() {
116 existing.push_str(&value);
117 let last = self.spans.len() - 1;
118 self.spans[last] = match (self.spans[last], span) {
119 (Some(previous), Some(next)) => Some(previous.union(next)),
120 _ => None,
121 };
122 return;
123 }
124 self.nodes.push(Node::Text(value));
125 } else {
126 self.nodes.push(node);
127 }
128 self.spans.push(span);
129 }
130}
131
132#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct XmlDeclaration {
135 pub version: String,
136 pub encoding: Option<String>,
137 pub standalone: Option<String>,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct Attribute {
143 qname: String,
144 prefix: Option<String>,
145 local_name: String,
146 namespace_uri: Option<Arc<str>>,
147 value: String,
148}
149
150impl Attribute {
151 pub(crate) fn parsed(
152 qname: String,
153 prefix: Option<String>,
154 local_name: String,
155 namespace_uri: Option<Arc<str>>,
156 value: String,
157 ) -> Self {
158 Self {
159 qname,
160 prefix,
161 local_name,
162 namespace_uri,
163 value,
164 }
165 }
166
167 #[must_use]
174 pub fn new(
175 prefix: Option<&str>,
176 local_name: &str,
177 namespace_uri: Option<&str>,
178 value: impl Into<String>,
179 ) -> Self {
180 let qname = match prefix {
181 Some(prefix) if !prefix.is_empty() => format!("{prefix}:{local_name}"),
182 _ => local_name.to_owned(),
183 };
184 Self {
185 qname,
186 prefix: prefix.filter(|value| !value.is_empty()).map(str::to_owned),
187 local_name: local_name.to_owned(),
188 namespace_uri: namespace_uri.map(Arc::from),
189 value: value.into(),
190 }
191 }
192
193 #[must_use]
194 pub fn qname(&self) -> &str {
195 &self.qname
196 }
197
198 #[must_use]
199 pub fn prefix(&self) -> Option<&str> {
200 self.prefix.as_deref()
201 }
202
203 #[must_use]
204 pub fn local_name(&self) -> &str {
205 &self.local_name
206 }
207
208 #[must_use]
209 pub fn namespace_uri(&self) -> Option<&str> {
210 self.namespace_uri.as_deref()
211 }
212
213 #[must_use]
214 pub fn value(&self) -> &str {
215 &self.value
216 }
217}
218
219#[derive(Debug, Clone, Eq)]
225pub struct Element {
226 qname: String,
227 prefix: Option<String>,
228 local_name: String,
229 namespace_uri: Option<Arc<str>>,
230 attributes: Vec<Attribute>,
231 nodes: NodeList,
232 empty_style: bool,
233 open_span: Option<Span>,
235 close_span: Option<Span>,
237}
238
239impl PartialEq for Element {
240 fn eq(&self, other: &Self) -> bool {
241 self.qname == other.qname
245 && self.prefix == other.prefix
246 && self.local_name == other.local_name
247 && self.namespace_uri == other.namespace_uri
248 && self.attributes == other.attributes
249 && self.nodes == other.nodes
250 }
251}
252
253impl Element {
254 pub(crate) fn parsed(
255 qname: String,
256 prefix: Option<String>,
257 local_name: String,
258 namespace_uri: Option<Arc<str>>,
259 attributes: Vec<Attribute>,
260 empty_style: bool,
261 open_span: Option<Span>,
262 ) -> Self {
263 Self {
264 qname,
265 prefix,
266 local_name,
267 namespace_uri,
268 attributes,
269 nodes: NodeList::new(),
270 empty_style,
271 open_span,
272 close_span: None,
273 }
274 }
275
276 pub(crate) fn set_close_span(&mut self, span: Option<Span>) {
277 self.close_span = span;
278 }
279
280 #[must_use]
291 pub fn new(namespace: Option<&str>, prefix: Option<&str>, local_name: &str) -> Self {
292 let prefix = prefix.filter(|value| !value.is_empty()).map(str::to_owned);
293 let qname = match &prefix {
294 Some(prefix) => format!("{prefix}:{local_name}"),
295 None => local_name.to_owned(),
296 };
297 Self {
298 qname,
299 prefix,
300 local_name: local_name.to_owned(),
301 namespace_uri: namespace.map(Arc::from),
302 attributes: Vec::new(),
303 nodes: NodeList::new(),
304 empty_style: false,
305 open_span: None,
306 close_span: None,
307 }
308 }
309
310 #[must_use]
312 pub fn with_attribute(mut self, attribute: Attribute) -> Self {
313 self.attributes.push(attribute);
314 self
315 }
316
317 #[must_use]
320 pub fn with_child(mut self, child: Element) -> Self {
321 self.nodes.push(Node::Element(child), None);
322 self
323 }
324
325 #[must_use]
327 pub fn with_text(mut self, text: impl Into<String>) -> Self {
328 self.nodes.push(Node::Text(text.into()), None);
329 self
330 }
331
332 pub(crate) fn full_span(&self) -> Option<Span> {
336 let open = self.open_span?;
337 match self.close_span {
338 Some(close) => Some(open.union(close)),
339 None if self.empty_style => Some(open),
341 None => None,
342 }
343 }
344
345 #[must_use]
346 pub fn qname(&self) -> &str {
347 &self.qname
348 }
349
350 #[must_use]
351 pub fn prefix(&self) -> Option<&str> {
352 self.prefix.as_deref()
353 }
354
355 #[must_use]
356 pub fn local_name(&self) -> &str {
357 &self.local_name
358 }
359
360 #[must_use]
361 pub fn namespace_uri(&self) -> Option<&str> {
362 self.namespace_uri.as_deref()
363 }
364
365 #[must_use]
366 pub fn attributes(&self) -> &[Attribute] {
367 &self.attributes
368 }
369
370 #[must_use]
371 pub fn nodes(&self) -> &[Node] {
372 self.nodes.nodes()
373 }
374
375 #[must_use]
377 pub const fn was_empty_element(&self) -> bool {
378 self.empty_style
379 }
380
381 pub fn children(&self) -> impl Iterator<Item = &Element> {
383 self.nodes.nodes().iter().filter_map(Node::as_element)
384 }
385
386 #[must_use]
388 pub fn attribute_ns(&self, namespace_uri: Option<&str>, local_name: &str) -> Option<&str> {
389 self.attributes
390 .iter()
391 .find(|attribute| {
392 attribute.namespace_uri() == namespace_uri && attribute.local_name() == local_name
393 })
394 .map(Attribute::value)
395 }
396
397 #[must_use]
399 pub fn direct_text(&self) -> String {
400 let mut result = String::new();
401 for node in self.nodes.nodes() {
402 match node {
403 Node::Text(value) | Node::CData(value) => result.push_str(value),
404 _ => {}
405 }
406 }
407 result
408 }
409
410 pub(crate) fn push(&mut self, node: Node, span: Option<Span>) {
411 self.nodes.push(node, span);
412 }
413}
414
415#[derive(Debug, Clone, PartialEq, Eq)]
417pub enum Node {
418 Element(Element),
419 Text(String),
420 CData(String),
421 Comment(String),
422 ProcessingInstruction(String),
424}
425
426impl Node {
427 #[must_use]
428 pub fn as_element(&self) -> Option<&Element> {
429 match self {
430 Self::Element(element) => Some(element),
431 _ => None,
432 }
433 }
434}
435
436#[derive(Debug, Clone, Eq)]
443pub struct Document {
444 declaration: Option<XmlDeclaration>,
445 prolog: NodeList,
446 root: Element,
447 epilog: NodeList,
448 source: Option<Arc<str>>,
450 declaration_span: Option<Span>,
451}
452
453impl PartialEq for Document {
454 fn eq(&self, other: &Self) -> bool {
455 self.declaration == other.declaration
456 && self.prolog == other.prolog
457 && self.root == other.root
458 && self.epilog == other.epilog
459 }
460}
461
462impl Document {
463 pub fn parse(xml: &str) -> Result<Self, ParseError> {
465 Self::parse_with_options(xml, ParseOptions::default())
466 }
467
468 pub fn parse_with_options(xml: &str, options: ParseOptions) -> Result<Self, ParseError> {
470 parse_document(xml, options)
471 }
472
473 pub(crate) fn parsed(
474 declaration: Option<XmlDeclaration>,
475 prolog: NodeList,
476 root: Element,
477 epilog: NodeList,
478 source: Option<Arc<str>>,
479 declaration_span: Option<Span>,
480 ) -> Self {
481 Self {
482 declaration,
483 prolog,
484 root,
485 epilog,
486 source,
487 declaration_span,
488 }
489 }
490
491 #[must_use]
492 pub const fn declaration(&self) -> Option<&XmlDeclaration> {
493 self.declaration.as_ref()
494 }
495
496 #[must_use]
497 pub fn prolog(&self) -> &[Node] {
498 self.prolog.nodes()
499 }
500
501 #[must_use]
502 pub const fn root(&self) -> &Element {
503 &self.root
504 }
505
506 #[must_use]
507 pub fn epilog(&self) -> &[Node] {
508 self.epilog.nodes()
509 }
510
511 #[must_use]
513 pub fn source(&self) -> Option<&str> {
514 self.source.as_deref()
515 }
516
517 #[must_use]
523 pub fn without_source(mut self) -> Self {
524 self.source = None;
525 self
526 }
527
528 pub fn to_xml_string(&self) -> Result<String, WriteError> {
533 let mut writer = Writer::new(Cursor::new(Vec::new()));
534 if let Some(declaration) = &self.declaration {
535 writer.write_event(Event::Decl(BytesDecl::new(
536 &declaration.version,
537 declaration.encoding.as_deref(),
538 declaration.standalone.as_deref(),
539 )))?;
540 }
541 for node in self.prolog.nodes() {
542 write_node(&mut writer, node)?;
543 }
544 write_element(&mut writer, &self.root)?;
545 for node in self.epilog.nodes() {
546 write_node(&mut writer, node)?;
547 }
548 String::from_utf8(writer.into_inner().into_inner()).map_err(WriteError::Utf8)
549 }
550
551 pub fn to_xml_string_exact(&self) -> Result<String, ExactWriteError> {
559 let source = self.source.as_deref().ok_or(ExactWriteError::NoSource)?;
560 let mut output = String::with_capacity(source.len());
561 if self.declaration.is_some() {
562 push_span(&mut output, source, self.declaration_span)?;
563 }
564 push_nodes(&mut output, source, &self.prolog)?;
565 push_element(&mut output, source, &self.root)?;
566 push_nodes(&mut output, source, &self.epilog)?;
567 Ok(output)
568 }
569
570 pub(crate) fn take_root(self) -> Element {
571 self.root
572 }
573
574 #[must_use]
584 pub fn standalone(root: Element) -> Self {
585 let root = root.with_attribute(Attribute::new(
586 Some("xmlns"),
587 "dt",
588 Some("http://www.w3.org/2000/xmlns/"),
589 crate::NAMESPACE,
590 ));
591 Self {
592 declaration: None,
593 prolog: NodeList::new(),
594 root,
595 epilog: NodeList::new(),
596 source: None,
597 declaration_span: None,
598 }
599 }
600}
601
602fn push_span(output: &mut String, source: &str, span: Option<Span>) -> Result<(), ExactWriteError> {
603 let span = span.ok_or(ExactWriteError::MissingSpan)?;
604 output.push_str(span.slice(source).ok_or(ExactWriteError::SpanOutOfRange)?);
605 Ok(())
606}
607
608fn push_nodes(output: &mut String, source: &str, list: &NodeList) -> Result<(), ExactWriteError> {
609 for (node, span) in list.nodes().iter().zip(list.spans()) {
610 match node {
611 Node::Element(element) => push_element(output, source, element)?,
612 _ => push_span(output, source, *span)?,
613 }
614 }
615 Ok(())
616}
617
618fn push_element(
619 output: &mut String,
620 source: &str,
621 element: &Element,
622) -> Result<(), ExactWriteError> {
623 push_span(output, source, element.open_span)?;
624 if element.close_span.is_none() {
625 if element.nodes.is_empty() {
627 return Ok(());
628 }
629 return Err(ExactWriteError::MissingSpan);
630 }
631 push_nodes(output, source, &element.nodes)?;
632 push_span(output, source, element.close_span)
633}
634
635fn write_element(
636 writer: &mut Writer<Cursor<Vec<u8>>>,
637 element: &Element,
638) -> Result<(), WriteError> {
639 let mut start = BytesStart::new(element.qname());
640 for attribute in element.attributes() {
641 start.push_attribute(XmlAttribute {
642 key: QName(attribute.qname().as_bytes()),
643 value: Cow::Owned(escape_attribute_value(attribute.value())),
644 });
645 }
646 if element.empty_style && element.nodes.is_empty() {
647 writer.write_event(Event::Empty(start))?;
648 return Ok(());
649 }
650 writer.write_event(Event::Start(start))?;
651 for node in element.nodes() {
652 write_node(writer, node)?;
653 }
654 writer.write_event(Event::End(BytesEnd::new(element.qname())))?;
655 Ok(())
656}
657
658fn escape_attribute_value(value: &str) -> Vec<u8> {
659 let mut output = String::with_capacity(value.len());
660 for character in value.chars() {
661 match character {
662 '&' => output.push_str("&"),
663 '<' => output.push_str("<"),
664 '"' => output.push_str("""),
665 '\t' => output.push_str("	"),
666 '\n' => output.push_str("
"),
667 '\r' => output.push_str("
"),
668 _ => output.push(character),
669 }
670 }
671 output.into_bytes()
672}
673
674fn escape_text_value(value: &str) -> String {
675 let mut output = String::with_capacity(value.len());
676 for character in value.chars() {
677 match character {
678 '&' => output.push_str("&"),
679 '<' => output.push_str("<"),
680 '>' => output.push_str(">"),
681 '\r' => output.push_str(" "),
682 _ => output.push(character),
683 }
684 }
685 output
686}
687
688fn write_node(writer: &mut Writer<Cursor<Vec<u8>>>, node: &Node) -> Result<(), WriteError> {
689 match node {
690 Node::Element(element) => write_element(writer, element),
691 Node::Text(value) => writer
692 .write_event(Event::Text(BytesText::from_escaped(escape_text_value(
693 value,
694 ))))
695 .map_err(Into::into),
696 Node::CData(value) => writer
697 .write_event(Event::CData(BytesCData::new(value)))
698 .map_err(Into::into),
699 Node::Comment(value) => writer
702 .write_event(Event::Comment(BytesText::from_escaped(value.as_str())))
703 .map_err(Into::into),
704 Node::ProcessingInstruction(value) => writer
705 .write_event(Event::PI(BytesPI::new(value)))
706 .map_err(Into::into),
707 }
708}
709
710#[derive(Debug)]
712pub enum WriteError {
713 Xml(std::io::Error),
714 Utf8(std::string::FromUtf8Error),
715}
716
717impl fmt::Display for WriteError {
718 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
719 match self {
720 Self::Xml(error) => write!(formatter, "could not write XML: {error}"),
721 Self::Utf8(error) => write!(formatter, "XML writer produced invalid UTF-8: {error}"),
722 }
723 }
724}
725
726impl Error for WriteError {
727 fn source(&self) -> Option<&(dyn Error + 'static)> {
728 match self {
729 Self::Xml(error) => Some(error),
730 Self::Utf8(error) => Some(error),
731 }
732 }
733}
734
735impl From<std::io::Error> for WriteError {
736 fn from(value: std::io::Error) -> Self {
737 Self::Xml(value)
738 }
739}
740
741#[derive(Debug, Clone, Copy, PartialEq, Eq)]
746pub enum ExactWriteError {
747 NoSource,
749 MissingSpan,
751 SpanOutOfRange,
753}
754
755impl fmt::Display for ExactWriteError {
756 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
757 let message = match self {
758 Self::NoSource => "document does not retain its parse input",
759 Self::MissingSpan => "a retained node has no recorded source span",
760 Self::SpanOutOfRange => "a recorded source span is not a character boundary",
761 };
762 formatter.write_str(message)
763 }
764}
765
766impl Error for ExactWriteError {}