diff options
| author | Edward Barnard | 2015-08-28 21:17:14 +0700 |
|---|---|---|
| committer | Edward Barnard | 2015-08-28 21:17:14 +0700 |
| commit | 3e41485288049b1d7a9be60b84b4d78dcd400f39 (patch) | |
| tree | 66692c381e6e9f4337868ac8ca43c06dcb909e75 /src/builder.rs | |
| parent | 1ef9fbdc9377a831eb4abccaa4b18d89b5fd560d (diff) | |
| download | rust-plist-3e41485288049b1d7a9be60b84b4d78dcd400f39.tar.bz2 | |
Expand folder structure
Diffstat (limited to 'src/builder.rs')
| -rw-r--r-- | src/builder.rs | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src/builder.rs b/src/builder.rs new file mode 100644 index 0000000..50afad7 --- /dev/null +++ b/src/builder.rs @@ -0,0 +1,176 @@ +use std::collections::HashMap; +use std::io::{Read, Seek}; + +use {ParserError, ParserResult, Plist, PlistEvent, StreamingParser}; + +pub type BuilderResult<T> = Result<T, BuilderError>; + +#[derive(Debug)] +pub enum BuilderError { + InvalidEvent, + UnsupportedDictionaryKey, + ParserError(ParserError) +} + +impl From<ParserError> for BuilderError { + fn from(err: ParserError) -> BuilderError { + BuilderError::ParserError(err) + } +} + +pub struct Builder<T> { + stream: T, + token: Option<PlistEvent>, +} + +impl<R: Read + Seek> Builder<StreamingParser<R>> { + pub fn new(reader: R) -> Builder<StreamingParser<R>> { + Builder::from_event_stream(StreamingParser::new(reader)) + } +} + +impl<T:Iterator<Item=ParserResult<PlistEvent>>> Builder<T> { + pub fn from_event_stream(stream: T) -> Builder<T> { + Builder { + stream: stream, + token: None + } + } + + pub fn build(mut self) -> BuilderResult<Plist> { + try!(self.bump()); + if let Some(PlistEvent::StartPlist) = self.token { + try!(self.bump()); + } + + let plist = try!(self.build_value()); + try!(self.bump()); + match self.token { + None => (), + Some(PlistEvent::EndPlist) => try!(self.bump()), + // The stream should have finished + _ => return Err(BuilderError::InvalidEvent) + }; + Ok(plist) + } + + fn bump(&mut self) -> BuilderResult<()> { + self.token = match self.stream.next() { + Some(Ok(token)) => Some(token), + Some(Err(err)) => return Err(BuilderError::ParserError(err)), + None => None, + }; + Ok(()) + } + + fn build_value(&mut self) -> BuilderResult<Plist> { + match self.token.take() { + Some(PlistEvent::StartPlist) => Err(BuilderError::InvalidEvent), + Some(PlistEvent::EndPlist) => Err(BuilderError::InvalidEvent), + + Some(PlistEvent::StartArray(len)) => Ok(Plist::Array(try!(self.build_array(len)))), + Some(PlistEvent::StartDictionary(len)) => Ok(Plist::Dictionary(try!(self.build_dict(len)))), + + 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(BuilderError::InvalidEvent), + Some(PlistEvent::EndDictionary) => Err(BuilderError::InvalidEvent), + + // The stream should not have ended here + None => Err(BuilderError::InvalidEvent) + } + } + + fn build_array(&mut self, len: Option<u64>) -> Result<Vec<Plist>, BuilderError> { + let mut values = match len { + Some(len) => Vec::with_capacity(len as usize), + None => Vec::new() + }; + + loop { + try!(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, len: Option<u64>) -> Result<HashMap<String, Plist>, BuilderError> { + let mut values = match len { + Some(len) => HashMap::with_capacity(len as usize), + None => HashMap::new() + }; + + loop { + try!(self.bump()); + match self.token.take() { + Some(PlistEvent::EndDictionary) => return Ok(values), + Some(PlistEvent::StringValue(s)) => { + try!(self.bump()); + values.insert(s, try!(self.build_value())); + }, + _ => { + // Only string keys are supported in plists + return Err(BuilderError::UnsupportedDictionaryKey) + } + } + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use super::*; + use Plist; + + #[test] + fn builder() { + use PlistEvent::*; + + // Input + + let events = vec![ + StartPlist, + 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("Birthdate".to_owned()), + IntegerValue(1564), + StringValue("Height".to_owned()), + RealValue(1.60), + EndDictionary, + EndPlist, + ]; + + let builder = Builder::from_event_stream(events.into_iter().map(|e| Ok(e))); + let plist = builder.build(); + + // Expected output + + 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.unwrap(), Plist::Dictionary(dict)); + } +}
\ No newline at end of file |
