summaryrefslogtreecommitdiffstats
path: root/third_party/rust/xml-rs/src/writer
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/xml-rs/src/writer')
-rw-r--r--third_party/rust/xml-rs/src/writer/config.rs157
-rw-r--r--third_party/rust/xml-rs/src/writer/emitter.rs447
-rw-r--r--third_party/rust/xml-rs/src/writer/events.rs241
-rw-r--r--third_party/rust/xml-rs/src/writer/mod.rs93
4 files changed, 938 insertions, 0 deletions
diff --git a/third_party/rust/xml-rs/src/writer/config.rs b/third_party/rust/xml-rs/src/writer/config.rs
new file mode 100644
index 0000000000..ebabf181f0
--- /dev/null
+++ b/third_party/rust/xml-rs/src/writer/config.rs
@@ -0,0 +1,157 @@
+//! Contains emitter configuration structure.
+
+use std::io::Write;
+use std::borrow::Cow;
+
+use writer::EventWriter;
+
+/// Emitter configuration structure.
+///
+/// This structure contains various options which control XML document emitter behavior.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct EmitterConfig {
+ /// Line separator used to separate lines in formatted output. Default is `"\n"`.
+ pub line_separator: Cow<'static, str>,
+
+ /// A string which will be used for a single level of indentation. Default is `" "`
+ /// (two spaces).
+ pub indent_string: Cow<'static, str>,
+
+ /// Whether or not the emitted document should be indented. Default is false.
+ ///
+ /// The emitter is capable to perform automatic indentation of the emitted XML document.
+ /// It is done in stream-like fashion and does not require the knowledge of the whole
+ /// document in advance.
+ ///
+ /// Sometimes, however, automatic indentation is undesirable, e.g. when you want to keep
+ /// existing layout when processing an existing XML document. Also the indentiation algorithm
+ /// is not thoroughly tested. Hence by default it is disabled.
+ pub perform_indent: bool,
+
+ /// Whether or not characters in output events will be escaped. Default is true.
+ ///
+ /// The emitter can automatically escape characters which can't appear in PCDATA sections
+ /// or element attributes of an XML document, like `<` or `"` (in attributes). This may
+ /// introduce some overhead because then every corresponding piece of character data
+ /// should be scanned for invalid characters.
+ ///
+ /// If this option is disabled, the XML writer may produce non-well-formed documents, so
+ /// use `false` value for this option with care.
+ pub perform_escaping: bool,
+
+ /// Whether or not to write XML document declaration at the beginning of a document.
+ /// Default is true.
+ ///
+ /// This option controls whether the document declaration should be emitted automatically
+ /// before a root element is written if it was not emitted explicitly by the user.
+ pub write_document_declaration: bool,
+
+ /// Whether or not to convert elements with empty content to empty elements. Default is true.
+ ///
+ /// This option allows turning elements like `<a></a>` (an element with empty content)
+ /// into `<a />` (an empty element).
+ pub normalize_empty_elements: bool,
+
+ /// Whether or not to emit CDATA events as plain characters. Default is false.
+ ///
+ /// This option forces the emitter to convert CDATA events into regular character events,
+ /// performing all the necessary escaping beforehand. This may be occasionally useful
+ /// for feeding the document into incorrect parsers which do not support CDATA.
+ pub cdata_to_characters: bool,
+
+ /// Whether or not to keep element names to support `EndElement` events without explicit names.
+ /// Default is true.
+ ///
+ /// This option makes the emitter to keep names of written elements in order to allow
+ /// omitting names when writing closing element tags. This could incur some memory overhead.
+ pub keep_element_names_stack: bool,
+
+ /// Whether or not to automatically insert leading and trailing spaces in emitted comments,
+ /// if necessary. Default is true.
+ ///
+ /// This is a convenience option in order for the user not to append spaces before and after
+ /// comments text in order to get more pretty comments: `<!-- something -->` instead of
+ /// `<!--something-->`.
+ pub autopad_comments: bool,
+
+ /// Whether or not to automatically insert spaces before the trailing `/>` in self-closing
+ /// elements. Default is true.
+ ///
+ /// This option is only meaningful if `normalize_empty_elements` is true. For example, the
+ /// element `<a></a>` would be unaffected. When `normalize_empty_elements` is true, then when
+ /// this option is also true, the same element would appear `<a />`. If this option is false,
+ /// then the same element would appear `<a/>`.
+ pub pad_self_closing: bool,
+}
+
+impl EmitterConfig {
+ /// Creates an emitter configuration with default values.
+ ///
+ /// You can tweak default options with builder-like pattern:
+ ///
+ /// ```rust
+ /// use xml::writer::EmitterConfig;
+ ///
+ /// let config = EmitterConfig::new()
+ /// .line_separator("\r\n")
+ /// .perform_indent(true)
+ /// .normalize_empty_elements(false);
+ /// ```
+ #[inline]
+ pub fn new() -> EmitterConfig {
+ EmitterConfig {
+ line_separator: "\n".into(),
+ indent_string: " ".into(), // two spaces
+ perform_indent: false,
+ perform_escaping: true,
+ write_document_declaration: true,
+ normalize_empty_elements: true,
+ cdata_to_characters: false,
+ keep_element_names_stack: true,
+ autopad_comments: true,
+ pad_self_closing: true
+ }
+ }
+
+ /// Creates an XML writer with this configuration.
+ ///
+ /// This is a convenience method for configuring and creating a writer at the same time:
+ ///
+ /// ```rust
+ /// use xml::writer::EmitterConfig;
+ ///
+ /// let mut target: Vec<u8> = Vec::new();
+ ///
+ /// let writer = EmitterConfig::new()
+ /// .line_separator("\r\n")
+ /// .perform_indent(true)
+ /// .normalize_empty_elements(false)
+ /// .create_writer(&mut target);
+ /// ```
+ ///
+ /// This method is exactly equivalent to calling `EventWriter::new_with_config()` with
+ /// this configuration object.
+ #[inline]
+ pub fn create_writer<W: Write>(self, sink: W) -> EventWriter<W> {
+ EventWriter::new_with_config(sink, self)
+ }
+}
+
+impl Default for EmitterConfig {
+ #[inline]
+ fn default() -> EmitterConfig {
+ EmitterConfig::new()
+ }
+}
+
+gen_setters!(EmitterConfig,
+ line_separator: into Cow<'static, str>,
+ indent_string: into Cow<'static, str>,
+ perform_indent: val bool,
+ write_document_declaration: val bool,
+ normalize_empty_elements: val bool,
+ cdata_to_characters: val bool,
+ keep_element_names_stack: val bool,
+ autopad_comments: val bool,
+ pad_self_closing: val bool
+);
diff --git a/third_party/rust/xml-rs/src/writer/emitter.rs b/third_party/rust/xml-rs/src/writer/emitter.rs
new file mode 100644
index 0000000000..ba80f66781
--- /dev/null
+++ b/third_party/rust/xml-rs/src/writer/emitter.rs
@@ -0,0 +1,447 @@
+use std::io;
+use std::io::prelude::*;
+use std::fmt;
+use std::result;
+use std::borrow::Cow;
+use std::error::Error;
+
+use common;
+use name::{Name, OwnedName};
+use attribute::Attribute;
+use escape::{escape_str_attribute, escape_str_pcdata};
+use common::XmlVersion;
+use namespace::{NamespaceStack, NS_NO_PREFIX, NS_EMPTY_URI, NS_XMLNS_PREFIX, NS_XML_PREFIX};
+
+use writer::config::EmitterConfig;
+
+/// An error which may be returned by `XmlWriter` when writing XML events.
+#[derive(Debug)]
+pub enum EmitterError {
+ /// An I/O error occured in the underlying `Write` instance.
+ Io(io::Error),
+
+ /// Document declaration has already been written to the output stream.
+ DocumentStartAlreadyEmitted,
+
+ /// The name of the last opening element is not available.
+ LastElementNameNotAvailable,
+
+ /// The name of the last opening element is not equal to the name of the provided
+ /// closing element.
+ EndElementNameIsNotEqualToLastStartElementName,
+
+ /// End element name is not specified when it is needed, for example, when automatic
+ /// closing is not enabled in configuration.
+ EndElementNameIsNotSpecified
+}
+
+impl From<io::Error> for EmitterError {
+ fn from(err: io::Error) -> EmitterError {
+ EmitterError::Io(err)
+ }
+}
+
+impl fmt::Display for EmitterError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+
+ write!(f, "emitter error: ")?;
+ match *self {
+ EmitterError::Io(ref e) =>
+ write!(f, "I/O error: {}", e),
+ ref other =>
+ write!(f, "{}", other.description()),
+ }
+ }
+}
+
+impl Error for EmitterError {
+ fn description(&self) -> &str {
+ match *self {
+ EmitterError::Io(_) =>
+ "I/O error",
+ EmitterError::DocumentStartAlreadyEmitted =>
+ "document start event has already been emitted",
+ EmitterError::LastElementNameNotAvailable =>
+ "last element name is not available",
+ EmitterError::EndElementNameIsNotEqualToLastStartElementName =>
+ "end element name is not equal to last start element name",
+ EmitterError::EndElementNameIsNotSpecified =>
+ "end element name is not specified and can't be inferred",
+ }
+ }
+}
+
+/// A result type yielded by `XmlWriter`.
+pub type Result<T> = result::Result<T, EmitterError>;
+
+// TODO: split into a low-level fast writer without any checks and formatting logic and a
+// high-level indenting validating writer
+pub struct Emitter {
+ config: EmitterConfig,
+
+ nst: NamespaceStack,
+
+ indent_level: usize,
+ indent_stack: Vec<IndentFlags>,
+
+ element_names: Vec<OwnedName>,
+
+ start_document_emitted: bool,
+ just_wrote_start_element: bool
+}
+
+impl Emitter {
+ pub fn new(config: EmitterConfig) -> Emitter {
+ Emitter {
+ config,
+
+ nst: NamespaceStack::empty(),
+
+ indent_level: 0,
+ indent_stack: vec![IndentFlags::WroteNothing],
+
+ element_names: Vec::new(),
+
+ start_document_emitted: false,
+ just_wrote_start_element: false
+ }
+ }
+}
+
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
+enum IndentFlags {
+ WroteNothing,
+ WroteMarkup,
+ WroteText,
+}
+
+impl Emitter {
+ /// Returns the current state of namespaces.
+ #[inline]
+ pub fn namespace_stack_mut(&mut self) -> &mut NamespaceStack {
+ &mut self.nst
+ }
+
+ #[inline]
+ fn wrote_text(&self) -> bool {
+ *self.indent_stack.last().unwrap() == IndentFlags::WroteText
+ }
+
+ #[inline]
+ fn wrote_markup(&self) -> bool {
+ *self.indent_stack.last().unwrap() == IndentFlags::WroteMarkup
+ }
+
+ #[inline]
+ fn set_wrote_text(&mut self) {
+ *self.indent_stack.last_mut().unwrap() = IndentFlags::WroteText;
+ }
+
+ #[inline]
+ fn set_wrote_markup(&mut self) {
+ *self.indent_stack.last_mut().unwrap() = IndentFlags::WroteMarkup;
+ }
+
+ #[inline]
+ fn reset_state(&mut self) {
+ *self.indent_stack.last_mut().unwrap() = IndentFlags::WroteNothing;
+ }
+
+ fn write_newline<W: Write>(&mut self, target: &mut W, level: usize) -> Result<()> {
+ target.write_all(self.config.line_separator.as_bytes())?;
+ for _ in 0..level {
+ target.write_all(self.config.indent_string.as_bytes())?;
+ }
+ Ok(())
+ }
+
+ fn before_markup<W: Write>(&mut self, target: &mut W) -> Result<()> {
+ if self.config.perform_indent && !self.wrote_text() &&
+ (self.indent_level > 0 || self.wrote_markup()) {
+ let indent_level = self.indent_level;
+ self.write_newline(target, indent_level)?;
+ if self.indent_level > 0 && self.config.indent_string.len() > 0 {
+ self.after_markup();
+ }
+ }
+ Ok(())
+ }
+
+ fn after_markup(&mut self) {
+ self.set_wrote_markup();
+ }
+
+ fn before_start_element<W: Write>(&mut self, target: &mut W) -> Result<()> {
+ self.before_markup(target)?;
+ self.indent_stack.push(IndentFlags::WroteNothing);
+ Ok(())
+ }
+
+ fn after_start_element(&mut self) {
+ self.after_markup();
+ self.indent_level += 1;
+ }
+
+ fn before_end_element<W: Write>(&mut self, target: &mut W) -> Result<()> {
+ if self.config.perform_indent && self.indent_level > 0 && self.wrote_markup() &&
+ !self.wrote_text() {
+ let indent_level = self.indent_level;
+ self.write_newline(target, indent_level - 1)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn after_end_element(&mut self) {
+ if self.indent_level > 0 {
+ self.indent_level -= 1;
+ self.indent_stack.pop();
+ }
+ self.set_wrote_markup();
+ }
+
+ fn after_text(&mut self) {
+ self.set_wrote_text();
+ }
+
+ pub fn emit_start_document<W: Write>(&mut self, target: &mut W,
+ version: XmlVersion,
+ encoding: &str,
+ standalone: Option<bool>) -> Result<()> {
+ if self.start_document_emitted {
+ return Err(EmitterError::DocumentStartAlreadyEmitted);
+ }
+ self.start_document_emitted = true;
+
+ self.before_markup(target)?;
+ let result = {
+ let mut write = move || {
+ write!(target, "<?xml version=\"{}\" encoding=\"{}\"", version, encoding)?;
+
+ if let Some(standalone) = standalone {
+ write!(target, " standalone=\"{}\"", if standalone { "yes" } else { "no" })?;
+ }
+
+ write!(target, "?>")?;
+
+ Ok(())
+ };
+ write()
+ };
+ self.after_markup();
+
+ result
+ }
+
+ fn check_document_started<W: Write>(&mut self, target: &mut W) -> Result<()> {
+ if !self.start_document_emitted && self.config.write_document_declaration {
+ self.emit_start_document(target, common::XmlVersion::Version10, "utf-8", None)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn fix_non_empty_element<W: Write>(&mut self, target: &mut W) -> Result<()> {
+ if self.config.normalize_empty_elements && self.just_wrote_start_element {
+ self.just_wrote_start_element = false;
+ target.write_all(b">").map_err(From::from)
+ } else {
+ Ok(())
+ }
+ }
+
+ pub fn emit_processing_instruction<W: Write>(&mut self,
+ target: &mut W,
+ name: &str,
+ data: Option<&str>) -> Result<()> {
+ self.check_document_started(target)?;
+ self.fix_non_empty_element(target)?;
+
+ self.before_markup(target)?;
+
+ let result = {
+ let mut write = || {
+ write!(target, "<?{}", name)?;
+
+ if let Some(data) = data {
+ write!(target, " {}", data)?;
+ }
+
+ write!(target, "?>")?;
+
+ Ok(())
+ };
+ write()
+ };
+
+ self.after_markup();
+
+ result
+ }
+
+ fn emit_start_element_initial<W>(&mut self, target: &mut W,
+ name: Name,
+ attributes: &[Attribute]) -> Result<()>
+ where W: Write
+ {
+ self.check_document_started(target)?;
+ self.fix_non_empty_element(target)?;
+ self.before_start_element(target)?;
+ write!(target, "<{}", name.repr_display())?;
+ self.emit_current_namespace_attributes(target)?;
+ self.emit_attributes(target, attributes)?;
+ self.after_start_element();
+ Ok(())
+ }
+
+ pub fn emit_start_element<W>(&mut self, target: &mut W,
+ name: Name,
+ attributes: &[Attribute]) -> Result<()>
+ where W: Write
+ {
+ if self.config.keep_element_names_stack {
+ self.element_names.push(name.to_owned());
+ }
+
+ self.emit_start_element_initial(target, name, attributes)?;
+ self.just_wrote_start_element = true;
+
+ if !self.config.normalize_empty_elements {
+ write!(target, ">")?;
+ }
+
+ Ok(())
+ }
+
+ pub fn emit_current_namespace_attributes<W>(&mut self, target: &mut W) -> Result<()>
+ where W: Write
+ {
+ for (prefix, uri) in self.nst.peek() {
+ match prefix {
+ // internal namespaces are not emitted
+ NS_XMLNS_PREFIX | NS_XML_PREFIX => Ok(()),
+ //// there is already a namespace binding with this prefix in scope
+ //prefix if self.nst.get(prefix) == Some(uri) => Ok(()),
+ // emit xmlns only if it is overridden
+ NS_NO_PREFIX => if uri != NS_EMPTY_URI {
+ write!(target, " xmlns=\"{}\"", uri)
+ } else { Ok(()) },
+ // everything else
+ prefix => write!(target, " xmlns:{}=\"{}\"", prefix, uri)
+ }?;
+ }
+ Ok(())
+ }
+
+ pub fn emit_attributes<W: Write>(&mut self, target: &mut W,
+ attributes: &[Attribute]) -> Result<()> {
+ for attr in attributes.iter() {
+ write!(
+ target, " {}=\"{}\"",
+ attr.name.repr_display(),
+ if self.config.perform_escaping { escape_str_attribute(attr.value) } else { Cow::Borrowed(attr.value) }
+ )?
+ }
+ Ok(())
+ }
+
+ pub fn emit_end_element<W: Write>(&mut self, target: &mut W,
+ name: Option<Name>) -> Result<()> {
+ let owned_name = if self.config.keep_element_names_stack {
+ Some(self.element_names.pop().ok_or(EmitterError::LastElementNameNotAvailable)?)
+ } else {
+ None
+ };
+
+ // Check that last started element name equals to the provided name, if there are both
+ if let Some(ref last_name) = owned_name {
+ if let Some(ref name) = name {
+ if last_name.borrow() != *name {
+ return Err(EmitterError::EndElementNameIsNotEqualToLastStartElementName);
+ }
+ }
+ }
+
+ if let Some(name) = owned_name.as_ref().map(|n| n.borrow()).or(name) {
+ if self.config.normalize_empty_elements && self.just_wrote_start_element {
+ self.just_wrote_start_element = false;
+ let termination = if self.config.pad_self_closing { " />" } else { "/>" };
+ let result = target.write_all(termination.as_bytes()).map_err(From::from);
+ self.after_end_element();
+ result
+ } else {
+ self.just_wrote_start_element = false;
+
+ self.before_end_element(target)?;
+ let result = write!(target, "</{}>", name.repr_display()).map_err(From::from);
+ self.after_end_element();
+
+ result
+ }
+ } else {
+ Err(EmitterError::EndElementNameIsNotSpecified)
+ }
+ }
+
+ pub fn emit_cdata<W: Write>(&mut self, target: &mut W, content: &str) -> Result<()> {
+ self.fix_non_empty_element(target)?;
+ if self.config.cdata_to_characters {
+ self.emit_characters(target, content)
+ } else {
+ // TODO: escape ']]>' characters in CDATA as two adjacent CDATA blocks
+ target.write_all(b"<![CDATA[")?;
+ target.write_all(content.as_bytes())?;
+ target.write_all(b"]]>")?;
+
+ self.after_text();
+
+ Ok(())
+ }
+ }
+
+ pub fn emit_characters<W: Write>(&mut self, target: &mut W,
+ content: &str) -> Result<()> {
+ self.check_document_started(target)?;
+ self.fix_non_empty_element(target)?;
+ target.write_all(
+ (if self.config.perform_escaping {
+ escape_str_pcdata(content)
+ } else {
+ Cow::Borrowed(content)
+ }).as_bytes()
+ )?;
+ self.after_text();
+ Ok(())
+ }
+
+ pub fn emit_comment<W: Write>(&mut self, target: &mut W, content: &str) -> Result<()> {
+ self.fix_non_empty_element(target)?;
+
+ // TODO: add escaping dashes at the end of the comment
+
+ let autopad_comments = self.config.autopad_comments;
+ let write = |target: &mut W| -> Result<()> {
+ target.write_all(b"<!--")?;
+
+ if autopad_comments && !content.starts_with(char::is_whitespace) {
+ target.write_all(b" ")?;
+ }
+
+ target.write_all(content.as_bytes())?;
+
+ if autopad_comments && !content.ends_with(char::is_whitespace) {
+ target.write_all(b" ")?;
+ }
+
+ target.write_all(b"-->")?;
+
+ Ok(())
+ };
+
+ self.before_markup(target)?;
+ let result = write(target);
+ self.after_markup();
+
+ result
+ }
+}
diff --git a/third_party/rust/xml-rs/src/writer/events.rs b/third_party/rust/xml-rs/src/writer/events.rs
new file mode 100644
index 0000000000..1f7040f66a
--- /dev/null
+++ b/third_party/rust/xml-rs/src/writer/events.rs
@@ -0,0 +1,241 @@
+//! Contains `XmlEvent` datatype, instances of which are consumed by the writer.
+
+use std::borrow::Cow;
+
+use name::Name;
+use attribute::Attribute;
+use common::XmlVersion;
+use namespace::{Namespace, NS_NO_PREFIX};
+
+/// A part of an XML output stream.
+///
+/// Objects of this enum are consumed by `EventWriter`. They correspond to different parts of
+/// an XML document.
+#[derive(Debug)]
+pub enum XmlEvent<'a> {
+ /// Corresponds to XML document declaration.
+ ///
+ /// This event should always be written before any other event. If it is not written
+ /// at all, a default XML declaration will be outputted if the corresponding option
+ /// is set in the configuration. Otherwise an error will be returned.
+ StartDocument {
+ /// XML version.
+ ///
+ /// Defaults to `XmlVersion::Version10`.
+ version: XmlVersion,
+
+ /// XML document encoding.
+ ///
+ /// Defaults to `Some("UTF-8")`.
+ encoding: Option<&'a str>,
+
+ /// XML standalone declaration.
+ ///
+ /// Defaults to `None`.
+ standalone: Option<bool>
+ },
+
+ /// Denotes an XML processing instruction.
+ ProcessingInstruction {
+ /// Processing instruction target.
+ name: &'a str,
+
+ /// Processing instruction content.
+ data: Option<&'a str>
+ },
+
+ /// Denotes a beginning of an XML element.
+ StartElement {
+ /// Qualified name of the element.
+ name: Name<'a>,
+
+ /// A list of attributes associated with the element.
+ ///
+ /// Currently attributes are not checked for duplicates (TODO). Attribute values
+ /// will be escaped, and all characters invalid for attribute values like `"` or `<`
+ /// will be changed into character entities.
+ attributes: Cow<'a, [Attribute<'a>]>,
+
+ /// Contents of the namespace mapping at this point of the document.
+ ///
+ /// This mapping will be inspected for "new" entries, and if at this point of the document
+ /// a particular pair of prefix and namespace URI is already defined, no namespace
+ /// attributes will be emitted.
+ namespace: Cow<'a, Namespace>,
+ },
+
+ /// Denotes an end of an XML element.
+ EndElement {
+ /// Optional qualified name of the element.
+ ///
+ /// If `None`, then it is assumed that the element name should be the last valid one.
+ /// If `Some` and element names tracking is enabled, then the writer will check it for
+ /// correctness.
+ name: Option<Name<'a>>
+ },
+
+ /// Denotes CDATA content.
+ ///
+ /// This event contains unparsed data, and no escaping will be performed when writing it
+ /// to the output stream.
+ CData(&'a str),
+
+ /// Denotes a comment.
+ ///
+ /// The string will be checked for invalid sequences and error will be returned by the
+ /// write operation
+ Comment(&'a str),
+
+ /// Denotes character data outside of tags.
+ ///
+ /// Contents of this event will be escaped if `perform_escaping` option is enabled,
+ /// that is, every character invalid for PCDATA will appear as a character entity.
+ Characters(&'a str)
+}
+
+impl<'a> XmlEvent<'a> {
+ /// Returns an writer event for a processing instruction.
+ #[inline]
+ pub fn processing_instruction(name: &'a str, data: Option<&'a str>) -> XmlEvent<'a> {
+ XmlEvent::ProcessingInstruction { name: name, data: data }
+ }
+
+ /// Returns a builder for a starting element.
+ ///
+ /// This builder can then be used to tweak attributes and namespace starting at
+ /// this element.
+ #[inline]
+ pub fn start_element<S>(name: S) -> StartElementBuilder<'a> where S: Into<Name<'a>> {
+ StartElementBuilder {
+ name: name.into(),
+ attributes: Vec::new(),
+ namespace: Namespace::empty().into()
+ }
+ }
+
+ /// Returns a builder for an closing element.
+ ///
+ /// This method, unline `start_element()`, does not accept a name because by default
+ /// the writer is able to determine it automatically. However, when this functionality
+ /// is disabled, it is possible to specify the name with `name()` method on the builder.
+ #[inline]
+ pub fn end_element() -> EndElementBuilder<'a> {
+ EndElementBuilder { name: None }
+ }
+
+ /// Returns a CDATA event.
+ ///
+ /// Naturally, the provided string won't be escaped, except for closing CDATA token `]]>`
+ /// (depending on the configuration).
+ #[inline]
+ pub fn cdata(data: &'a str) -> XmlEvent<'a> { XmlEvent::CData(data) }
+
+ /// Returns a regular characters (PCDATA) event.
+ ///
+ /// All offending symbols, in particular, `&` and `<`, will be escaped by the writer.
+ #[inline]
+ pub fn characters(data: &'a str) -> XmlEvent<'a> { XmlEvent::Characters(data) }
+
+ /// Returns a comment event.
+ #[inline]
+ pub fn comment(data: &'a str) -> XmlEvent<'a> { XmlEvent::Comment(data) }
+}
+
+impl<'a> From<&'a str> for XmlEvent<'a> {
+ #[inline]
+ fn from(s: &'a str) -> XmlEvent<'a> { XmlEvent::Characters(s) }
+}
+
+pub struct EndElementBuilder<'a> {
+ name: Option<Name<'a>>
+}
+
+/// A builder for a closing element event.
+impl<'a> EndElementBuilder<'a> {
+ /// Sets the name of this closing element.
+ ///
+ /// Usually the writer is able to determine closing element names automatically. If
+ /// this functionality is enabled (by default it is), then this name is checked for correctness.
+ /// It is possible, however, to disable such behavior; then the user must ensure that
+ /// closing element name is correct manually.
+ #[inline]
+ pub fn name<N>(mut self, name: N) -> EndElementBuilder<'a> where N: Into<Name<'a>> {
+ self.name = Some(name.into());
+ self
+ }
+}
+
+impl<'a> From<EndElementBuilder<'a>> for XmlEvent<'a> {
+ fn from(b: EndElementBuilder<'a>) -> XmlEvent<'a> {
+ XmlEvent::EndElement { name: b.name }
+ }
+}
+
+/// A builder for a starting element event.
+pub struct StartElementBuilder<'a> {
+ name: Name<'a>,
+ attributes: Vec<Attribute<'a>>,
+ namespace: Namespace
+}
+
+impl<'a> StartElementBuilder<'a> {
+ /// Sets an attribute value of this element to the given string.
+ ///
+ /// This method can be used to add attributes to the starting element. Name is a qualified
+ /// name; its namespace is ignored, but its prefix is checked for correctness, that is,
+ /// it is checked that the prefix is bound to some namespace in the current context.
+ ///
+ /// Currently attributes are not checked for duplicates. Note that duplicate attributes
+ /// are a violation of XML document well-formedness.
+ ///
+ /// The writer checks that you don't specify reserved prefix names, for example `xmlns`.
+ #[inline]
+ pub fn attr<N>(mut self, name: N, value: &'a str) -> StartElementBuilder<'a>
+ where N: Into<Name<'a>>
+ {
+ self.attributes.push(Attribute::new(name.into(), value));
+ self
+ }
+
+ /// Adds a namespace to the current namespace context.
+ ///
+ /// If no namespace URI was bound to the provided prefix at this point of the document,
+ /// then the mapping from the prefix to the provided namespace URI will be written as
+ /// a part of this element attribute set.
+ ///
+ /// If the same namespace URI was bound to the provided prefix at this point of the document,
+ /// then no namespace attributes will be emitted.
+ ///
+ /// If some other namespace URI was bound to the provided prefix at this point of the document,
+ /// then another binding will be added as a part of this element attribute set, shadowing
+ /// the outer binding.
+ #[inline]
+ pub fn ns<S1, S2>(mut self, prefix: S1, uri: S2) -> StartElementBuilder<'a>
+ where S1: Into<String>, S2: Into<String>
+ {
+ self.namespace.put(prefix, uri);
+ self
+ }
+
+ /// Adds a default namespace mapping to the current namespace context.
+ ///
+ /// Same rules as for `ns()` are also valid for the default namespace mapping.
+ #[inline]
+ pub fn default_ns<S>(mut self, uri: S) -> StartElementBuilder<'a>
+ where S: Into<String>
+ {
+ self.namespace.put(NS_NO_PREFIX, uri);
+ self
+ }
+}
+
+impl<'a> From<StartElementBuilder<'a>> for XmlEvent<'a> {
+ #[inline]
+ fn from(b: StartElementBuilder<'a>) -> XmlEvent<'a> {
+ XmlEvent::StartElement {
+ name: b.name,
+ attributes: Cow::Owned(b.attributes),
+ namespace: Cow::Owned(b.namespace)
+ }
+ }
+}
diff --git a/third_party/rust/xml-rs/src/writer/mod.rs b/third_party/rust/xml-rs/src/writer/mod.rs
new file mode 100644
index 0000000000..ea1b24266f
--- /dev/null
+++ b/third_party/rust/xml-rs/src/writer/mod.rs
@@ -0,0 +1,93 @@
+//! Contains high-level interface for an events-based XML emitter.
+//!
+//! The most important type in this module is `EventWriter` which allows writing an XML document
+//! to some output stream.
+
+pub use self::emitter::Result;
+pub use self::emitter::EmitterError as Error;
+pub use self::config::EmitterConfig;
+pub use self::events::XmlEvent;
+
+use self::emitter::Emitter;
+
+use std::io::prelude::*;
+
+mod emitter;
+mod config;
+pub mod events;
+
+/// A wrapper around an `std::io::Write` instance which emits XML document according to provided
+/// events.
+pub struct EventWriter<W> {
+ sink: W,
+ emitter: Emitter
+}
+
+impl<W: Write> EventWriter<W> {
+ /// Creates a new `EventWriter` out of an `std::io::Write` instance using the default
+ /// configuration.
+ #[inline]
+ pub fn new(sink: W) -> EventWriter<W> {
+ EventWriter::new_with_config(sink, EmitterConfig::new())
+ }
+
+ /// Creates a new `EventWriter` out of an `std::io::Write` instance using the provided
+ /// configuration.
+ #[inline]
+ pub fn new_with_config(sink: W, config: EmitterConfig) -> EventWriter<W> {
+ EventWriter {
+ sink,
+ emitter: Emitter::new(config)
+ }
+ }
+
+ /// Writes the next piece of XML document according to the provided event.
+ ///
+ /// Note that output data may not exactly correspond to the written event because
+ /// of various configuration options. For example, `XmlEvent::EndElement` may
+ /// correspond to a separate closing element or it may cause writing an empty element.
+ /// Another example is that `XmlEvent::CData` may be represented as characters in
+ /// the output stream.
+ pub fn write<'a, E>(&mut self, event: E) -> Result<()> where E: Into<XmlEvent<'a>> {
+ match event.into() {
+ XmlEvent::StartDocument { version, encoding, standalone } =>
+ self.emitter.emit_start_document(&mut self.sink, version, encoding.unwrap_or("UTF-8"), standalone),
+ XmlEvent::ProcessingInstruction { name, data } =>
+ self.emitter.emit_processing_instruction(&mut self.sink, name, data),
+ XmlEvent::StartElement { name, attributes, namespace } => {
+ self.emitter.namespace_stack_mut().push_empty().checked_target().extend(namespace.as_ref());
+ self.emitter.emit_start_element(&mut self.sink, name, &attributes)
+ }
+ XmlEvent::EndElement { name } => {
+ let r = self.emitter.emit_end_element(&mut self.sink, name);
+ self.emitter.namespace_stack_mut().try_pop();
+ r
+ }
+ XmlEvent::Comment(content) =>
+ self.emitter.emit_comment(&mut self.sink, content),
+ XmlEvent::CData(content) =>
+ self.emitter.emit_cdata(&mut self.sink, content),
+ XmlEvent::Characters(content) =>
+ self.emitter.emit_characters(&mut self.sink, content)
+ }
+ }
+
+ /// Returns a mutable reference to the underlying `Writer`.
+ ///
+ /// Note that having a reference to the underlying sink makes it very easy to emit invalid XML
+ /// documents. Use this method with care. Valid use cases for this method include accessing
+ /// methods like `Write::flush`, which do not emit new data but rather change the state
+ /// of the stream itself.
+ pub fn inner_mut(&mut self) -> &mut W {
+ &mut self.sink
+ }
+
+ /// Unwraps this `EventWriter`, returning the underlying writer.
+ ///
+ /// Note that this is a destructive operation: unwrapping a writer and then wrapping
+ /// it again with `EventWriter::new()` will create a fresh writer whose state will be
+ /// blank; for example, accumulated namespaces will be reset.
+ pub fn into_inner(self) -> W {
+ self.sink
+ }
+}