xml_no_std/reader/events.rs
1//! Contains `XmlEvent` datatype, instances of which are emitted by the parser.
2extern crate alloc;
3
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use core::fmt;
8use crate::attribute::OwnedAttribute;
9use crate::common::XmlVersion;
10use crate::name::OwnedName;
11use crate::namespace::Namespace;
12
13/// An element of an XML input stream.
14///
15/// Items of this enum are emitted by `reader::EventReader`. They correspond to different
16/// elements of an XML document.
17#[derive(PartialEq, Clone)]
18pub enum XmlEvent {
19 /// Corresponds to XML document declaration.
20 ///
21 /// This event is always emitted before any other event. It is emitted
22 /// even if the actual declaration is not present in the document.
23 StartDocument {
24 /// XML version.
25 ///
26 /// If XML declaration is not present, defaults to `Version10`.
27 version: XmlVersion,
28
29 /// XML document encoding.
30 ///
31 /// If XML declaration is not present or does not contain `encoding` attribute,
32 /// defaults to `"UTF-8"`. This field is currently used for no other purpose than
33 /// informational.
34 encoding: String,
35
36 /// XML standalone declaration.
37 ///
38 /// If XML document is not present or does not contain `standalone` attribute,
39 /// defaults to `None`. This field is currently used for no other purpose than
40 /// informational.
41 standalone: Option<bool>,
42 },
43
44 /// Denotes to the end of the document stream.
45 ///
46 /// This event is always emitted after any other event (except `Error`). After it
47 /// is emitted for the first time, it will always be emitted on next event pull attempts.
48 EndDocument,
49
50 /// Denotes an XML processing instruction.
51 ///
52 /// This event contains a processing instruction target (`name`) and opaque `data`. It
53 /// is up to the application to process them.
54 ProcessingInstruction {
55 /// Processing instruction target.
56 name: String,
57
58 /// Processing instruction content.
59 data: Option<String>,
60 },
61
62 /// Denotes a beginning of an XML element.
63 ///
64 /// This event is emitted after parsing opening tags or after parsing bodiless tags. In the
65 /// latter case `EndElement` event immediately follows.
66 StartElement {
67 /// Qualified name of the element.
68 name: OwnedName,
69
70 /// A list of attributes associated with the element.
71 ///
72 /// Currently attributes are not checked for duplicates (TODO)
73 attributes: Vec<OwnedAttribute>,
74
75 /// Contents of the namespace mapping at this point of the document.
76 namespace: Namespace,
77 },
78
79 /// Denotes an end of an XML element.
80 ///
81 /// This event is emitted after parsing closing tags or after parsing bodiless tags. In the
82 /// latter case it is emitted immediately after corresponding `StartElement` event.
83 EndElement {
84 /// Qualified name of the element.
85 name: OwnedName,
86 },
87
88 /// Denotes CDATA content.
89 ///
90 /// This event contains unparsed data. No unescaping will be performed.
91 ///
92 /// It is possible to configure a parser to emit `Characters` event instead of `CData`. See
93 /// `pull::ParserConfiguration` structure for more information.
94 CData(String),
95
96 /// Denotes a comment.
97 ///
98 /// It is possible to configure a parser to ignore comments, so this event will never be emitted.
99 /// See `pull::ParserConfiguration` structure for more information.
100 Comment(String),
101
102 /// Denotes character data outside of tags.
103 ///
104 /// Contents of this event will always be unescaped, so no entities like `<` or `&` or `{`
105 /// will appear in it.
106 ///
107 /// It is possible to configure a parser to trim leading and trailing whitespace for this event.
108 /// See `pull::ParserConfiguration` structure for more information.
109 Characters(String),
110
111 /// Denotes a chunk of whitespace outside of tags.
112 ///
113 /// It is possible to configure a parser to emit `Characters` event instead of `Whitespace`.
114 /// See `pull::ParserConfiguration` structure for more information. When combined with whitespace
115 /// trimming, it will eliminate standalone whitespace from the event stream completely.
116 Whitespace(String),
117}
118
119impl fmt::Debug for XmlEvent {
120 #[cold]
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match *self {
123 XmlEvent::StartDocument { ref version, ref encoding, standalone } =>
124 write!(f, "StartDocument({}, {}, {:?})", version, *encoding, standalone),
125 XmlEvent::EndDocument =>
126 write!(f, "EndDocument"),
127 XmlEvent::ProcessingInstruction { ref name, ref data } =>
128 write!(f, "ProcessingInstruction({}{})", *name, match *data {
129 Some(ref data) => alloc::format!(", {data}"),
130 None => String::new()
131 }),
132 XmlEvent::StartElement { ref name, ref attributes, namespace: Namespace(ref namespace) } =>
133 write!(f, "StartElement({}, {:?}{})", name, namespace, if attributes.is_empty() {
134 String::new()
135 } else {
136 let attributes: Vec<String> = attributes.iter().map(
137 |a| alloc::format!("{} -> {}", a.name, a.value)
138 ).collect();
139 alloc::format!(", [{}]", attributes.join(", "))
140 }),
141 XmlEvent::EndElement { ref name } =>
142 write!(f, "EndElement({name})"),
143 XmlEvent::Comment(ref data) =>
144 write!(f, "Comment({data})"),
145 XmlEvent::CData(ref data) =>
146 write!(f, "CData({data})"),
147 XmlEvent::Characters(ref data) =>
148 write!(f, "Characters({data})"),
149 XmlEvent::Whitespace(ref data) =>
150 write!(f, "Whitespace({data})")
151 }
152 }
153}
154
155impl XmlEvent {
156 /// Obtains a writer event from this reader event.
157 ///
158 /// This method is useful for streaming processing of XML documents where the output
159 /// is also an XML document. With this method it is possible to process some events
160 /// while passing other events through to the writer unchanged:
161 ///
162 /// ```rust
163 /// use std::str;
164 ///
165 /// use xml_no_std::{EventReader, EventWriter};
166 /// use xml_no_std::reader::XmlEvent as ReaderEvent;
167 /// use xml_no_std::writer::XmlEvent as WriterEvent;
168 ///
169 /// let mut input: &[u8] = b"<hello>world</hello>";
170 /// let mut output: Vec<u8> = Vec::new();
171 ///
172 /// {
173 /// let mut reader = EventReader::new(input.into_iter());
174 /// let mut writer = EventWriter::new();
175 ///
176 /// for e in reader {
177 /// match e.unwrap() {
178 /// ReaderEvent::Characters(s) =>
179 /// writer.write(WriterEvent::characters(&s.to_uppercase())).unwrap(),
180 /// e => if let Some(e) = e.as_writer_event() {
181 /// writer.write(e).unwrap()
182 /// }
183 /// }
184 /// }
185 /// output = writer.into_inner().into_bytes();
186 /// }
187 ///
188 /// assert_eq!(
189 /// str::from_utf8(&output).unwrap(),
190 /// r#"<?xml version="1.0" encoding="UTF-8"?><hello>WORLD</hello>"#
191 /// );
192 /// ```
193 ///
194 /// Note that this API may change or get additions in future to improve its ergonomics.
195 #[must_use]
196 pub fn as_writer_event(&self) -> Option<crate::writer::events::XmlEvent<'_>> {
197 match *self {
198 XmlEvent::StartDocument { version, ref encoding, standalone } =>
199 Some(crate::writer::events::XmlEvent::StartDocument {
200 version,
201 encoding: Some(encoding),
202 standalone
203 }),
204 XmlEvent::ProcessingInstruction { ref name, ref data } =>
205 Some(crate::writer::events::XmlEvent::ProcessingInstruction {
206 name,
207 data: data.as_ref().map(|s| &**s)
208 }),
209 XmlEvent::StartElement { ref name, ref attributes, ref namespace } =>
210 Some(crate::writer::events::XmlEvent::StartElement {
211 name: name.borrow(),
212 attributes: attributes.iter().map(|a| a.borrow()).collect(),
213 namespace: namespace.borrow(),
214 }),
215 XmlEvent::EndElement { ref name } =>
216 Some(crate::writer::events::XmlEvent::EndElement { name: Some(name.borrow()) }),
217 XmlEvent::Comment(ref data) => Some(crate::writer::events::XmlEvent::Comment(data)),
218 XmlEvent::CData(ref data) => Some(crate::writer::events::XmlEvent::CData(data)),
219 XmlEvent::Characters(ref data) |
220 XmlEvent::Whitespace(ref data) => Some(crate::writer::events::XmlEvent::Characters(data)),
221 XmlEvent::EndDocument => None,
222 }
223 }
224}