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