diff options
| author | Edward Barnard | 2018-05-17 20:30:41 +0100 |
|---|---|---|
| committer | Edward Barnard | 2018-05-17 20:50:21 +0100 |
| commit | 2af279c344a6a917ce4102fec82e6cba8cf8b37c (patch) | |
| tree | a2d3746d4981e8e22e30b91fd3b4d1821444777a /src/xml | |
| parent | d8a4f4ef2a0e80f1dc6237678410807cce83cce0 (diff) | |
| download | rust-plist-2af279c344a6a917ce4102fec82e6cba8cf8b37c.tar.bz2 | |
Reorganise crate.
Diffstat (limited to 'src/xml')
| -rw-r--r-- | src/xml/mod.rs | 5 | ||||
| -rw-r--r-- | src/xml/reader.rs | 199 | ||||
| -rw-r--r-- | src/xml/writer.rs | 259 |
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 < 3</string> -</dict> -</plist>"; - - let s = String::from_utf8(cursor.into_inner()).unwrap(); - - assert_eq!(s, comparison); - } -} |
