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};
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 const fn processing_instruction(name: &'a str, data: Option<&'a str>) -> Self {
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 const 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 const fn cdata(data: &'a str) -> Self {
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 const fn characters(data: &'a str) -> Self {
144        XmlEvent::Characters(data)
145    }
146
147    /// Returns a comment event.
148    #[inline]
149    #[must_use]
150    pub const fn comment(data: &'a str) -> Self {
151        XmlEvent::Comment(data)
152    }
153}
154
155impl<'a> From<&'a str> for XmlEvent<'a> {
156    #[inline]
157    fn from(s: &'a str) -> Self {
158        XmlEvent::Characters(s)
159    }
160}
161
162/// A builder for a closing element event.
163pub struct EndElementBuilder<'a> {
164    name: Option<Name<'a>>,
165}
166
167/// A builder for a closing element event.
168impl<'a> EndElementBuilder<'a> {
169    /// Sets the name of this closing element.
170    ///
171    /// Usually the writer is able to determine closing element names automatically. If
172    /// this functionality is enabled (by default it is), then this name is checked for correctness.
173    /// It is possible, however, to disable such behavior; then the user must ensure that
174    /// closing element name is correct manually.
175    #[inline]
176    #[must_use]
177    pub fn name<N>(mut self, name: N) -> Self where N: Into<Name<'a>> {
178        self.name = Some(name.into());
179        self
180    }
181}
182
183impl<'a> From<EndElementBuilder<'a>> for XmlEvent<'a> {
184    fn from(b: EndElementBuilder<'a>) -> Self {
185        XmlEvent::EndElement { name: b.name }
186    }
187}
188
189/// A builder for a starting element event.
190pub struct StartElementBuilder<'a> {
191    name: Name<'a>,
192    attributes: Vec<Attribute<'a>>,
193    namespace: Namespace,
194}
195
196impl<'a> StartElementBuilder<'a> {
197    /// Sets an attribute value of this element to the given string.
198    ///
199    /// This method can be used to add attributes to the starting element. Name is a qualified
200    /// name; its namespace is ignored, but its prefix is checked for correctness, that is,
201    /// it is checked that the prefix is bound to some namespace in the current context.
202    ///
203    /// Currently attributes are not checked for duplicates. Note that duplicate attributes
204    /// are a violation of XML document well-formedness.
205    ///
206    /// The writer checks that you don't specify reserved prefix names, for example `xmlns`.
207    #[inline]
208    #[must_use]
209    pub fn attr<N>(mut self, name: N, value: &'a str) -> Self
210    where N: Into<Name<'a>> {
211        self.attributes.push(Attribute::new(name.into(), value));
212        self
213    }
214
215    /// Adds a namespace to the current namespace context.
216    ///
217    /// If no namespace URI was bound to the provided prefix at this point of the document,
218    /// then the mapping from the prefix to the provided namespace URI will be written as
219    /// a part of this element attribute set.
220    ///
221    /// If the same namespace URI was bound to the provided prefix at this point of the document,
222    /// then no namespace attributes will be emitted.
223    ///
224    /// If some other namespace URI was bound to the provided prefix at this point of the document,
225    /// then another binding will be added as a part of this element attribute set, shadowing
226    /// the outer binding.
227    #[inline]
228    #[must_use]
229    pub fn ns<S1, S2>(mut self, prefix: S1, uri: S2) -> Self
230        where S1: Into<String>, S2: Into<String>
231    {
232        self.namespace.put(prefix, uri);
233        self
234    }
235
236    /// Adds a default namespace mapping to the current namespace context.
237    ///
238    /// Same rules as for `ns()` are also valid for the default namespace mapping.
239    #[inline]
240    #[must_use]
241    pub fn default_ns<S>(mut self, uri: S) -> Self
242    where S: Into<String> {
243        self.namespace.put(NS_NO_PREFIX, uri);
244        self
245    }
246}
247
248impl<'a> From<StartElementBuilder<'a>> for XmlEvent<'a> {
249    #[inline]
250    fn from(b: StartElementBuilder<'a>) -> Self {
251        XmlEvent::StartElement {
252            name: b.name,
253            attributes: Cow::Owned(b.attributes),
254            namespace: Cow::Owned(b.namespace),
255        }
256    }
257}
258
259impl<'a> TryFrom<&'a crate::reader::XmlEvent> for XmlEvent<'a> {
260    type Error = crate::reader::Error;
261
262    fn try_from(event: &crate::reader::XmlEvent) -> Result<XmlEvent<'_>, Self::Error> {
263        event.as_writer_event().ok_or(crate::reader::Error {
264            pos: crate::common::TextPosition::new(),
265            kind: crate::reader::ErrorKind::UnexpectedEof,
266        })
267    }
268}