//! # Plist //! //! A rusty plist parser. //! //! ## Usage //! //! Put this in your `Cargo.toml`: //! //! ```toml //! [dependencies] //! plist = "0.1" //! ``` //! //! And put this in your crate root: //! //! ```rust //! extern crate plist; //! ``` //! //! ## Examples //! //! ```rust //! 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) => (), //! _ => () //! } //! ``` //! //! ```rust,ignore //! #[macro_use] //! extern crate serde_derive; //! //! use plist::serde::deserialize; //! use std::fs::File; //! //! #[derive(Deserialize)] //! struct Info { //! name: String, //! x: i32 //! } //! //! let file = File::open("tests/data/xml.plist").unwrap(); //! let info: Info = deserialize(file).unwrap(); //! ``` extern crate byteorder; extern crate chrono; extern crate rustc_serialize; extern crate xml as xml_rs; pub mod binary; pub mod xml; mod builder; mod plist; pub use plist::Plist; // Optional serde module #[cfg(feature = "serde")] #[macro_use] extern crate serde as serde_base; #[cfg(feature = "serde")] pub mod serde; use chrono::{DateTime, UTC}; use std::fmt; use std::io::{Read, Seek, SeekFrom}; use std::io::Error as IoError; /// 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), Serde(String), } 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(), Error::Serde(ref err) => &err, } } fn cause(&self) -> Option<&::std::error::Error> { match *self { Error::Io(ref err) => Some(err), _ => None, } } } 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) } } 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), } }