1//! Contains `XmlEvent` datatype, instances of which are consumed by the writer.
2
3use std::borrow::Cow;
4
5use crate::attribute::Attribute;
6use crate::common::XmlVersion;
7use crate::name::Name;
8use 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)]
15pub 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
96impl<'a> XmlEvent<'a> {
97 /// Returns an writer event for a processing instruction.
98 #[inline]
99 #[must_use]
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]
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]
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]
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]
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]
150 pub fn comment(data: &'a str) -> XmlEvent<'a> {
151 XmlEvent::Comment(data)
152 }
153}
154
155impl<'a> From<&'a str> for XmlEvent<'a> {
156 #[inline]
157 fn from(s: &'a str) -> XmlEvent<'a> {
158 XmlEvent::Characters(s)
159 }
160}
161
162pub struct EndElementBuilder<'a> {
163 name: Option<Name<'a>>,
164}
165
166/// A builder for a closing element event.
167impl<'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]
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
181impl<'a> From<EndElementBuilder<'a>> for 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.
188pub struct StartElementBuilder<'a> {
189 name: Name<'a>,
190 attributes: Vec<Attribute<'a>>,
191 namespace: Namespace,
192}
193
194impl<'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]
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]
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]
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
247impl<'a> From<StartElementBuilder<'a>> for XmlEvent<'a> {
248 #[inline]
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