| 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 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 | |
| 155 | impl<'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. |
| 163 | pub struct EndElementBuilder<'a> { |
| 164 | name: Option<Name<'a>>, |
| 165 | } |
| 166 | |
| 167 | /// A builder for a closing element event. |
| 168 | impl<'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 | |
| 183 | impl<'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. |
| 190 | pub struct StartElementBuilder<'a> { |
| 191 | name: Name<'a>, |
| 192 | attributes: Vec<Attribute<'a>>, |
| 193 | namespace: Namespace, |
| 194 | } |
| 195 | |
| 196 | impl<'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 | |
| 248 | impl<'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 | |
| 259 | impl<'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(err:crate::reader::Error { |
| 264 | pos: crate::common::TextPosition::new(), |
| 265 | kind: crate::reader::ErrorKind::UnexpectedEof, |
| 266 | }) |
| 267 | } |
| 268 | } |
| 269 | |