From 094994360a7eb81eb545fccfe73e80765be71fd3 Mon Sep 17 00:00:00 2001 From: Edward Barnard Date: Tue, 4 Aug 2015 17:24:37 +0100 Subject: More parsing --- src/reader.rs | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 121 insertions(+), 15 deletions(-) (limited to 'src/reader.rs') diff --git a/src/reader.rs b/src/reader.rs index 64e666e..6055b74 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,11 +1,13 @@ use rustc_serialize::base64::FromBase64; +use std::collections::HashMap; use std::io::Read; use std::str::FromStr; -use xml::reader::ParserConfig; -use xml::reader::EventReader as XmlEventReader; +use xml::reader::{EventReader, ParserConfig}; use xml::reader::events::XmlEvent; -#[derive(PartialEq, Debug, Clone)] +use super::Plist; + +#[derive(Clone, Debug, PartialEq)] pub enum PlistEvent { StartArray, EndArray, @@ -17,19 +19,19 @@ pub enum PlistEvent { BooleanValue(bool), DataValue(Vec), DateValue(String), - FloatValue(f64), IntegerValue(i64), + RealValue(f64), StringValue(String), Error(()) } -pub struct EventReader { - xml_reader: XmlEventReader +pub struct StreamingParser { + xml_reader: EventReader } -impl EventReader { - pub fn new(reader: R) -> EventReader { +impl StreamingParser { + pub fn new(reader: R) -> StreamingParser { let config = ParserConfig { trim_whitespace: false, whitespace_to_characters: true, @@ -38,8 +40,8 @@ impl EventReader { coalesce_characters: true, }; - EventReader { - xml_reader: XmlEventReader::with_config(reader, config) + StreamingParser { + xml_reader: EventReader::with_config(reader, config) } } @@ -51,7 +53,7 @@ impl EventReader { } } -impl Iterator for EventReader { +impl Iterator for StreamingParser { type Item = PlistEvent; fn next(&mut self) -> Option { @@ -80,7 +82,7 @@ impl Iterator for EventReader { })), "real" => return Some(self.read_string(|s| { match FromStr::from_str(&s) { - Ok(f) => PlistEvent::FloatValue(f), + Ok(f) => PlistEvent::RealValue(f), Err(_) => PlistEvent::Error(()) } })), @@ -99,20 +101,103 @@ impl Iterator for EventReader { } } +pub struct Parser { + reader: StreamingParser, + token: Option, +} + +impl Parser { + pub fn new(reader: R) -> Parser { + Parser { + reader: StreamingParser::new(reader), + token: None + } + } + + pub fn parse(mut self) -> Result { + self.bump(); + let plist = try!(self.build_value()); + self.bump(); + match self.token { + None => (), + _ => return Err(()) + }; + Ok(plist) + } + + fn bump(&mut self) { + self.token = self.reader.next(); + } + + fn build_value(&mut self) -> Result { + match self.token.take() { + Some(PlistEvent::StartArray) => Ok(Plist::Array(try!(self.build_array()))), + Some(PlistEvent::StartDictionary) => Ok(Plist::Dictionary(try!(self.build_dict()))), + + Some(PlistEvent::BooleanValue(b)) => Ok(Plist::Boolean(b)), + Some(PlistEvent::DataValue(d)) => Ok(Plist::Data(d)), + Some(PlistEvent::DateValue(d)) => Ok(Plist::Date(d)), + Some(PlistEvent::IntegerValue(i)) => Ok(Plist::Integer(i)), + Some(PlistEvent::RealValue(f)) => Ok(Plist::Real(f)), + Some(PlistEvent::StringValue(s)) => Ok(Plist::String(s)), + + Some(PlistEvent::EndArray) => Err(()), + Some(PlistEvent::EndDictionary) => Err(()), + Some(PlistEvent::DictionaryKey(_)) => Err(()), + Some(PlistEvent::Error(_)) => Err(()), + None => Err(()) + } + } + + fn build_array(&mut self) -> Result, ()> { + let mut values = Vec::new(); + + loop { + self.bump(); + if let Some(PlistEvent::EndArray) = self.token { + self.token.take(); + return Ok(values); + } + values.push(try!(self.build_value())); + } + } + + fn build_dict(&mut self) -> Result, ()> { + let mut values = HashMap::new(); + + loop { + + self.bump(); + match self.token.take() { + Some(PlistEvent::EndDictionary) => return Ok(values), + Some(PlistEvent::DictionaryKey(s)) => { + self.bump(); + values.insert(s, try!(self.build_value())); + }, + _ => { + return Err(()) + } + } + } + } +} + #[cfg(test)] mod tests { use std::fs::File; + use std::collections::HashMap; use std::path::Path; use reader::*; + use super::super::Plist; #[test] - fn simple() { + fn streaming_parser() { use reader::PlistEvent::*; let reader = File::open(&Path::new("./tests/data/simple.plist")).unwrap(); - let event_reader = EventReader::new(reader); - let events: Vec = event_reader.collect(); + let streaming_parser = StreamingParser::new(reader); + let events: Vec = streaming_parser.collect(); let comparison = &[ StartDictionary, @@ -125,9 +210,30 @@ mod tests { EndArray, DictionaryKey("Birthdate".to_owned()), IntegerValue(1564), + DictionaryKey("Height".to_owned()), + RealValue(1.60), EndDictionary ]; assert_eq!(events, comparison); } + + #[test] + fn parser() { + let reader = File::open(&Path::new("./tests/data/simple.plist")).unwrap(); + let parser = Parser::new(reader); + let plist = parser.parse(); + + let mut lines = Vec::new(); + lines.push(Plist::String("It is a tale told by an idiot,".to_owned())); + lines.push(Plist::String("Full of sound and fury, signifying nothing.".to_owned())); + + let mut dict = HashMap::new(); + dict.insert("Author".to_owned(), Plist::String("William Shakespeare".to_owned())); + dict.insert("Lines".to_owned(), Plist::Array(lines)); + dict.insert("Birthdate".to_owned(), Plist::Integer(1564)); + dict.insert("Height".to_owned(), Plist::Real(1.60)); + + assert_eq!(plist, Ok(Plist::Dictionary(dict))); + } } \ No newline at end of file -- cgit v1.2.3