//! # Plist //! //! A rusty plist parser. //! //! ## Usage //! //! Put this in your `Cargo.toml`: //! //! ```toml //! [dependencies] //! plist = "0.0.12" //! ``` //! //! 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 serde; extern crate xml as xml_rs; pub mod binary; pub mod xml; mod builder; mod de; mod ser; pub use de::{Deserializer, DeserializeError}; pub use ser::Serializer; use chrono::{DateTime, UTC}; use chrono::format::ParseError as ChronoParseError; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::io::{Read, Seek, SeekFrom, Write}; use std::io::Error as IoError; pub fn deserialize (reader: R) -> ::std::result::Result { let reader = EventReader::new(reader); let mut de = Deserializer::new(reader); Deserialize::deserialize(&mut de) } pub fn serialize_to_xml(writer: W, value: &T) -> Result<()> { let writer = xml::EventWriter::new(writer); let mut ser = Serializer::new(writer); value.serialize(&mut ser) } #[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); builder.build() } 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 { // While the length of an array or dict cannot be feasably greater than max(usize) this better // conveys the concept of an effectively unbounded event stream. StartArray(Option), EndArray, StartDictionary(Option), EndDictionary, BooleanValue(bool), DataValue(Vec), DateValue(DateTime), IntegerValue(i64), RealValue(f64), StringValue(String), } pub type Result = ::std::result::Result; #[derive(Debug)] pub enum Error { InvalidData, UnexpectedEof, Io(IoError), } impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { Error::InvalidData => "invalid data", Error::UnexpectedEof => "unexpected eof", Error::Io(ref err) => err.description(), } } fn cause(&self) -> Option<&::std::error::Error> { match *self { Error::Io(ref err) => Some(err), _ => None, } } } use std::fmt; impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Error::Io(ref err) => err.fmt(fmt), _ => ::description(self).fmt(fmt), } } } impl From for Error { fn from(err: IoError) -> Error { Error::Io(err) } } impl From for Error { fn from(_: ChronoParseError) -> Error { Error::InvalidData } } use xml_rs::writer::Error as XmlWriterError; impl From for Error { fn from(err: XmlWriterError) -> Error { match err { XmlWriterError::Io(err) => Error::Io(err), _ => Error::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 = Result; 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(err)); } }; ::std::mem::replace(&mut self.0, event_reader); self.next() } } pub trait EventWriter { fn write(&mut self, event: &PlistEvent) -> Result<()>; } fn u64_to_usize(len_u64: u64) -> Result { let len = len_u64 as usize; if len as u64 != len_u64 { return Err(Error::InvalidData); // Too long } Ok(len) } fn u64_option_to_usize(len: Option) -> Result> { match len { Some(len) => Ok(Some(try!(u64_to_usize(len)))), None => Ok(None), } }