xml_no_std/writer/
events.rs

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