Skip to main content

xml/writer/
events.rs

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