use std::collections::BTreeMap; use {Error, Result, Plist, PlistEvent, u64_option_to_usize}; pub struct Builder { stream: T, token: Option, } impl>> Builder { pub fn new(stream: T) -> Builder { Builder { stream: stream, token: None, } } pub fn build(mut self) -> Result { try!(self.bump()); let plist = try!(self.build_value()); try!(self.bump()); match self.token { None => (), // The stream should have finished _ => return Err(Error::InvalidData), }; Ok(plist) } fn bump(&mut self) -> Result<()> { self.token = match self.stream.next() { Some(Ok(token)) => Some(token), Some(Err(err)) => return Err(err), None => None, }; Ok(()) } fn build_value(&mut self) -> Result { match self.token.take() { 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(Error::InvalidData), Some(PlistEvent::EndDictionary) => Err(Error::InvalidData), // The stream should not have ended here None => Err(Error::InvalidData), } } fn build_array(&mut self, len: Option) -> Result> { let len = try!(u64_option_to_usize(len)); let mut values = match len { Some(len) => Vec::with_capacity(len), 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) -> Result> { let mut values = BTreeMap::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(Error::InvalidData); } } } } } #[cfg(test)] mod tests { use std::collections::BTreeMap; use super::*; use Plist; #[test] fn builder() { use PlistEvent::*; // Input let events = vec![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]; let builder = Builder::new(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 = BTreeMap::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)); } }