From 28045361f3f970ee5c6145e975dc600055c9b2f1 Mon Sep 17 00:00:00 2001 From: lumi Date: Sun, 19 Feb 2017 20:46:44 +0100 Subject: [PATCH] initial commit --- Cargo.toml | 7 ++ src/error.rs | 32 ++++++ src/lib.rs | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/error.rs create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..765a12d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "minidom" +version = "0.1.0" +authors = ["lumi "] + +[dependencies] +xml-rs = "*" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..db18406 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,32 @@ +use std::io; + +use std::convert::From; + +use xml::writer::Error as WriterError; +use xml::reader::Error as ReaderError; + +#[derive(Debug)] +pub enum Error { + IoError(io::Error), + XmlWriterError(WriterError), + XmlReaderError(ReaderError), + EndOfDocument, +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Error::IoError(err) + } +} + +impl From for Error { + fn from(err: WriterError) -> Error { + Error::XmlWriterError(err) + } +} + +impl From for Error { + fn from(err: ReaderError) -> Error { + Error::XmlReaderError(err) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7509f40 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,281 @@ +extern crate xml; + +pub mod error; + +use std::io::prelude::*; + +use std::convert::From; + +use std::fmt; + +use xml::name::{OwnedName, Name}; +use xml::reader::{XmlEvent as ReaderEvent, EventReader}; +use xml::writer::{XmlEvent as WriterEvent, EventWriter}; +use xml::attribute::OwnedAttribute; + +use error::Error; + +#[derive(Clone, PartialEq, Eq)] +pub struct Element { + name: OwnedName, + attributes: Vec, + children: Vec, +} + +impl fmt::Debug for Element { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "<{}", self.name)?; + for attr in &self.attributes { + write!(fmt, " {}", attr)?; + } + write!(fmt, ">")?; + for child in &self.children { + match *child { + Fork::Element(ref e) => { + write!(fmt, "{:?}", e)?; + }, + Fork::Text(ref s) => { + write!(fmt, "{}", s)?; + }, + } + } + write!(fmt, "", self.name)?; + Ok(()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Fork { + Element(Element), + Text(String), +} + +impl Element { + pub fn new(name: OwnedName, attributes: Vec) -> Element { + Element { + name: name, + attributes: attributes, + children: Vec::new(), + } + } + + pub fn builder>(name: S) -> ElementBuilder { + ElementBuilder { + name: OwnedName::local(name), + attributes: Vec::new(), + } + } + + pub fn tag(&self) -> &str { + &self.name.local_name + } + + pub fn ns(&self) -> Option<&str> { + self.name.namespace.as_ref() + .map(String::as_ref) + } + + pub fn attr(&self, key: &str) -> Option<&str> { + for attr in &self.attributes { + if attr.name.local_name == key { + return Some(&attr.value); + } + } + None + } + + pub fn from_reader(reader: &mut EventReader) -> Result { + loop { + let e = reader.next()?; + match e { + ReaderEvent::StartElement { name, attributes, .. } => { + let mut root = Element::new(name, attributes); + root.from_reader_inner(reader); + return Ok(root); + }, + ReaderEvent::EndDocument => { + return Err(Error::EndOfDocument); + }, + _ => () // TODO: may need more errors + } + } + } + + fn from_reader_inner(&mut self, reader: &mut EventReader) -> Result<(), Error> { + loop { + let e = reader.next()?; + match e { + ReaderEvent::StartElement { name, attributes, .. } => { + let elem = Element::new(name, attributes); + let elem_ref = self.append_child(elem); + elem_ref.from_reader_inner(reader); + }, + ReaderEvent::EndElement { .. } => { + // TODO: may want to check whether we're closing the correct element + return Ok(()); + }, + ReaderEvent::Characters(s) => { + self.append_text_node(s); + }, + ReaderEvent::CData(s) => { + self.append_text_node(s); + }, + ReaderEvent::EndDocument => { + return Err(Error::EndOfDocument); + }, + _ => (), // TODO: may need to implement more + } + } + } + + pub fn write_to(&self, writer: &mut EventWriter) -> Result<(), Error> { + let mut start = WriterEvent::start_element(self.name.borrow()); + if let Some(ref ns) = self.name.namespace { + start = start.default_ns(ns.as_ref()); + } + for attr in &self.attributes { // TODO: I think this could be done a lot more efficiently + start = start.attr(attr.name.borrow(), &attr.value); + } + writer.write(start)?; + for child in &self.children { + match *child { + Fork::Element(ref e) => { + e.write_to(writer)?; + }, + Fork::Text(ref s) => { + writer.write(WriterEvent::characters(s))?; + }, + } + } + writer.write(WriterEvent::end_element())?; + Ok(()) + } + + pub fn children<'a>(&'a self) -> Children<'a> { + unimplemented!(); + } + + pub fn children_mut<'a>(&'a mut self) -> ChildrenMut<'a> { + unimplemented!(); + } + + pub fn append_child(&mut self, child: Element) -> &mut Element { + self.children.push(Fork::Element(child)); + if let Fork::Element(ref mut cld) = *self.children.last_mut().unwrap() { + cld + } + else { + unreachable!() + } + } + + pub fn append_text_node>(&mut self, child: S) { + self.children.push(Fork::Text(child.into())); + } + + pub fn text(&self) -> &str { + unimplemented!() + } + + pub fn get_child<'a, N: Into>>(&self, name: N) -> Option<&Element> { + unimplemented!() + } + + pub fn get_child_mut<'a, N: Into>>(&mut self, name: N) -> Option<&mut Element> { + unimplemented!() + } + + pub fn into_child<'a, N: Into>>(self, name: N) -> Option { + unimplemented!() + } +} + +pub struct Children<'a> { + elem: &'a Element, +} + +pub struct ChildrenMut<'a> { + elem: &'a mut Element, +} + +pub struct ElementBuilder { + name: OwnedName, + attributes: Vec, +} + +impl ElementBuilder { + pub fn ns>(mut self, namespace: S) -> ElementBuilder { + self.name.namespace = Some(namespace.into()); + self + } + + pub fn attr, V: Into>(mut self, name: S, value: V) -> ElementBuilder { + self.attributes.push(OwnedAttribute::new(OwnedName::local(name), value)); + self + } + + pub fn attr_ns, N: Into, V: Into>(mut self, name: S, namespace: N, value: V) -> ElementBuilder { + self.attributes.push(OwnedAttribute::new(OwnedName::qualified::<_, _, &'static str>(name, namespace, None), value)); + self + } + + pub fn build(self) -> Element { + Element::new(self.name, self.attributes) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_STRING: &'static str = r#"meownya"#; + + fn build_test_tree() -> Element { + let mut root = Element::builder("root") + .ns("root_ns") + .attr("a", "b") + .build(); + root.append_text_node("meow"); + let child = Element::builder("child") + .attr("c", "d") + .build(); + root.append_child(child); + let other_child = Element::builder("child") + .ns("child_ns") + .attr("d", "e") + .build(); + root.append_child(other_child); + root.append_text_node("nya"); + root + } + + #[test] + fn reader_works() { + use std::io::Cursor; + let mut reader = EventReader::new(Cursor::new(TEST_STRING)); + // TODO: fix a bunch of namespace stuff so this test passes + assert_eq!(Element::from_reader(&mut reader).unwrap(), build_test_tree()); + } + + #[test] + fn writer_works() { + let root = build_test_tree(); + let mut out = Vec::new(); + { + let mut writer = EventWriter::new(&mut out); + root.write_to(&mut writer); + } + assert_eq!(String::from_utf8(out).unwrap(), TEST_STRING); + } + + #[test] + fn builder_works() { + let elem = Element::builder("a") + .ns("b") + .attr("c", "d") + .build(); + assert_eq!(elem.tag(), "a"); + assert_eq!(elem.ns(), Some("b")); + assert_eq!(elem.attr("c"), Some("d")); + } +}