extern crate xml;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::io::{Read, Write};
use std::iter::Filter;
use std::slice::{Iter, IterMut};
#[derive(Debug)]
pub enum Error {
ParseError(xml::reader::Error),
WriteError(xml::writer::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::ParseError(ref e) => e.fmt(f),
Error::WriteError(ref e) => e.fmt(f),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::ParseError(ref e) => e.description(),
Error::WriteError(_) => "Error writing XML element",
}
}
}
impl From<xml::reader::Error> for Error {
fn from(err: xml::reader::Error) -> Error {
Error::ParseError(err)
}
}
impl From<xml::writer::Error> for Error {
fn from(err: xml::writer::Error) -> Error {
Error::WriteError(err)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum XmlVersion {
Version10,
Version11,
}
impl From<xml::common::XmlVersion> for XmlVersion {
fn from(v: xml::common::XmlVersion) -> XmlVersion {
match v {
xml::common::XmlVersion::Version10 => XmlVersion::Version10,
xml::common::XmlVersion::Version11 => XmlVersion::Version11,
}
}
}
impl Into<xml::common::XmlVersion> for XmlVersion {
fn into(self) -> xml::common::XmlVersion {
match self {
XmlVersion::Version10 => xml::common::XmlVersion::Version10,
XmlVersion::Version11 => xml::common::XmlVersion::Version11,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Element {
pub prefix: Option<String>,
pub name: String,
pub attributes: HashMap<String, String>,
pub children: Vec<Element>,
pub text: Option<String>,
pub cdata: Option<String>,
}
impl Default for Element {
fn default() -> Self {
Element{
prefix: None,
name: "tag".to_owned(),
attributes: HashMap::new(),
children: Vec::new(),
text: None,
cdata: None,
}
}
}
impl Element {
pub fn new<S>(name: S) -> Element
where S: Into<String>
{
Element{name: name.into(), .. Element::default()}
}
fn parse<R: Read>(&mut self, mut reader: &mut xml::reader::EventReader<R>) -> Result<(), Error> {
use xml::reader::XmlEvent;
loop {
let ev = try!(reader.next());
match ev {
XmlEvent::StartElement{name, attributes, ..} => {
let mut attr_map = HashMap::new();
for attr in attributes {
let attr_name = match attr.name.prefix {
Some(prefix) => format!("{}:{}", prefix, attr.name.local_name),
None => attr.name.local_name,
};
attr_map.insert(attr_name, attr.value);
}
let mut child = Element{
prefix: name.prefix,
name: name.local_name,
attributes: attr_map,
.. Element::default()
};
try!(child.parse(&mut reader));
self.children.push(child);
},
XmlEvent::EndElement{name} => {
if name.prefix == self.prefix && name.local_name == self.name {
return Ok(());
} else {
panic!("Unexpected closing tag: {}, expected {}", name, self.name);
}
},
XmlEvent::Characters(s) => {
let text = match self.text {
Some(ref v) => v.clone(),
None => String::new(),
};
self.text = Some(text + &s)
},
XmlEvent::CData(s) => {
let cdata = match self.cdata {
Some(ref v) => v.clone(),
None => String::new(),
};
self.cdata = Some(cdata + &s);
},
XmlEvent::Whitespace(_) => {},
XmlEvent::Comment(_) => {},
_ => {},
}
}
}
fn write<W: Write>(&self, writer: &mut xml::writer::EventWriter<W>) -> Result<(), Error> {
use xml::writer::XmlEvent;
use xml::name::Name;
use xml::attribute::Attribute;
use xml::namespace::Namespace;
let name = Name::local(&self.name);
let mut attributes = Vec::with_capacity(self.attributes.len());
for (k, v) in self.attributes.iter() {
attributes.push(Attribute{name: Name::local(k), value: v});
}
let namespace = Namespace::empty();
try!(writer.write(XmlEvent::StartElement{
name: name,
attributes: Cow::Owned(attributes),
namespace: Cow::Owned(namespace),
}));
if let Some(ref text) = self.text {
try!(writer.write(XmlEvent::Characters(&text[..])));
}
if let Some(ref cdata) = self.cdata {
try!(writer.write(XmlEvent::CData(&cdata[..])));
}
for ref e in &self.children {
try!(e.write(writer));
}
try!(writer.write(XmlEvent::EndElement{name: Some(name)}));
Ok(())
}
pub fn find_child<P>(&self, predicate: P) -> Option<&Element>
where P: for<'r> Fn(&'r &Element) -> bool
{
self.children.iter().find(predicate)
}
pub fn find_child_mut<P>(&mut self, predicate: P) -> Option<&mut Element>
where P: for<'r> FnMut(&'r &mut Element) -> bool
{
self.children.iter_mut().find(predicate)
}
pub fn filter_children<P>(&self, predicate: P) -> Filter<Iter<Element>, P>
where P: for<'r> Fn(&'r &Element) -> bool
{
self.children.iter().filter(predicate)
}
pub fn filter_children_mut<P>(&mut self, predicate: P) -> Filter<IterMut<Element>, P>
where P: for<'r> FnMut(&'r &mut Element) -> bool
{
self.children.iter_mut().filter(predicate)
}
}
impl fmt::Display for Element {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let doc = Document{
root: Some(self.clone()),
.. Document::default()
};
let mut v = Vec::<u8>::new();
doc._write(&mut v, false, " ").unwrap();
let s = String::from_utf8(v).unwrap();
f.write_str(&s[..])
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Document {
pub version: XmlVersion,
pub encoding: String,
pub root: Option<Element>,
}
impl Default for Document {
fn default() -> Self {
Document{
version: XmlVersion::Version10,
encoding: "UTF-8".to_owned(),
root: None,
}
}
}
impl Document {
pub fn new() -> Document {
Document{.. Document::default()}
}
pub fn parse<R: Read>(r: R) -> Result<Document, Error> {
use xml::reader::{EventReader, XmlEvent};
let mut reader = EventReader::new(r);
let mut doc = Document::new();
loop {
let ev = try!(reader.next());
match ev {
XmlEvent::StartDocument{version, encoding, ..} => {
doc.version = XmlVersion::from(version);
doc.encoding = encoding;
},
XmlEvent::StartElement{name, attributes, ..} => {
let mut attr_map = HashMap::new();
for attr in attributes {
let attr_name = match attr.name.prefix {
Some(prefix) => format!("{}:{}", prefix, attr.name.local_name),
None => attr.name.local_name,
};
attr_map.insert(attr_name, attr.value);
}
let mut root = Element{
prefix: name.prefix,
name: name.local_name,
attributes: attr_map,
.. Element::default()
};
try!(root.parse(&mut reader));
doc.root = Some(root);
},
XmlEvent::EndDocument => break,
_ => {},
}
}
Ok(doc)
}
pub fn write<W: Write>(&self, mut w: &mut W) -> Result<(), Error> {
self._write(&mut w, true, " ")
}
fn _write<W: Write>(&self, w: &mut W, document_decl: bool, indent_str: &'static str) -> Result<(), Error> {
use xml::writer::{EmitterConfig, XmlEvent};
let mut writer = EmitterConfig::new()
.perform_indent(true)
.write_document_declaration(document_decl)
.indent_string(indent_str)
.create_writer(w);
if document_decl {
try!(writer.write(XmlEvent::StartDocument{
version: self.version.into(),
encoding: Some(&self.encoding),
standalone: None,
}));
}
if let Some(ref e) = self.root {
try!(e.write(&mut writer));
}
Ok(())
}
}
impl fmt::Display for Document {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut v = Vec::<u8>::new();
self.write(&mut v).unwrap();
let s = String::from_utf8(v).unwrap();
f.write_str(&s[..])
}
}