aboutsummaryrefslogtreecommitdiffstats
path: root/src/xml
diff options
context:
space:
mode:
authorEdward Barnard2018-05-17 20:30:41 +0100
committerEdward Barnard2018-05-17 20:50:21 +0100
commit2af279c344a6a917ce4102fec82e6cba8cf8b37c (patch)
treea2d3746d4981e8e22e30b91fd3b4d1821444777a /src/xml
parentd8a4f4ef2a0e80f1dc6237678410807cce83cce0 (diff)
downloadrust-plist-2af279c344a6a917ce4102fec82e6cba8cf8b37c.tar.bz2
Reorganise crate.
Diffstat (limited to 'src/xml')
-rw-r--r--src/xml/mod.rs5
-rw-r--r--src/xml/reader.rs199
-rw-r--r--src/xml/writer.rs259
3 files changed, 0 insertions, 463 deletions
diff --git a/src/xml/mod.rs b/src/xml/mod.rs
deleted file mode 100644
index 0400e13..0000000
--- a/src/xml/mod.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-mod reader;
-mod writer;
-
-pub use self::reader::EventReader;
-pub use self::writer::EventWriter;
diff --git a/src/xml/reader.rs b/src/xml/reader.rs
deleted file mode 100644
index 6642d59..0000000
--- a/src/xml/reader.rs
+++ /dev/null
@@ -1,199 +0,0 @@
-use base64;
-use std::io::Read;
-use std::str::FromStr;
-use xml_rs::reader::{EventReader as XmlEventReader, ParserConfig, XmlEvent};
-
-use {Date, Error, PlistEvent, Result};
-
-pub struct EventReader<R: Read> {
- xml_reader: XmlEventReader<R>,
- queued_event: Option<XmlEvent>,
- element_stack: Vec<String>,
- finished: bool,
-}
-
-impl<R: Read> EventReader<R> {
- pub fn new(reader: R) -> EventReader<R> {
- let config = ParserConfig::new()
- .trim_whitespace(false)
- .whitespace_to_characters(true)
- .cdata_to_characters(true)
- .ignore_comments(true)
- .coalesce_characters(true);
-
- EventReader {
- xml_reader: XmlEventReader::new_with_config(reader, config),
- queued_event: None,
- element_stack: Vec::new(),
- finished: false,
- }
- }
-
- fn read_content<F>(&mut self, f: F) -> Result<PlistEvent>
- where
- F: FnOnce(String) -> Result<PlistEvent>,
- {
- match self.xml_reader.next() {
- Ok(XmlEvent::Characters(s)) => f(s),
- Ok(event @ XmlEvent::EndElement { .. }) => {
- self.queued_event = Some(event);
- f("".to_owned())
- }
- _ => Err(Error::InvalidData),
- }
- }
-
- fn next_event(&mut self) -> ::std::result::Result<XmlEvent, ()> {
- if let Some(event) = self.queued_event.take() {
- Ok(event)
- } else {
- self.xml_reader.next().map_err(|_| ())
- }
- }
-
- fn read_next(&mut self) -> Option<Result<PlistEvent>> {
- loop {
- match self.next_event() {
- Ok(XmlEvent::StartElement { name, .. }) => {
- // Add the current element to the element stack
- self.element_stack.push(name.local_name.clone());
-
- match &name.local_name[..] {
- "plist" => (),
- "array" => return Some(Ok(PlistEvent::StartArray(None))),
- "dict" => return Some(Ok(PlistEvent::StartDictionary(None))),
- "key" => return Some(self.read_content(|s| Ok(PlistEvent::StringValue(s)))),
- "true" => return Some(Ok(PlistEvent::BooleanValue(true))),
- "false" => return Some(Ok(PlistEvent::BooleanValue(false))),
- "data" => {
- return Some(self.read_content(|s| {
- let data = base64::decode_config(&s, base64::MIME)
- .map_err(|_| Error::InvalidData)?;
- Ok(PlistEvent::DataValue(data))
- }))
- }
- "date" => {
- return Some(self.read_content(|s| {
- Ok(PlistEvent::DateValue(Date::from_rfc3339(&s)?))
- }))
- }
- "integer" => {
- return Some(self.read_content(|s| match FromStr::from_str(&s) {
- Ok(i) => Ok(PlistEvent::IntegerValue(i)),
- Err(_) => Err(Error::InvalidData),
- }))
- }
- "real" => {
- return Some(self.read_content(|s| match FromStr::from_str(&s) {
- Ok(f) => Ok(PlistEvent::RealValue(f)),
- Err(_) => Err(Error::InvalidData),
- }))
- }
- "string" => {
- return Some(self.read_content(|s| Ok(PlistEvent::StringValue(s))))
- }
- _ => return Some(Err(Error::InvalidData)),
- }
- }
- Ok(XmlEvent::EndElement { name, .. }) => {
- // Check the corrent element is being closed
- match self.element_stack.pop() {
- Some(ref open_name) if &name.local_name == open_name => (),
- Some(ref _open_name) => return Some(Err(Error::InvalidData)),
- None => return Some(Err(Error::InvalidData)),
- }
-
- match &name.local_name[..] {
- "array" => return Some(Ok(PlistEvent::EndArray)),
- "dict" => return Some(Ok(PlistEvent::EndDictionary)),
- "plist" => (),
- _ => (),
- }
- }
- Ok(XmlEvent::EndDocument) => {
- if self.element_stack.is_empty() {
- return None;
- } else {
- return Some(Err(Error::UnexpectedEof));
- }
- }
- Err(_) => return Some(Err(Error::InvalidData)),
- _ => (),
- }
- }
- }
-}
-
-impl<R: Read> Iterator for EventReader<R> {
- type Item = Result<PlistEvent>;
-
- fn next(&mut self) -> Option<Result<PlistEvent>> {
- if self.finished {
- None
- } else {
- match self.read_next() {
- Some(Ok(event)) => Some(Ok(event)),
- Some(Err(err)) => {
- self.finished = true;
- Some(Err(err))
- }
- None => {
- self.finished = true;
- None
- }
- }
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use humantime::parse_rfc3339_weak;
- use std::fs::File;
- use std::path::Path;
-
- use super::*;
- use PlistEvent;
-
- #[test]
- fn streaming_parser() {
- use PlistEvent::*;
-
- let reader = File::open(&Path::new("./tests/data/xml.plist")).unwrap();
- let streaming_parser = EventReader::new(reader);
- let events: Vec<PlistEvent> = streaming_parser.map(|e| e.unwrap()).collect();
-
- let comparison = &[
- StartDictionary(None),
- StringValue("Author".to_owned()),
- StringValue("William Shakespeare".to_owned()),
- StringValue("Lines".to_owned()),
- StartArray(None),
- StringValue("It is a tale told by an idiot,".to_owned()),
- StringValue("Full of sound and fury, signifying nothing.".to_owned()),
- EndArray,
- StringValue("Death".to_owned()),
- IntegerValue(1564),
- StringValue("Height".to_owned()),
- RealValue(1.60),
- StringValue("Data".to_owned()),
- DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
- StringValue("Birthdate".to_owned()),
- DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()),
- StringValue("Blank".to_owned()),
- StringValue("".to_owned()),
- EndDictionary,
- ];
-
- assert_eq!(events, comparison);
- }
-
- #[test]
- fn bad_data() {
- let reader = File::open(&Path::new("./tests/data/xml_error.plist")).unwrap();
- let streaming_parser = EventReader::new(reader);
- let events: Vec<_> = streaming_parser.collect();
-
- assert!(events.last().unwrap().is_err());
- }
-}
diff --git a/src/xml/writer.rs b/src/xml/writer.rs
deleted file mode 100644
index e002e55..0000000
--- a/src/xml/writer.rs
+++ /dev/null
@@ -1,259 +0,0 @@
-use base64;
-use std::borrow::Cow;
-use std::io::Write;
-use xml_rs::name::Name;
-use xml_rs::namespace::Namespace;
-use xml_rs::writer::events::XmlEvent as WriteXmlEvent;
-use xml_rs::writer::{EmitterConfig, Error as XmlWriterError, EventWriter as XmlEventWriter};
-
-use {Error, EventWriter as PlistEventWriter, PlistEvent, Result};
-
-impl From<XmlWriterError> for Error {
- fn from(err: XmlWriterError) -> Error {
- match err {
- XmlWriterError::Io(err) => Error::Io(err),
- _ => Error::InvalidData,
- }
- }
-}
-
-enum Element {
- Dictionary(DictionaryState),
- Array,
- Root,
-}
-
-enum DictionaryState {
- ExpectKey,
- ExpectValue,
-}
-
-pub struct EventWriter<W: Write> {
- xml_writer: XmlEventWriter<W>,
- stack: Vec<Element>,
- // Not very nice
- empty_namespace: Namespace,
-}
-
-impl<W: Write> EventWriter<W> {
- pub fn new(writer: W) -> EventWriter<W> {
- let config = EmitterConfig::new()
- .line_separator("\n")
- .indent_string("\t")
- .perform_indent(true)
- .write_document_declaration(false)
- .normalize_empty_elements(true)
- .cdata_to_characters(true)
- .keep_element_names_stack(false)
- .autopad_comments(true);
-
- EventWriter {
- xml_writer: XmlEventWriter::new_with_config(writer, config),
- stack: Vec::new(),
- empty_namespace: Namespace::empty(),
- }
- }
-
- fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<()> {
- self.start_element(name)?;
- self.write_value(value)?;
- self.end_element(name)?;
- Ok(())
- }
-
- fn start_element(&mut self, name: &str) -> Result<()> {
- self.xml_writer.write(WriteXmlEvent::StartElement {
- name: Name::local(name),
- attributes: Cow::Borrowed(&[]),
- namespace: Cow::Borrowed(&self.empty_namespace),
- })?;
- Ok(())
- }
-
- fn end_element(&mut self, name: &str) -> Result<()> {
- self.xml_writer.write(WriteXmlEvent::EndElement {
- name: Some(Name::local(name)),
- })?;
- Ok(())
- }
-
- fn write_value(&mut self, value: &str) -> Result<()> {
- self.xml_writer.write(WriteXmlEvent::Characters(value))?;
- Ok(())
- }
-
- fn maybe_end_plist(&mut self) -> Result<()> {
- // If there are no more open tags then write the </plist> element
- if self.stack.len() == 1 {
- // We didn't tell the xml_writer about the <plist> tag so we'll skip telling it
- // about the </plist> tag as well.
- self.xml_writer.inner_mut().write(b"\n</plist>")?;
- if let Some(Element::Root) = self.stack.pop() {
- } else {
- return Err(Error::InvalidData);
- }
- }
- Ok(())
- }
-
- pub fn write(&mut self, event: &PlistEvent) -> Result<()> {
- <Self as PlistEventWriter>::write(self, event)
- }
-}
-
-impl<W: Write> PlistEventWriter for EventWriter<W> {
- fn write(&mut self, event: &PlistEvent) -> Result<()> {
- match self.stack.pop() {
- Some(Element::Dictionary(DictionaryState::ExpectKey)) => {
- match *event {
- PlistEvent::StringValue(ref value) => {
- self.write_element_and_value("key", &*value)?;
- self.stack
- .push(Element::Dictionary(DictionaryState::ExpectValue));
- }
- PlistEvent::EndDictionary => {
- self.end_element("dict")?;
- // We might be closing the last tag here as well
- self.maybe_end_plist()?;
- }
- _ => return Err(Error::InvalidData),
- };
- return Ok(());
- }
- Some(Element::Dictionary(DictionaryState::ExpectValue)) => self.stack
- .push(Element::Dictionary(DictionaryState::ExpectKey)),
- Some(other) => self.stack.push(other),
- None => {
- // Write prologue
- let prologue = r#"<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-"#;
- self.xml_writer.inner_mut().write(prologue.as_bytes())?;
-
- self.stack.push(Element::Root);
- }
- }
-
- match *event {
- PlistEvent::StartArray(_) => {
- self.start_element("array")?;
- self.stack.push(Element::Array);
- }
- PlistEvent::EndArray => {
- self.end_element("array")?;
- if let Some(Element::Array) = self.stack.pop() {
- } else {
- return Err(Error::InvalidData);
- }
- }
-
- PlistEvent::StartDictionary(_) => {
- self.start_element("dict")?;
- self.stack
- .push(Element::Dictionary(DictionaryState::ExpectKey));
- }
- PlistEvent::EndDictionary => return Err(Error::InvalidData),
-
- PlistEvent::BooleanValue(true) => {
- self.start_element("true")?;
- self.end_element("true")?;
- }
- PlistEvent::BooleanValue(false) => {
- self.start_element("false")?;
- self.end_element("false")?;
- }
- PlistEvent::DataValue(ref value) => {
- let base64_data = base64::encode_config(&value, base64::MIME);
- self.write_element_and_value("data", &base64_data)?;
- }
- PlistEvent::DateValue(ref value) => {
- self.write_element_and_value("date", &value.to_rfc3339())?
- }
- PlistEvent::IntegerValue(ref value) => {
- self.write_element_and_value("integer", &value.to_string())?
- }
- PlistEvent::RealValue(ref value) => {
- self.write_element_and_value("real", &value.to_string())?
- }
- PlistEvent::StringValue(ref value) => self.write_element_and_value("string", &*value)?,
- };
-
- self.maybe_end_plist()?;
-
- Ok(())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use humantime::parse_rfc3339_weak;
- use std::io::Cursor;
-
- use super::*;
-
- #[test]
- fn streaming_parser() {
- use PlistEvent::*;
-
- let plist = &[
- StartDictionary(None),
- StringValue("Author".to_owned()),
- StringValue("William Shakespeare".to_owned()),
- StringValue("Lines".to_owned()),
- StartArray(None),
- StringValue("It is a tale told by an idiot,".to_owned()),
- StringValue("Full of sound and fury, signifying nothing.".to_owned()),
- EndArray,
- StringValue("Death".to_owned()),
- IntegerValue(1564),
- StringValue("Height".to_owned()),
- RealValue(1.60),
- StringValue("Data".to_owned()),
- DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
- StringValue("Birthdate".to_owned()),
- DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()),
- StringValue("Comment".to_owned()),
- StringValue("2 < 3".to_owned()), // make sure characters are escaped
- EndDictionary,
- ];
-
- let mut cursor = Cursor::new(Vec::new());
-
- {
- let mut plist_w = EventWriter::new(&mut cursor);
-
- for item in plist {
- plist_w.write(item).unwrap();
- }
- }
-
- let comparison = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
-<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
-<plist version=\"1.0\">
-<dict>
-\t<key>Author</key>
-\t<string>William Shakespeare</string>
-\t<key>Lines</key>
-\t<array>
-\t\t<string>It is a tale told by an idiot,</string>
-\t\t<string>Full of sound and fury, signifying nothing.</string>
-\t</array>
-\t<key>Death</key>
-\t<integer>1564</integer>
-\t<key>Height</key>
-\t<real>1.6</real>
-\t<key>Data</key>
-\t<data>AAAAvgAAAAMAAAAeAAAA</data>
-\t<key>Birthdate</key>
-\t<date>1981-05-16T11:32:06Z</date>
-\t<key>Comment</key>
-\t<string>2 &lt; 3</string>
-</dict>
-</plist>";
-
- let s = String::from_utf8(cursor.into_inner()).unwrap();
-
- assert_eq!(s, comparison);
- }
-}