//! # Plist //! //! A rusty plist parser. //! //! ## Usage //! //! Put this in your `Cargo.toml`: //! //! ```toml //! [dependencies] //! plist = "0.0.11" //! ``` //! //! And put this in your crate root: //! //! ```rust //! extern crate plist; //! ``` //! //! ## Examples //! //! ```rust ignore //! use plist::Plist; //! use std::fs::File; //! //! let file = File::open("tests/data/xml.plist").unwrap(); //! let plist = Plist::read(file).unwrap(); //! //! match plist { //! Plist::Array(_array) => (), //! _ => () //! } //! //! ``` //! //! extern crate byteorder; extern crate chrono; extern crate rustc_serialize; extern crate xml as xml_rs; pub mod binary; pub mod xml; mod builder; use chrono::{DateTime, UTC}; use chrono::format::ParseError as ChronoParseError; use std::collections::BTreeMap; use std::io::{Read, Seek, SeekFrom}; use std::io::Error as IoError; #[derive(Clone, Debug, PartialEq)] pub enum Plist { Array(Vec), Dictionary(BTreeMap), Boolean(bool), Data(Vec), Date(DateTime), Real(f64), Integer(i64), String(String), } use rustc_serialize::base64::{STANDARD, ToBase64}; use rustc_serialize::json::Json as RustcJson; impl Plist { pub fn read(reader: R) -> Result { let reader = EventReader::new(reader); Plist::from_events(reader) } pub fn from_events(events: T) -> Result where T: IntoIterator> { let iter = events.into_iter(); let builder = builder::Builder::new(iter); match builder.build() { Ok(plist) => Ok(plist), Err(_) => Err(()), } } pub fn into_events(self) -> Vec { let mut events = Vec::new(); self.into_events_inner(&mut events); events } fn into_events_inner(self, events: &mut Vec) { match self { Plist::Array(array) => { events.push(PlistEvent::StartArray(Some(array.len() as u64))); for value in array.into_iter() { value.into_events_inner(events); } events.push(PlistEvent::EndArray); } Plist::Dictionary(dict) => { events.push(PlistEvent::StartDictionary(Some(dict.len() as u64))); for (key, value) in dict.into_iter() { events.push(PlistEvent::StringValue(key)); value.into_events_inner(events); } events.push(PlistEvent::EndDictionary); } Plist::Boolean(value) => events.push(PlistEvent::BooleanValue(value)), Plist::Data(value) => events.push(PlistEvent::DataValue(value)), Plist::Date(value) => events.push(PlistEvent::DateValue(value)), Plist::Real(value) => events.push(PlistEvent::RealValue(value)), Plist::Integer(value) => events.push(PlistEvent::IntegerValue(value)), Plist::String(value) => events.push(PlistEvent::StringValue(value)), } } pub fn into_rustc_serialize_json(self) -> RustcJson { match self { Plist::Array(value) => { RustcJson::Array(value.into_iter().map(|p| p.into_rustc_serialize_json()).collect()) } Plist::Dictionary(value) => { RustcJson::Object(value.into_iter() .map(|(k, v)| (k, v.into_rustc_serialize_json())) .collect()) } Plist::Boolean(value) => RustcJson::Boolean(value), Plist::Data(value) => RustcJson::String(value.to_base64(STANDARD)), Plist::Date(value) => RustcJson::String(value.to_rfc3339()), Plist::Real(value) => RustcJson::F64(value), Plist::Integer(value) => RustcJson::I64(value), Plist::String(value) => RustcJson::String(value), } } } /// An encoding of a plist as a flat structure. /// /// Output by the event readers. /// /// Dictionary keys and values are represented as pairs of values e.g.: /// /// ```ignore rust /// StartDictionary /// StringValue("Height") // Key /// RealValue(181.2) // Value /// StringValue("Age") // Key /// IntegerValue(28) // Value /// EndDictionary /// ``` #[derive(Clone, Debug, PartialEq)] pub enum PlistEvent { StartPlist, EndPlist, StartArray(Option), EndArray, StartDictionary(Option), EndDictionary, BooleanValue(bool), DataValue(Vec), DateValue(DateTime), IntegerValue(i64), RealValue(f64), StringValue(String), } pub type ReadResult = Result; #[derive(Debug)] pub enum ReadError { InvalidData, UnexpectedEof, UnsupportedType, Io(IoError), } impl From for ReadError { fn from(io_error: IoError) -> ReadError { ReadError::Io(io_error) } } impl From for ReadError { fn from(_: ChronoParseError) -> ReadError { ReadError::InvalidData } } pub struct EventReader(EventReaderInner); enum EventReaderInner { Uninitialized(Option), Xml(xml::EventReader), Binary(binary::EventReader), } impl EventReader { pub fn new(reader: R) -> EventReader { EventReader(EventReaderInner::Uninitialized(Some(reader))) } fn is_binary(reader: &mut R) -> Result { try!(reader.seek(SeekFrom::Start(0))); let mut magic = [0; 8]; try!(reader.read(&mut magic)); try!(reader.seek(SeekFrom::Start(0))); Ok(if &magic == b"bplist00" { true } else { false }) } } impl Iterator for EventReader { type Item = ReadResult; fn next(&mut self) -> Option> { let mut reader = match self.0 { EventReaderInner::Xml(ref mut parser) => return parser.next(), EventReaderInner::Binary(ref mut parser) => return parser.next(), EventReaderInner::Uninitialized(ref mut reader) => reader.take().unwrap(), }; let event_reader = match EventReader::is_binary(&mut reader) { Ok(true) => EventReaderInner::Binary(binary::EventReader::new(reader)), Ok(false) => EventReaderInner::Xml(xml::EventReader::new(reader)), Err(err) => { ::std::mem::replace(&mut self.0, EventReaderInner::Uninitialized(Some(reader))); return Some(Err(ReadError::Io(err))); } }; ::std::mem::replace(&mut self.0, event_reader); self.next() } }