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}