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