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 `&lt;` or `&amp;` or `&#123;`
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}