diff options
| author | Edward Barnard | 2015-08-04 17:24:37 +0100 |
|---|---|---|
| committer | Edward Barnard | 2015-08-04 17:24:37 +0100 |
| commit | 094994360a7eb81eb545fccfe73e80765be71fd3 (patch) | |
| tree | f37e86ce5e475a87598ba65ed27af6b6e55f5f77 | |
| parent | da761e65e2f7b74afd2bdf1e07b19e038d990df6 (diff) | |
| download | rust-plist-094994360a7eb81eb545fccfe73e80765be71fd3.tar.bz2 | |
More parsing
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/lib.rs | 19 | ||||
| -rw-r--r-- | src/reader.rs | 136 | ||||
| -rw-r--r-- | tests/data/simple.plist | 2 |
4 files changed, 143 insertions, 17 deletions
@@ -1,9 +1,10 @@ [package] name = "plist" -version = "0.0.1" +version = "0.0.2" authors = ["Ed Barnard <eabarnard@gmail.com>"] description = "A rusty plist parser. Very much a work in progress." license = "MIT" +repository = "https://github.com/ebarnard/rust-plist" [dependencies] rustc-serialize = "0.3.15" @@ -1,4 +1,21 @@ extern crate rustc_serialize; extern crate xml; -pub mod reader;
\ No newline at end of file +mod reader; + +pub use reader::{Parser, StreamingParser, PlistEvent}; + +use std::collections::HashMap; + +#[derive(Clone, Debug, PartialEq)] +pub enum Plist { + Array(Vec<Plist>), + Dictionary(HashMap<String, Plist>), + Boolean(bool), + Data(Vec<u8>), + Date(String), + Real(f64), + Integer(i64), + String(String) +} + 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<u8>), DateValue(String), - FloatValue(f64), IntegerValue(i64), + RealValue(f64), StringValue(String), Error(()) } -pub struct EventReader<R: Read> { - xml_reader: XmlEventReader<R> +pub struct StreamingParser<R: Read> { + xml_reader: EventReader<R> } -impl<R: Read> EventReader<R> { - pub fn new(reader: R) -> EventReader<R> { +impl<R: Read> StreamingParser<R> { + pub fn new(reader: R) -> StreamingParser<R> { let config = ParserConfig { trim_whitespace: false, whitespace_to_characters: true, @@ -38,8 +40,8 @@ impl<R: Read> EventReader<R> { coalesce_characters: true, }; - EventReader { - xml_reader: XmlEventReader::with_config(reader, config) + StreamingParser { + xml_reader: EventReader::with_config(reader, config) } } @@ -51,7 +53,7 @@ impl<R: Read> EventReader<R> { } } -impl<R: Read> Iterator for EventReader<R> { +impl<R: Read> Iterator for StreamingParser<R> { type Item = PlistEvent; fn next(&mut self) -> Option<PlistEvent> { @@ -80,7 +82,7 @@ impl<R: Read> Iterator for EventReader<R> { })), "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<R: Read> Iterator for EventReader<R> { } } +pub struct Parser<R: Read> { + reader: StreamingParser<R>, + token: Option<PlistEvent>, +} + +impl<R: Read> Parser<R> { + pub fn new(reader: R) -> Parser<R> { + Parser { + reader: StreamingParser::new(reader), + token: None + } + } + + pub fn parse(mut self) -> Result<Plist, ()> { + 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<Plist, ()> { + 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<Vec<Plist>, ()> { + 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<HashMap<String, Plist>, ()> { + 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<PlistEvent> = event_reader.collect(); + let streaming_parser = StreamingParser::new(reader); + let events: Vec<PlistEvent> = 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 diff --git a/tests/data/simple.plist b/tests/data/simple.plist index c48e224..7fb0208 100644 --- a/tests/data/simple.plist +++ b/tests/data/simple.plist @@ -11,5 +11,7 @@ </array> <key>Birthdate</key> <integer>1564</integer> + <key>Height</key> + <real>1.60</real> </dict> </plist>
\ No newline at end of file |
