extern crate xml;
use std::borrow::Cow;
#[cfg(not(feature = "attribute-order"))]
use std::collections::HashMap as AttributeMap;
use std::fmt;
use std::io::{Read, Write};
pub use xml::namespace::Namespace;
use xml::reader::{EventReader, ParserConfig, XmlEvent};
pub use xml::writer::{EmitterConfig, Error};
#[cfg(feature = "attribute-order")]
use indexmap::map::IndexMap as AttributeMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum XMLNode {
Element(Element),
Comment(String),
CData(String),
Text(String),
ProcessingInstruction(String, Option<String>),
}
impl XMLNode {
pub fn as_element(&self) -> Option<&Element> {
if let XMLNode::Element(e) = self {
Some(e)
} else {
None
}
}
pub fn as_comment(&self) -> Option<&str> {
if let XMLNode::Comment(c) = self {
Some(c)
} else {
None
}
}
pub fn as_cdata(&self) -> Option<&str> {
if let XMLNode::CData(c) = self {
Some(c)
} else {
None
}
}
pub fn as_text(&self) -> Option<&str> {
if let XMLNode::Text(c) = self {
Some(c)
} else {
None
}
}
pub fn as_processing_instruction(&self) -> Option<(&str, Option<&str>)> {
if let XMLNode::ProcessingInstruction(s, o) = self {
Some((s, o.as_ref().map(|s| s.as_str())))
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Element {
pub prefix: Option<String>,
pub namespace: Option<String>,
pub namespaces: Option<Namespace>,
pub name: String,
pub attributes: AttributeMap<String, String>,
pub children: Vec<XMLNode>,
}
#[derive(Debug)]
pub enum ParseError {
MalformedXml(xml::reader::Error),
CannotParse,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParseError::MalformedXml(ref e) => write!(f, "Malformed XML. {}", e),
ParseError::CannotParse => write!(f, "Cannot parse"),
}
}
}
impl std::error::Error for ParseError {
fn description(&self) -> &str {
match *self {
ParseError::MalformedXml(..) => "Malformed XML",
ParseError::CannotParse => "Cannot parse",
}
}
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
ParseError::MalformedXml(ref e) => Some(e),
ParseError::CannotParse => None,
}
}
}
fn build<B: Read>(reader: &mut EventReader<B>, mut elem: Element) -> Result<Element, ParseError> {
loop {
match reader.next() {
Ok(XmlEvent::EndElement { ref name }) => {
if name.local_name == elem.name {
return Ok(elem);
} else {
return Err(ParseError::CannotParse);
}
}
Ok(XmlEvent::StartElement {
name,
attributes,
namespace,
}) => {
let mut attr_map = AttributeMap::new();
for attr in attributes {
attr_map.insert(attr.name.local_name, attr.value);
}
let new_elem = Element {
prefix: name.prefix,
namespace: name.namespace,
namespaces: if namespace.is_essentially_empty() {
None
} else {
Some(namespace)
},
name: name.local_name,
attributes: attr_map,
children: Vec::new(),
};
elem.children
.push(XMLNode::Element(build(reader, new_elem)?));
}
Ok(XmlEvent::Characters(s)) => elem.children.push(XMLNode::Text(s)),
Ok(XmlEvent::Whitespace(..)) => (),
Ok(XmlEvent::Comment(s)) => elem.children.push(XMLNode::Comment(s)),
Ok(XmlEvent::CData(s)) => elem.children.push(XMLNode::Text(s)),
Ok(XmlEvent::ProcessingInstruction { name, data }) => elem
.children
.push(XMLNode::ProcessingInstruction(name, data)),
Ok(XmlEvent::StartDocument { .. }) | Ok(XmlEvent::EndDocument) => {
return Err(ParseError::CannotParse)
}
Err(e) => return Err(ParseError::MalformedXml(e)),
}
}
}
impl Element {
pub fn new(name: &str) -> Element {
Element {
name: String::from(name),
prefix: None,
namespace: None,
namespaces: None,
attributes: AttributeMap::new(),
children: Vec::new(),
}
}
pub fn parse_all<R: Read>(r: R) -> Result<Vec<XMLNode>, ParseError> {
let parser_config = ParserConfig::new().ignore_comments(false);
let mut reader = EventReader::new_with_config(r, parser_config);
let mut root_nodes = Vec::new();
loop {
match reader.next() {
Ok(XmlEvent::StartElement {
name,
attributes,
namespace,
}) => {
let mut attr_map = AttributeMap::with_capacity(attributes.len());
for attr in attributes {
attr_map.insert(attr.name.local_name, attr.value);
}
let root = Element {
prefix: name.prefix,
namespace: name.namespace,
namespaces: if namespace.is_essentially_empty() {
None
} else {
Some(namespace)
},
name: name.local_name,
attributes: attr_map,
children: Vec::new(),
};
root_nodes.push(XMLNode::Element(build(&mut reader, root)?));
}
Ok(XmlEvent::Comment(comment_string)) => {
root_nodes.push(XMLNode::Comment(comment_string))
}
Ok(XmlEvent::Characters(text_string)) => {
root_nodes.push(XMLNode::Text(text_string))
}
Ok(XmlEvent::CData(cdata_string)) => root_nodes.push(XMLNode::CData(cdata_string)),
Ok(XmlEvent::Whitespace(..)) | Ok(XmlEvent::StartDocument { .. }) => continue,
Ok(XmlEvent::ProcessingInstruction { name, data }) => {
root_nodes.push(XMLNode::ProcessingInstruction(name, data))
}
Ok(XmlEvent::EndElement { .. }) => (),
Ok(XmlEvent::EndDocument) => return Ok(root_nodes),
Err(e) => return Err(ParseError::MalformedXml(e)),
}
}
}
pub fn parse<R: Read>(r: R) -> Result<Element, ParseError> {
let nodes = Element::parse_all(r)?;
for node in nodes {
match node {
XMLNode::Element(elem) => return Ok(elem),
_ => (),
}
}
unreachable!();
}
fn _write<B: Write>(&self, emitter: &mut xml::writer::EventWriter<B>) -> Result<(), Error> {
use xml::attribute::Attribute;
use xml::name::Name;
use xml::writer::events::XmlEvent;
let mut name = Name::local(&self.name);
if let Some(ref ns) = self.namespace {
name.namespace = Some(ns);
}
if let Some(ref p) = self.prefix {
name.prefix = Some(p);
}
let mut attributes = Vec::with_capacity(self.attributes.len());
for (k, v) in &self.attributes {
attributes.push(Attribute {
name: Name::local(k),
value: v,
});
}
let empty_ns = Namespace::empty();
let namespace = if let Some(ref ns) = self.namespaces {
Cow::Borrowed(ns)
} else {
Cow::Borrowed(&empty_ns)
};
emitter.write(XmlEvent::StartElement {
name,
attributes: Cow::Owned(attributes),
namespace,
})?;
for node in &self.children {
match node {
XMLNode::Element(elem) => elem._write(emitter)?,
XMLNode::Text(text) => emitter.write(XmlEvent::Characters(text))?,
XMLNode::Comment(comment) => emitter.write(XmlEvent::Comment(comment))?,
XMLNode::CData(comment) => emitter.write(XmlEvent::CData(comment))?,
XMLNode::ProcessingInstruction(name, data) => match data.to_owned() {
Some(string) => emitter.write(XmlEvent::ProcessingInstruction {
name,
data: Some(&string),
})?,
None => emitter.write(XmlEvent::ProcessingInstruction { name, data: None })?,
},
}
}
emitter.write(XmlEvent::EndElement { name: Some(name) })?;
Ok(())
}
pub fn write<W: Write>(&self, w: W) -> Result<(), Error> {
self.write_with_config(w, EmitterConfig::new())
}
pub fn write_with_config<W: Write>(&self, w: W, config: EmitterConfig) -> Result<(), Error> {
use xml::common::XmlVersion;
use xml::writer::events::XmlEvent;
use xml::writer::EventWriter;
let mut emitter = EventWriter::new_with_config(w, config);
emitter.write(XmlEvent::StartDocument {
version: XmlVersion::Version10,
encoding: None,
standalone: None,
})?;
self._write(&mut emitter)
}
pub fn get_child<P: ElementPredicate>(&self, k: P) -> Option<&Element> {
self.children
.iter()
.filter_map(|e| match e {
XMLNode::Element(elem) => Some(elem),
_ => None,
})
.find(|e| k.match_element(e))
}
pub fn get_mut_child<P: ElementPredicate>(&mut self, k: P) -> Option<&mut Element> {
self.children
.iter_mut()
.filter_map(|e| match e {
XMLNode::Element(elem) => Some(elem),
_ => None,
})
.find(|e| k.match_element(e))
}
pub fn take_child<P: ElementPredicate>(&mut self, k: P) -> Option<Element> {
let index = self.children.iter().position(|e| match e {
XMLNode::Element(elem) => k.match_element(elem),
_ => false,
});
match index {
Some(index) => match self.children.remove(index) {
XMLNode::Element(elem) => Some(elem),
_ => None,
},
None => None,
}
}
pub fn get_text<'a>(&'a self) -> Option<Cow<'a, str>> {
let text_nodes: Vec<&'a str> = self
.children
.iter()
.filter_map(|node| node.as_text().or_else(|| node.as_cdata()))
.collect();
if text_nodes.is_empty() {
None
} else if text_nodes.len() == 1 {
Some(Cow::Borrowed(text_nodes[0]))
} else {
let mut full_text = String::new();
for text in text_nodes {
full_text.push_str(text);
}
Some(Cow::Owned(full_text))
}
}
}
pub trait ElementPredicate {
fn match_element(&self, e: &Element) -> bool;
}
impl<TN> ElementPredicate for (TN,)
where
String: PartialEq<TN>,
{
fn match_element(&self, e: &Element) -> bool {
e.name == self.0
}
}
impl<'a> ElementPredicate for &'a str {
fn match_element(&self, e: &Element) -> bool {
(*self,).match_element(e)
}
}
impl<'a> ElementPredicate for Cow<'a, str> {
fn match_element(&self, e: &Element) -> bool {
(&**self,).match_element(e)
}
}
impl ElementPredicate for String {
fn match_element(&self, e: &Element) -> bool {
(&**self,).match_element(e)
}
}
impl<TN, NS> ElementPredicate for (TN, NS)
where
String: PartialEq<TN>,
String: PartialEq<NS>,
{
fn match_element(&self, e: &Element) -> bool {
e.name == self.0
&& e.namespace
.as_ref()
.map(|ns| ns == &self.1)
.unwrap_or(false)
}
}