1 //! Contains `XmlEvent` datatype, instances of which are consumed by the writer.
2 
3 use std::borrow::Cow;
4 
5 use crate::attribute::Attribute;
6 use crate::common::XmlVersion;
7 use crate::name::Name;
8 use crate::namespace::{Namespace, NS_NO_PREFIX};
9 
10 /// A part of an XML output stream.
11 ///
12 /// Objects of this enum are consumed by `EventWriter`. They correspond to different parts of
13 /// an XML document.
14 #[derive(Debug, Clone)]
15 pub enum XmlEvent<'a> {
16     /// Corresponds to XML document declaration.
17     ///
18     /// This event should always be written before any other event. If it is not written
19     /// at all, a default XML declaration will be outputted if the corresponding option
20     /// is set in the configuration. Otherwise an error will be returned.
21     StartDocument {
22         /// XML version.
23         ///
24         /// Defaults to `XmlVersion::Version10`.
25         version: XmlVersion,
26 
27         /// XML document encoding.
28         ///
29         /// Defaults to `Some("UTF-8")`.
30         encoding: Option<&'a str>,
31 
32         /// XML standalone declaration.
33         ///
34         /// Defaults to `None`.
35         standalone: Option<bool>,
36     },
37 
38     /// Denotes an XML processing instruction.
39     ProcessingInstruction {
40         /// Processing instruction target.
41         name: &'a str,
42 
43         /// Processing instruction content.
44         data: Option<&'a str>,
45     },
46 
47     /// Denotes a beginning of an XML element.
48     StartElement {
49         /// Qualified name of the element.
50         name: Name<'a>,
51 
52         /// A list of attributes associated with the element.
53         ///
54         /// Currently attributes are not checked for duplicates (TODO). Attribute values
55         /// will be escaped, and all characters invalid for attribute values like `"` or `<`
56         /// will be changed into character entities.
57         attributes: Cow<'a, [Attribute<'a>]>,
58 
59         /// Contents of the namespace mapping at this point of the document.
60         ///
61         /// This mapping will be inspected for "new" entries, and if at this point of the document
62         /// a particular pair of prefix and namespace URI is already defined, no namespace
63         /// attributes will be emitted.
64         namespace: Cow<'a, Namespace>,
65     },
66 
67     /// Denotes an end of an XML element.
68     EndElement {
69         /// Optional qualified name of the element.
70         ///
71         /// If `None`, then it is assumed that the element name should be the last valid one.
72         /// If `Some` and element names tracking is enabled, then the writer will check it for
73         /// correctness.
74         name: Option<Name<'a>>,
75     },
76 
77     /// Denotes CDATA content.
78     ///
79     /// This event contains unparsed data, and no escaping will be performed when writing it
80     /// to the output stream.
81     CData(&'a str),
82 
83     /// Denotes a comment.
84     ///
85     /// The string will be checked for invalid sequences and error will be returned by the
86     /// write operation
87     Comment(&'a str),
88 
89     /// Denotes character data outside of tags.
90     ///
91     /// Contents of this event will be escaped if `perform_escaping` option is enabled,
92     /// that is, every character invalid for PCDATA will appear as a character entity.
93     Characters(&'a str),
94 }
95 
96 impl<'a> XmlEvent<'a> {
97     /// Returns an writer event for a processing instruction.
98     #[inline]
99     #[must_use]
processing_instruction(name: &'a str, data: Option<&'a str>) -> XmlEvent<'a>100     pub fn processing_instruction(name: &'a str, data: Option<&'a str>) -> XmlEvent<'a> {
101         XmlEvent::ProcessingInstruction { name, data }
102     }
103 
104     /// Returns a builder for a starting element.
105     ///
106     /// This builder can then be used to tweak attributes and namespace starting at
107     /// this element.
108     #[inline]
start_element<S>(name: S) -> StartElementBuilder<'a> where S: Into<Name<'a>>109     pub fn start_element<S>(name: S) -> StartElementBuilder<'a> where S: Into<Name<'a>> {
110         StartElementBuilder {
111             name: name.into(),
112             attributes: Vec::new(),
113             namespace: Namespace::empty(),
114         }
115     }
116 
117     /// Returns a builder for an closing element.
118     ///
119     /// This method, unline `start_element()`, does not accept a name because by default
120     /// the writer is able to determine it automatically. However, when this functionality
121     /// is disabled, it is possible to specify the name with `name()` method on the builder.
122     #[inline]
123     #[must_use]
end_element() -> EndElementBuilder<'a>124     pub fn end_element() -> EndElementBuilder<'a> {
125         EndElementBuilder { name: None }
126     }
127 
128     /// Returns a CDATA event.
129     ///
130     /// Naturally, the provided string won't be escaped, except for closing CDATA token `]]>`
131     /// (depending on the configuration).
132     #[inline]
133     #[must_use]
cdata(data: &'a str) -> XmlEvent<'a>134     pub fn cdata(data: &'a str) -> XmlEvent<'a> {
135         XmlEvent::CData(data)
136     }
137 
138     /// Returns a regular characters (PCDATA) event.
139     ///
140     /// All offending symbols, in particular, `&` and `<`, will be escaped by the writer.
141     #[inline]
142     #[must_use]
characters(data: &'a str) -> XmlEvent<'a>143     pub fn characters(data: &'a str) -> XmlEvent<'a> {
144         XmlEvent::Characters(data)
145     }
146 
147     /// Returns a comment event.
148     #[inline]
149     #[must_use]
comment(data: &'a str) -> XmlEvent<'a>150     pub fn comment(data: &'a str) -> XmlEvent<'a> {
151         XmlEvent::Comment(data)
152     }
153 }
154 
155 impl<'a> From<&'a str> for XmlEvent<'a> {
156     #[inline]
from(s: &'a str) -> XmlEvent<'a>157     fn from(s: &'a str) -> XmlEvent<'a> {
158         XmlEvent::Characters(s)
159     }
160 }
161 
162 pub struct EndElementBuilder<'a> {
163     name: Option<Name<'a>>,
164 }
165 
166 /// A builder for a closing element event.
167 impl<'a> EndElementBuilder<'a> {
168     /// Sets the name of this closing element.
169     ///
170     /// Usually the writer is able to determine closing element names automatically. If
171     /// this functionality is enabled (by default it is), then this name is checked for correctness.
172     /// It is possible, however, to disable such behavior; then the user must ensure that
173     /// closing element name is correct manually.
174     #[inline]
name<N>(mut self, name: N) -> EndElementBuilder<'a> where N: Into<Name<'a>>175     pub fn name<N>(mut self, name: N) -> EndElementBuilder<'a> where N: Into<Name<'a>> {
176         self.name = Some(name.into());
177         self
178     }
179 }
180 
181 impl<'a> From<EndElementBuilder<'a>> for XmlEvent<'a> {
from(b: EndElementBuilder<'a>) -> XmlEvent<'a>182     fn from(b: EndElementBuilder<'a>) -> XmlEvent<'a> {
183         XmlEvent::EndElement { name: b.name }
184     }
185 }
186 
187 /// A builder for a starting element event.
188 pub struct StartElementBuilder<'a> {
189     name: Name<'a>,
190     attributes: Vec<Attribute<'a>>,
191     namespace: Namespace,
192 }
193 
194 impl<'a> StartElementBuilder<'a> {
195     /// Sets an attribute value of this element to the given string.
196     ///
197     /// This method can be used to add attributes to the starting element. Name is a qualified
198     /// name; its namespace is ignored, but its prefix is checked for correctness, that is,
199     /// it is checked that the prefix is bound to some namespace in the current context.
200     ///
201     /// Currently attributes are not checked for duplicates. Note that duplicate attributes
202     /// are a violation of XML document well-formedness.
203     ///
204     /// The writer checks that you don't specify reserved prefix names, for example `xmlns`.
205     #[inline]
attr<N>(mut self, name: N, value: &'a str) -> StartElementBuilder<'a> where N: Into<Name<'a>>206     pub fn attr<N>(mut self, name: N, value: &'a str) -> StartElementBuilder<'a>
207         where N: Into<Name<'a>>
208     {
209         self.attributes.push(Attribute::new(name.into(), value));
210         self
211     }
212 
213     /// Adds a namespace to the current namespace context.
214     ///
215     /// If no namespace URI was bound to the provided prefix at this point of the document,
216     /// then the mapping from the prefix to the provided namespace URI will be written as
217     /// a part of this element attribute set.
218     ///
219     /// If the same namespace URI was bound to the provided prefix at this point of the document,
220     /// then no namespace attributes will be emitted.
221     ///
222     /// If some other namespace URI was bound to the provided prefix at this point of the document,
223     /// then another binding will be added as a part of this element attribute set, shadowing
224     /// the outer binding.
225     #[inline]
226     #[must_use]
ns<S1, S2>(mut self, prefix: S1, uri: S2) -> StartElementBuilder<'a> where S1: Into<String>, S2: Into<String>227     pub fn ns<S1, S2>(mut self, prefix: S1, uri: S2) -> StartElementBuilder<'a>
228         where S1: Into<String>, S2: Into<String>
229     {
230         self.namespace.put(prefix, uri);
231         self
232     }
233 
234     /// Adds a default namespace mapping to the current namespace context.
235     ///
236     /// Same rules as for `ns()` are also valid for the default namespace mapping.
237     #[inline]
238     #[must_use]
default_ns<S>(mut self, uri: S) -> StartElementBuilder<'a> where S: Into<String>239     pub fn default_ns<S>(mut self, uri: S) -> StartElementBuilder<'a>
240         where S: Into<String>
241     {
242         self.namespace.put(NS_NO_PREFIX, uri);
243         self
244     }
245 }
246 
247 impl<'a> From<StartElementBuilder<'a>> for XmlEvent<'a> {
248     #[inline]
from(b: StartElementBuilder<'a>) -> XmlEvent<'a>249     fn from(b: StartElementBuilder<'a>) -> XmlEvent<'a> {
250         XmlEvent::StartElement {
251             name: b.name,
252             attributes: Cow::Owned(b.attributes),
253             namespace: Cow::Owned(b.namespace),
254         }
255     }
256 }
257