1 //! Contains `XmlEvent` datatype, instances of which are emitted by the parser.
2 
3 use std::fmt;
4 use crate::attribute::OwnedAttribute;
5 use crate::common::XmlVersion;
6 use crate::name::OwnedName;
7 use crate::namespace::Namespace;
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)]
14 pub 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 `&lt;` or `&amp;` or `&#123;`
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 }
114 
115 impl fmt::Debug for XmlEvent {
116     #[cold]
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result117     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118         match *self {
119             XmlEvent::StartDocument { ref version, ref encoding, standalone } =>
120                 write!(f, "StartDocument({}, {}, {:?})", version, *encoding, standalone),
121             XmlEvent::EndDocument =>
122                 write!(f, "EndDocument"),
123             XmlEvent::ProcessingInstruction { ref name, ref data } =>
124                 write!(f, "ProcessingInstruction({}{})", *name, match *data {
125                     Some(ref data) => format!(", {data}"),
126                     None       => String::new()
127                 }),
128             XmlEvent::StartElement { ref name, ref attributes, namespace: Namespace(ref namespace) } =>
129                 write!(f, "StartElement({}, {:?}{})", name, namespace, if attributes.is_empty() {
130                     String::new()
131                 } else {
132                     let attributes: Vec<String> = attributes.iter().map(
133                         |a| format!("{} -> {}", a.name, a.value)
134                     ).collect();
135                     format!(", [{}]", attributes.join(", "))
136                 }),
137             XmlEvent::EndElement { ref name } =>
138                 write!(f, "EndElement({name})"),
139             XmlEvent::Comment(ref data) =>
140                 write!(f, "Comment({data})"),
141             XmlEvent::CData(ref data) =>
142                 write!(f, "CData({data})"),
143             XmlEvent::Characters(ref data) =>
144                 write!(f, "Characters({data})"),
145             XmlEvent::Whitespace(ref data) =>
146                 write!(f, "Whitespace({data})")
147         }
148     }
149 }
150 
151 impl XmlEvent {
152     /// Obtains a writer event from this reader event.
153     ///
154     /// This method is useful for streaming processing of XML documents where the output
155     /// is also an XML document. With this method it is possible to process some events
156     /// while passing other events through to the writer unchanged:
157     ///
158     /// ```rust
159     /// use std::str;
160     ///
161     /// use xml::{EventReader, EventWriter};
162     /// use xml::reader::XmlEvent as ReaderEvent;
163     /// use xml::writer::XmlEvent as WriterEvent;
164     ///
165     /// let mut input: &[u8] = b"<hello>world</hello>";
166     /// let mut output: Vec<u8> = Vec::new();
167     ///
168     /// {
169     ///     let mut reader = EventReader::new(&mut input);
170     ///     let mut writer = EventWriter::new(&mut output);
171     ///
172     ///     for e in reader {
173     ///         match e.unwrap() {
174     ///             ReaderEvent::Characters(s) =>
175     ///                 writer.write(WriterEvent::characters(&s.to_uppercase())).unwrap(),
176     ///             e => if let Some(e) = e.as_writer_event() {
177     ///                 writer.write(e).unwrap()
178     ///             }
179     ///         }
180     ///     }
181     /// }
182     ///
183     /// assert_eq!(
184     ///     str::from_utf8(&output).unwrap(),
185     ///     r#"<?xml version="1.0" encoding="UTF-8"?><hello>WORLD</hello>"#
186     /// );
187     /// ```
188     ///
189     /// Note that this API may change or get additions in future to improve its ergonomics.
190     #[must_use]
as_writer_event(&self) -> Option<crate::writer::events::XmlEvent<'_>>191     pub fn as_writer_event(&self) -> Option<crate::writer::events::XmlEvent<'_>> {
192         match *self {
193             XmlEvent::StartDocument { version, ref encoding, standalone } =>
194                 Some(crate::writer::events::XmlEvent::StartDocument {
195                     version,
196                     encoding: Some(encoding),
197                     standalone
198                 }),
199             XmlEvent::ProcessingInstruction { ref name, ref data } =>
200                 Some(crate::writer::events::XmlEvent::ProcessingInstruction {
201                     name,
202                     data: data.as_ref().map(|s| &**s)
203                 }),
204             XmlEvent::StartElement { ref name, ref attributes, ref namespace } =>
205                 Some(crate::writer::events::XmlEvent::StartElement {
206                     name: name.borrow(),
207                     attributes: attributes.iter().map(|a| a.borrow()).collect(),
208                     namespace: namespace.borrow(),
209                 }),
210             XmlEvent::EndElement { ref name } =>
211                 Some(crate::writer::events::XmlEvent::EndElement { name: Some(name.borrow()) }),
212             XmlEvent::Comment(ref data) => Some(crate::writer::events::XmlEvent::Comment(data)),
213             XmlEvent::CData(ref data) => Some(crate::writer::events::XmlEvent::CData(data)),
214             XmlEvent::Characters(ref data) |
215             XmlEvent::Whitespace(ref data) => Some(crate::writer::events::XmlEvent::Characters(data)),
216             XmlEvent::EndDocument => None,
217         }
218     }
219 }
220