plist-0.4.2/.editorconfig010064400007650000024000000004311263624407700135550ustar0000000000000000# EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 plist-0.4.2/.gitignore010064400007650000024000000000221260060366100130510ustar0000000000000000target Cargo.lock plist-0.4.2/.travis.yml010064400007650000024000000002521342766660200132130ustar0000000000000000os: - linux - mac - windows sudo: false language: rust rust: - nightly - beta - stable - 1.26.0 script: - cargo test - cargo test --no-default-features plist-0.4.2/Cargo.toml.orig010064400007650000024000000011561347751654600140030ustar0000000000000000[package] name = "plist" version = "0.4.2" authors = ["Ed Barnard "] description = "A rusty plist parser. Supports Serde serialization." license = "MIT" repository = "https://github.com/ebarnard/rust-plist/" documentation = "https://docs.rs/plist/0.4.2/plist/" keywords = ["plist", "parser"] categories = ["config", "encoding", "parser-implementations"] [features] default = ["serde"] [dependencies] base64 = "0.10.1" byteorder = "1.1.0" humantime = "1.1.1" line-wrap = "0.1.1" xml-rs = "0.8.0" serde = { version = "1.0.2", optional = true } [dev-dependencies] serde_derive = { version = "1.0.2" } plist-0.4.2/Cargo.toml0000644000000023430000000000000102310ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g. crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "plist" version = "0.4.2" authors = ["Ed Barnard "] description = "A rusty plist parser. Supports Serde serialization." documentation = "https://docs.rs/plist/0.4.2/plist/" keywords = ["plist", "parser"] categories = ["config", "encoding", "parser-implementations"] license = "MIT" repository = "https://github.com/ebarnard/rust-plist/" [dependencies.base64] version = "0.10.1" [dependencies.byteorder] version = "1.1.0" [dependencies.humantime] version = "1.1.1" [dependencies.line-wrap] version = "0.1.1" [dependencies.serde] version = "1.0.2" optional = true [dependencies.xml-rs] version = "0.8.0" [dev-dependencies.serde_derive] version = "1.0.2" [features] default = ["serde"] plist-0.4.2/Cargo.toml.orig0000644000000023440000000000000111710ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "plist" version = "0.4.2" authors = ["Ed Barnard "] description = "A rusty plist parser. Supports Serde serialization." documentation = "https://docs.rs/plist/0.4.2/plist/" keywords = ["plist", "parser"] categories = ["config", "encoding", "parser-implementations"] license = "MIT" repository = "https://github.com/ebarnard/rust-plist/" [dependencies.base64] version = "0.10.1" [dependencies.byteorder] version = "1.1.0" [dependencies.humantime] version = "1.1.1" [dependencies.line-wrap] version = "0.1.1" [dependencies.serde] version = "1.0.2" optional = true [dependencies.xml-rs] version = "0.8.0" [dev-dependencies.serde_derive] version = "1.0.2" [features] default = ["serde"] plist-0.4.2/LICENCE010064400007650000024000000020411260060366100120510ustar0000000000000000Copyright (c) 2015 Edward Barnard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.plist-0.4.2/README.md010064400007650000024000000003031305334745600123540ustar0000000000000000# Plist A rusty plist parser. [![Build Status](https://travis-ci.org/ebarnard/rust-plist.svg?branch=master)](https://travis-ci.org/ebarnard/rust-plist) [Documentation](https://docs.rs/plist/) plist-0.4.2/src/date.rs010064400007650000024000000072271347751654600131730ustar0000000000000000use humantime; use std::fmt; use std::time::{Duration, SystemTime, UNIX_EPOCH}; /// A UTC timestamp. Used for serialization to and from the plist date type. #[derive(Clone, Copy, Hash, PartialEq)] pub struct Date { inner: SystemTime, } impl Date { pub(crate) fn from_rfc3339(date: &str) -> Result { Ok(Date { inner: humantime::parse_rfc3339(date).map_err(|_| ())?, }) } pub(crate) fn to_rfc3339(&self) -> String { format!("{}", humantime::format_rfc3339(self.inner)) } pub(crate) fn from_seconds_since_plist_epoch(timestamp: f64) -> Result { // `timestamp` is the number of seconds since the plist epoch of 1/1/2001 00:00:00. // `PLIST_EPOCH_UNIX_TIMESTAMP` is the unix timestamp of the plist epoch. const PLIST_EPOCH_UNIX_TIMESTAMP: u64 = 978_307_200; let plist_epoch = UNIX_EPOCH + Duration::from_secs(PLIST_EPOCH_UNIX_TIMESTAMP); if !timestamp.is_finite() { return Err(()); } let is_negative = timestamp < 0.0; let timestamp = timestamp.abs(); let seconds = timestamp.floor() as u64; let subsec_nanos = (timestamp.fract() * 1e9) as u32; let dur_since_plist_epoch = Duration::new(seconds, subsec_nanos); let inner = if is_negative { plist_epoch - dur_since_plist_epoch } else { plist_epoch + dur_since_plist_epoch }; Ok(Date { inner }) } } impl fmt::Debug for Date { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { let rfc3339 = humantime::format_rfc3339(self.inner); ::fmt(&rfc3339, f) } } impl From for Date { fn from(date: SystemTime) -> Self { Date { inner: date } } } impl Into for Date { fn into(self) -> SystemTime { self.inner } } #[cfg(feature = "serde")] pub mod serde_impls { use serde::de::{Deserialize, Deserializer, Error, Unexpected, Visitor}; use serde::ser::{Serialize, Serializer}; use std::fmt; use Date; pub const DATE_NEWTYPE_STRUCT_NAME: &str = "PLIST-DATE"; impl Serialize for Date { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let date_str = self.to_rfc3339(); serializer.serialize_newtype_struct(DATE_NEWTYPE_STRUCT_NAME, &date_str) } } struct DateNewtypeVisitor; impl<'de> Visitor<'de> for DateNewtypeVisitor { type Value = Date; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a plist date newtype") } fn visit_newtype_struct(self, deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(DateStrVisitor) } } struct DateStrVisitor; impl<'de> Visitor<'de> for DateStrVisitor { type Value = Date; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a plist date string") } fn visit_str(self, v: &str) -> Result where E: Error, { Date::from_rfc3339(v).map_err(|()| E::invalid_value(Unexpected::Str(v), &self)) } } impl<'de> Deserialize<'de> for Date { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_newtype_struct(DATE_NEWTYPE_STRUCT_NAME, DateNewtypeVisitor) } } } plist-0.4.2/src/de.rs010064400007650000024000000261721347751654600126460ustar0000000000000000use serde::de; use std::fmt::Display; use std::fs::File; use std::io::{BufReader, Read, Seek}; use std::iter::Peekable; use std::path::Path; use stream::{self, Event}; use {u64_to_usize, Error}; macro_rules! expect { ($next:expr, $pat:pat) => { match $next { Some(Ok(v @ $pat)) => v, None => return Err(Error::UnexpectedEof), _ => return Err(event_mismatch_error()), } }; ($next:expr, $pat:pat => $save:expr) => { match $next { Some(Ok($pat)) => $save, None => return Err(Error::UnexpectedEof), _ => return Err(event_mismatch_error()), } }; } macro_rules! try_next { ($next:expr) => { match $next { Some(Ok(v)) => v, Some(Err(_)) => return Err(event_mismatch_error()), None => return Err(Error::UnexpectedEof), } }; } fn event_mismatch_error() -> Error { Error::InvalidData } impl de::Error for Error { fn custom(msg: T) -> Self { Error::Serde(msg.to_string()) } } /// A structure that deserializes plist event streams into Rust values. pub struct Deserializer where I: IntoIterator>, { events: Peekable<::IntoIter>, } impl Deserializer where I: IntoIterator>, { pub fn new(iter: I) -> Deserializer { Deserializer { events: iter.into_iter().peekable(), } } } impl<'de, 'a, I> de::Deserializer<'de> for &'a mut Deserializer where I: IntoIterator>, { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: de::Visitor<'de>, { match try_next!(self.events.next()) { Event::StartArray(len) => { let len = len.and_then(u64_to_usize); let ret = visitor.visit_seq(MapAndSeqAccess::new(self, false, len))?; expect!(self.events.next(), Event::EndArray); Ok(ret) } Event::EndArray => Err(event_mismatch_error()), Event::StartDictionary(len) => { let len = len.and_then(u64_to_usize); let ret = visitor.visit_map(MapAndSeqAccess::new(self, false, len))?; expect!(self.events.next(), Event::EndDictionary); Ok(ret) } Event::EndDictionary => Err(event_mismatch_error()), Event::BooleanValue(v) => visitor.visit_bool(v), Event::DataValue(v) => visitor.visit_byte_buf(v), Event::DateValue(v) => visitor.visit_string(v.to_rfc3339()), Event::IntegerValue(v) if v.is_positive() => visitor.visit_u64(v as u64), Event::IntegerValue(v) => visitor.visit_i64(v as i64), Event::RealValue(v) => visitor.visit_f64(v), Event::StringValue(v) => visitor.visit_string(v), } } forward_to_deserialize_any! { bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq bytes byte_buf map unit_struct tuple_struct tuple ignored_any identifier } fn deserialize_unit(self, visitor: V) -> Result where V: de::Visitor<'de>, { expect!(self.events.next(), Event::StringValue(_)); visitor.visit_unit() } fn deserialize_option(self, visitor: V) -> Result where V: de::Visitor<'de>, { expect!(self.events.next(), Event::StartDictionary(_)); let ret = match try_next!(self.events.next()) { Event::StringValue(ref s) if &s[..] == "None" => { expect!(self.events.next(), Event::StringValue(_)); visitor.visit_none::()? } Event::StringValue(ref s) if &s[..] == "Some" => visitor.visit_some(&mut *self)?, _ => return Err(event_mismatch_error()), }; expect!(self.events.next(), Event::EndDictionary); Ok(ret) } fn deserialize_newtype_struct( self, _name: &'static str, visitor: V, ) -> Result where V: de::Visitor<'de>, { visitor.visit_newtype_struct(self) } fn deserialize_struct( self, _name: &'static str, _fields: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { expect!(self.events.next(), Event::StartDictionary(_)); let ret = visitor.visit_map(MapAndSeqAccess::new(self, true, None))?; expect!(self.events.next(), Event::EndDictionary); Ok(ret) } fn deserialize_enum( self, _enum: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { expect!(self.events.next(), Event::StartDictionary(_)); let ret = visitor.visit_enum(&mut *self)?; expect!(self.events.next(), Event::EndDictionary); Ok(ret) } } impl<'de, 'a, I> de::EnumAccess<'de> for &'a mut Deserializer where I: IntoIterator>, { type Error = Error; type Variant = Self; fn variant_seed(self, seed: V) -> Result<(V::Value, Self), Self::Error> where V: de::DeserializeSeed<'de>, { Ok((seed.deserialize(&mut *self)?, self)) } } impl<'de, 'a, I> de::VariantAccess<'de> for &'a mut Deserializer where I: IntoIterator>, { type Error = Error; fn unit_variant(self) -> Result<(), Self::Error> { <() as de::Deserialize>::deserialize(self) } fn newtype_variant_seed(self, seed: T) -> Result where T: de::DeserializeSeed<'de>, { seed.deserialize(self) } fn tuple_variant(self, len: usize, visitor: V) -> Result where V: de::Visitor<'de>, { ::deserialize_tuple(self, len, visitor) } fn struct_variant( self, fields: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { let name = ""; ::deserialize_struct(self, name, fields, visitor) } } pub struct StructValueDeserializer<'a, I: 'a> where I: IntoIterator>, { de: &'a mut Deserializer, } impl<'de, 'a, I> de::Deserializer<'de> for StructValueDeserializer<'a, I> where I: IntoIterator>, { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: de::Visitor<'de>, { self.de.deserialize_any(visitor) } forward_to_deserialize_any! { bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq bytes byte_buf map unit_struct tuple_struct tuple ignored_any identifier } fn deserialize_unit(self, visitor: V) -> Result where V: de::Visitor<'de>, { self.de.deserialize_unit(visitor) } fn deserialize_option(self, visitor: V) -> Result where V: de::Visitor<'de>, { // None struct values are ignored so if we're here the value must be Some. visitor.visit_some(self.de) } fn deserialize_newtype_struct( self, name: &'static str, visitor: V, ) -> Result where V: de::Visitor<'de>, { self.de.deserialize_newtype_struct(name, visitor) } fn deserialize_struct( self, name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { self.de.deserialize_struct(name, fields, visitor) } fn deserialize_enum( self, enum_: &'static str, variants: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { self.de.deserialize_enum(enum_, variants, visitor) } } struct MapAndSeqAccess<'a, I> where I: 'a + IntoIterator>, { de: &'a mut Deserializer, is_struct: bool, remaining: Option, } impl<'a, I> MapAndSeqAccess<'a, I> where I: 'a + IntoIterator>, { fn new( de: &'a mut Deserializer, is_struct: bool, len: Option, ) -> MapAndSeqAccess<'a, I> { MapAndSeqAccess { de, is_struct, remaining: len, } } } impl<'de, 'a, I> de::SeqAccess<'de> for MapAndSeqAccess<'a, I> where I: 'a + IntoIterator>, { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where T: de::DeserializeSeed<'de>, { if let Some(&Ok(Event::EndArray)) = self.de.events.peek() { return Ok(None); } self.remaining = self.remaining.map(|r| r.saturating_sub(1)); seed.deserialize(&mut *self.de).map(Some) } fn size_hint(&self) -> Option { self.remaining } } impl<'de, 'a, I> de::MapAccess<'de> for MapAndSeqAccess<'a, I> where I: 'a + IntoIterator>, { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> where K: de::DeserializeSeed<'de>, { if let Some(&Ok(Event::EndDictionary)) = self.de.events.peek() { return Ok(None); } self.remaining = self.remaining.map(|r| r.saturating_sub(1)); seed.deserialize(&mut *self.de).map(Some) } fn next_value_seed(&mut self, seed: V) -> Result where V: de::DeserializeSeed<'de>, { if self.is_struct { seed.deserialize(StructValueDeserializer { de: &mut *self.de }) } else { seed.deserialize(&mut *self.de) } } fn size_hint(&self) -> Option { self.remaining } } /// Deserializes an instance of type `T` from a plist file of any encoding. pub fn from_file, T: de::DeserializeOwned>(path: P) -> Result { let file = File::open(path)?; from_reader(BufReader::new(file)) } /// Deserializes an instance of type `T` from a seekable byte stream containing a plist file of any encoding. pub fn from_reader(reader: R) -> Result { let reader = stream::Reader::new(reader); let mut de = Deserializer::new(reader); de::Deserialize::deserialize(&mut de) } /// Deserializes an instance of type `T` from a byte stream containing an XML encoded plist file. pub fn from_reader_xml(reader: R) -> Result { let reader = stream::XmlReader::new(reader); let mut de = Deserializer::new(reader); de::Deserialize::deserialize(&mut de) } plist-0.4.2/src/lib.rs010064400007650000024000000050541347751654600130200ustar0000000000000000//! # Plist //! //! A rusty plist parser. //! //! ## Usage //! //! Put this in your `Cargo.toml`: //! //! ```toml //! [dependencies] //! plist = "0.4" //! ``` //! //! And put this in your crate root: //! //! ```rust //! extern crate plist; //! ``` //! //! ## Examples //! //! ```rust //! use plist::Value; //! //! let value = Value::from_file("tests/data/xml.plist").unwrap(); //! //! match value { //! Value::Array(_array) => (), //! _ => () //! } //! ``` //! //! ```rust //! extern crate plist; //! # #[cfg(feature = "serde")] //! #[macro_use] //! extern crate serde_derive; //! //! # #[cfg(feature = "serde")] //! # fn main() { //! #[derive(Deserialize)] //! #[serde(rename_all = "PascalCase")] //! struct Info { //! author: String, //! height: f32, //! } //! //! let info: Info = plist::from_file("tests/data/xml.plist").unwrap(); //! # } //! # //! # #[cfg(not(feature = "serde"))] //! # fn main() {} //! ``` extern crate base64; extern crate byteorder; extern crate humantime; extern crate line_wrap; extern crate xml as xml_rs; pub mod stream; mod date; mod value; pub use date::Date; pub use value::Value; // Optional serde module #[cfg(feature = "serde")] #[macro_use] extern crate serde; #[cfg(feature = "serde")] mod de; #[cfg(feature = "serde")] mod ser; #[cfg(feature = "serde")] pub use self::de::{from_file, from_reader, from_reader_xml, Deserializer}; #[cfg(feature = "serde")] pub use self::ser::{to_writer_xml, Serializer}; use std::fmt; use std::io; #[derive(Debug)] pub enum Error { InvalidData, UnexpectedEof, Io(io::Error), 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: io::Error) -> Error { Error::Io(err) } } fn u64_to_usize(len_u64: u64) -> Option { let len = len_u64 as usize; if len as u64 != len_u64 { return None; // Too long } Some(len) } plist-0.4.2/src/ser.rs010064400007650000024000000473011347751654600130440ustar0000000000000000use serde::ser; use std::fmt::Display; use std::io::Write; use date::serde_impls::DATE_NEWTYPE_STRUCT_NAME; use stream::{self, Event, Writer}; use {Date, Error}; impl ser::Error for Error { fn custom(msg: T) -> Self { Error::Serde(msg.to_string()) } } /// A structure that serializes Rust values plist event streams. pub struct Serializer { writer: W, } impl Serializer { pub fn new(writer: W) -> Serializer { Serializer { writer } } fn emit(&mut self, event: Event) -> Result<(), Error> { self.writer.write(&event)?; Ok(()) } pub fn into_inner(self) -> W { self.writer } // Emit {key: value} fn single_key_dict(&mut self, key: String) -> Result<(), Error> { self.emit(Event::StartDictionary(Some(1)))?; self.emit(Event::StringValue(key))?; Ok(()) } fn single_key_dict_end(&mut self) -> Result<(), Error> { self.emit(Event::EndDictionary)?; Ok(()) } } impl<'a, W: Writer> ser::Serializer for &'a mut Serializer { type Ok = (); type Error = Error; type SerializeSeq = Compound<'a, W>; type SerializeTuple = Compound<'a, W>; type SerializeTupleStruct = Compound<'a, W>; type SerializeTupleVariant = Compound<'a, W>; type SerializeMap = Compound<'a, W>; type SerializeStruct = Compound<'a, W>; type SerializeStructVariant = Compound<'a, W>; fn serialize_bool(self, v: bool) -> Result<(), Self::Error> { self.emit(Event::BooleanValue(v)) } fn serialize_i8(self, v: i8) -> Result<(), Self::Error> { self.serialize_i64(v.into()) } fn serialize_i16(self, v: i16) -> Result<(), Self::Error> { self.serialize_i64(v.into()) } fn serialize_i32(self, v: i32) -> Result<(), Self::Error> { self.serialize_i64(v.into()) } fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { self.emit(Event::IntegerValue(v)) } fn serialize_u8(self, v: u8) -> Result<(), Self::Error> { self.serialize_u64(v.into()) } fn serialize_u16(self, v: u16) -> Result<(), Self::Error> { self.serialize_u64(v.into()) } fn serialize_u32(self, v: u32) -> Result<(), Self::Error> { self.serialize_u64(v.into()) } fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { self.emit(Event::IntegerValue(v as i64)) } fn serialize_f32(self, v: f32) -> Result<(), Self::Error> { self.serialize_f64(v.into()) } fn serialize_f64(self, v: f64) -> Result<(), Self::Error> { self.emit(Event::RealValue(v)) } fn serialize_char(self, v: char) -> Result<(), Self::Error> { self.emit(Event::StringValue(v.to_string())) } fn serialize_str(self, v: &str) -> Result<(), Self::Error> { self.emit(Event::StringValue(v.to_owned())) } fn serialize_bytes(self, v: &[u8]) -> Result<(), Self::Error> { self.emit(Event::DataValue(v.to_owned())) } fn serialize_none(self) -> Result<(), Self::Error> { self.single_key_dict("None".to_owned())?; self.serialize_unit()?; self.single_key_dict_end() } fn serialize_some(self, value: &T) -> Result<(), Self::Error> { self.single_key_dict("Some".to_owned())?; value.serialize(&mut *self)?; self.single_key_dict_end() } fn serialize_unit(self) -> Result<(), Self::Error> { // Emit empty string self.emit(Event::StringValue(String::new())) } fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Self::Error> { self.serialize_unit() } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result<(), Self::Error> { self.single_key_dict(variant.to_owned())?; self.serialize_unit()?; self.single_key_dict_end()?; Ok(()) } fn serialize_newtype_struct( self, name: &'static str, value: &T, ) -> Result<(), Self::Error> { if name == DATE_NEWTYPE_STRUCT_NAME { value.serialize(DateSerializer { ser: &mut *self }) } else { value.serialize(self) } } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result<(), Self::Error> { self.single_key_dict(variant.to_owned())?; value.serialize(&mut *self)?; self.single_key_dict_end() } fn serialize_seq(self, len: Option) -> Result { let len = len.map(|len| len as u64); self.emit(Event::StartArray(len))?; Ok(Compound { ser: self }) } fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } fn serialize_tuple_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_tuple(len) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.single_key_dict(variant.to_owned())?; self.serialize_tuple(len) } fn serialize_map(self, len: Option) -> Result { let len = len.map(|len| len as u64); self.emit(Event::StartDictionary(len))?; Ok(Compound { ser: self }) } fn serialize_struct( self, _name: &'static str, _len: usize, ) -> Result { // The number of struct fields is not known as fields with None values are ignored. self.serialize_map(None) } fn serialize_struct_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.single_key_dict(variant.to_owned())?; self.serialize_struct(name, len) } } struct StructFieldSerializer<'a, W: 'a + Writer> { ser: &'a mut Serializer, field_name: &'static str, } impl<'a, W: Writer> StructFieldSerializer<'a, W> { fn use_ser(self) -> Result<&'a mut Serializer, Error> { // We are going to serialize something so write the struct field name. self.ser .emit(Event::StringValue(self.field_name.to_owned()))?; Ok(self.ser) } } impl<'a, W: Writer> ser::Serializer for StructFieldSerializer<'a, W> { type Ok = (); type Error = Error; type SerializeSeq = Compound<'a, W>; type SerializeTuple = Compound<'a, W>; type SerializeTupleStruct = Compound<'a, W>; type SerializeTupleVariant = Compound<'a, W>; type SerializeMap = Compound<'a, W>; type SerializeStruct = Compound<'a, W>; type SerializeStructVariant = Compound<'a, W>; fn serialize_bool(self, v: bool) -> Result<(), Self::Error> { self.use_ser()?.serialize_bool(v) } fn serialize_i8(self, v: i8) -> Result<(), Self::Error> { self.use_ser()?.serialize_i8(v) } fn serialize_i16(self, v: i16) -> Result<(), Self::Error> { self.use_ser()?.serialize_i16(v) } fn serialize_i32(self, v: i32) -> Result<(), Self::Error> { self.use_ser()?.serialize_i32(v) } fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { self.use_ser()?.serialize_i64(v) } fn serialize_u8(self, v: u8) -> Result<(), Self::Error> { self.use_ser()?.serialize_u8(v) } fn serialize_u16(self, v: u16) -> Result<(), Self::Error> { self.use_ser()?.serialize_u16(v) } fn serialize_u32(self, v: u32) -> Result<(), Self::Error> { self.use_ser()?.serialize_u32(v) } fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { self.use_ser()?.serialize_u64(v) } fn serialize_f32(self, v: f32) -> Result<(), Self::Error> { self.use_ser()?.serialize_f32(v) } fn serialize_f64(self, v: f64) -> Result<(), Self::Error> { self.use_ser()?.serialize_f64(v) } fn serialize_char(self, v: char) -> Result<(), Self::Error> { self.use_ser()?.serialize_char(v) } fn serialize_str(self, v: &str) -> Result<(), Self::Error> { self.use_ser()?.serialize_str(v) } fn serialize_bytes(self, v: &[u8]) -> Result<(), Self::Error> { self.use_ser()?.serialize_bytes(v) } fn serialize_none(self) -> Result<(), Self::Error> { // Don't write a dict for None if the Option is in a struct. Ok(()) } fn serialize_some(self, value: &T) -> Result<(), Self::Error> { let ser = self.use_ser()?; value.serialize(ser) } fn serialize_unit(self) -> Result<(), Self::Error> { self.use_ser()?.serialize_unit() } fn serialize_unit_struct(self, name: &'static str) -> Result<(), Self::Error> { self.use_ser()?.serialize_unit_struct(name) } fn serialize_unit_variant( self, name: &'static str, variant_index: u32, variant: &'static str, ) -> Result<(), Self::Error> { self.use_ser()? .serialize_unit_variant(name, variant_index, variant) } fn serialize_newtype_struct( self, name: &'static str, value: &T, ) -> Result<(), Self::Error> { self.use_ser()?.serialize_newtype_struct(name, value) } fn serialize_newtype_variant( self, name: &'static str, variant_index: u32, variant: &'static str, value: &T, ) -> Result<(), Self::Error> { self.use_ser()? .serialize_newtype_variant(name, variant_index, variant, value) } fn serialize_seq(self, len: Option) -> Result { self.use_ser()?.serialize_seq(len) } fn serialize_tuple(self, len: usize) -> Result { self.use_ser()?.serialize_tuple(len) } fn serialize_tuple_struct( self, name: &'static str, len: usize, ) -> Result { self.use_ser()?.serialize_tuple_struct(name, len) } fn serialize_tuple_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.use_ser()? .serialize_tuple_variant(name, variant_index, variant, len) } fn serialize_map(self, len: Option) -> Result { self.use_ser()?.serialize_map(len) } fn serialize_struct( self, name: &'static str, len: usize, ) -> Result { self.use_ser()?.serialize_struct(name, len) } fn serialize_struct_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.use_ser()? .serialize_struct_variant(name, variant_index, variant, len) } } struct DateSerializer<'a, W: 'a + Writer> { ser: &'a mut Serializer, } impl<'a, W: Writer> DateSerializer<'a, W> { fn expecting_date_error(&self) -> Error { ser::Error::custom("plist date string expected") } } impl<'a, W: Writer> ser::Serializer for DateSerializer<'a, W> { type Ok = (); type Error = Error; type SerializeSeq = ser::Impossible<(), Error>; type SerializeTuple = ser::Impossible<(), Error>; type SerializeTupleStruct = ser::Impossible<(), Error>; type SerializeTupleVariant = ser::Impossible<(), Error>; type SerializeMap = ser::Impossible<(), Error>; type SerializeStruct = ser::Impossible<(), Error>; type SerializeStructVariant = ser::Impossible<(), Error>; fn serialize_bool(self, _: bool) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_i8(self, _: i8) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_i16(self, _: i16) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_i32(self, _: i32) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_i64(self, _: i64) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_u8(self, _: u8) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_u16(self, _: u16) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_u32(self, _: u32) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_u64(self, _: u64) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_f32(self, _: f32) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_f64(self, _: f64) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_char(self, _: char) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_str(self, v: &str) -> Result<(), Self::Error> { let date = Date::from_rfc3339(v).map_err(|()| self.expecting_date_error())?; self.ser.emit(Event::DateValue(date)) } fn serialize_bytes(self, _: &[u8]) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_none(self) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_some(self, _: &T) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_unit(self) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_unit_struct(self, _: &'static str) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_unit_variant( self, _: &'static str, _: u32, _: &'static str, ) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_newtype_struct( self, _: &'static str, _: &T, ) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_newtype_variant( self, _: &'static str, _: u32, _: &'static str, _: &T, ) -> Result<(), Self::Error> { Err(self.expecting_date_error()) } fn serialize_seq(self, _: Option) -> Result { Err(self.expecting_date_error()) } fn serialize_tuple(self, _: usize) -> Result { Err(self.expecting_date_error()) } fn serialize_tuple_struct( self, _: &'static str, _: usize, ) -> Result { Err(self.expecting_date_error()) } fn serialize_tuple_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(self.expecting_date_error()) } fn serialize_map(self, _: Option) -> Result { Err(self.expecting_date_error()) } fn serialize_struct( self, _: &'static str, _: usize, ) -> Result { Err(self.expecting_date_error()) } fn serialize_struct_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(self.expecting_date_error()) } } #[doc(hidden)] pub struct Compound<'a, W: 'a + Writer> { ser: &'a mut Serializer, } impl<'a, W: Writer> ser::SerializeSeq for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_element( &mut self, value: &T, ) -> Result<(), Self::Error> { value.serialize(&mut *self.ser) } fn end(self) -> Result { self.ser.emit(Event::EndArray) } } impl<'a, W: Writer> ser::SerializeTuple for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_element( &mut self, value: &T, ) -> Result<(), Self::Error> { ::serialize_element(self, value) } fn end(self) -> Result { ::end(self) } } impl<'a, W: Writer> ser::SerializeTupleStruct for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field( &mut self, value: &T, ) -> Result<(), Self::Error> { ::serialize_element(self, value) } fn end(self) -> Result { ::end(self) } } impl<'a, W: Writer> ser::SerializeTupleVariant for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field( &mut self, value: &T, ) -> Result<(), Self::Error> { ::serialize_element(self, value) } fn end(self) -> Result { self.ser.emit(Event::EndArray)?; self.ser.single_key_dict_end() } } impl<'a, W: Writer> ser::SerializeMap for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> { key.serialize(&mut *self.ser) } fn serialize_value( &mut self, value: &T, ) -> Result<(), Self::Error> { value.serialize(&mut *self.ser) } fn end(self) -> Result { self.ser.emit(Event::EndDictionary) } } impl<'a, W: Writer> ser::SerializeStruct for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field( &mut self, key: &'static str, value: &T, ) -> Result<(), Self::Error> { // We don't want to serialize None if the Option is a struct field as this is how null // fields are represented in plists. value.serialize(StructFieldSerializer { field_name: key, ser: &mut *self.ser, }) } fn end(self) -> Result { ::end(self) } } impl<'a, W: Writer> ser::SerializeStructVariant for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field( &mut self, key: &'static str, value: &T, ) -> Result<(), Self::Error> { ::serialize_field(self, key, value) } fn end(self) -> Result { self.ser.emit(Event::EndDictionary)?; self.ser.single_key_dict_end() } } /// Serializes the given data structure as an XML encoded plist file. pub fn to_writer_xml(writer: W, value: &T) -> Result<(), Error> { let writer = stream::XmlWriter::new(writer); let mut ser = Serializer::new(writer); value.serialize(&mut ser) } plist-0.4.2/src/stream/binary_reader.rs010064400007650000024000000317031347751654600163530ustar0000000000000000use byteorder::{BigEndian, ReadBytesExt}; use std::io::{Read, Seek, SeekFrom}; use std::mem::size_of; use std::string::{FromUtf16Error, FromUtf8Error}; use stream::Event; use {u64_to_usize, Date, Error}; impl From for Error { fn from(_: FromUtf8Error) -> Error { Error::InvalidData } } impl From for Error { fn from(_: FromUtf16Error) -> Error { Error::InvalidData } } struct StackItem { object_ref: u64, child_object_refs: Vec, ty: StackType, } enum StackType { Array, Dict, } // https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c // https://hg.python.org/cpython/file/3.4/Lib/plistlib.py pub struct BinaryReader { stack: Vec, object_offsets: Vec, object_on_stack: Vec, reader: R, ref_size: u8, root_object: u64, // The largest single allocation allowed for this plist. // Equal to the number of bytes in the plist minus the magic number and trailer. max_allocation_bytes: usize, } impl BinaryReader { pub fn new(reader: R) -> BinaryReader { BinaryReader { stack: Vec::new(), object_offsets: Vec::new(), object_on_stack: Vec::new(), reader, ref_size: 0, root_object: 0, max_allocation_bytes: 0, } } fn can_allocate(&self, len: u64, size: usize) -> bool { let byte_len = len.saturating_mul(size as u64); byte_len <= self.max_allocation_bytes as u64 } fn allocate_vec(&self, len: u64, size: usize) -> Result, Error> { if self.can_allocate(len, size) { Ok(Vec::with_capacity(len as usize)) } else { Err(Error::InvalidData) } } fn read_trailer(&mut self) -> Result<(), Error> { self.reader.seek(SeekFrom::Start(0))?; let mut magic = [0; 8]; self.reader.read_exact(&mut magic)?; if &magic != b"bplist00" { return Err(Error::InvalidData); } // Trailer starts with 6 bytes of padding let trailer_start = self.reader.seek(SeekFrom::End(-32 + 6))?; let offset_size = self.reader.read_u8()?; match offset_size { 1 | 2 | 4 | 8 => (), _ => return Err(Error::InvalidData), } self.ref_size = self.reader.read_u8()?; match self.ref_size { 1 | 2 | 4 | 8 => (), _ => return Err(Error::InvalidData), } let num_objects = self.reader.read_u64::()?; self.root_object = self.reader.read_u64::()?; let offset_table_offset = self.reader.read_u64::()?; // File size minus trailer and header // Truncated to max(usize) self.max_allocation_bytes = trailer_start.saturating_sub(8) as usize; // Read offset table self.reader.seek(SeekFrom::Start(offset_table_offset))?; self.object_offsets = self.read_ints(num_objects, offset_size)?; self.object_on_stack = vec![false; self.object_offsets.len()]; Ok(()) } fn read_ints(&mut self, len: u64, size: u8) -> Result, Error> { let mut ints = self.allocate_vec(len, size as usize)?; for _ in 0..len { match size { 1 => ints.push(self.reader.read_u8()?.into()), 2 => ints.push(self.reader.read_u16::()?.into()), 4 => ints.push(self.reader.read_u32::()?.into()), 8 => ints.push(self.reader.read_u64::()?), _ => return Err(Error::InvalidData), } } Ok(ints) } fn read_refs(&mut self, len: u64) -> Result, Error> { let ref_size = self.ref_size; self.read_ints(len, ref_size) } fn read_object_len(&mut self, len: u8) -> Result { if (len & 0x0f) == 0x0f { let len_power_of_two = self.reader.read_u8()? & 0x03; Ok(match len_power_of_two { 0 => self.reader.read_u8()?.into(), 1 => self.reader.read_u16::()?.into(), 2 => self.reader.read_u32::()?.into(), 3 => self.reader.read_u64::()?, _ => return Err(Error::InvalidData), }) } else { Ok(len.into()) } } fn read_data(&mut self, len: u64) -> Result, Error> { let mut data = self.allocate_vec(len, size_of::())?; data.resize(len as usize, 0); self.reader.read_exact(&mut data)?; Ok(data) } fn seek_to_object(&mut self, object_ref: u64) -> Result { let object_ref = u64_to_usize(object_ref).ok_or(Error::InvalidData)?; let offset = *self .object_offsets .get(object_ref) .ok_or(Error::InvalidData)?; Ok(self.reader.seek(SeekFrom::Start(offset))?) } fn push_stack_item_and_check_for_recursion(&mut self, item: StackItem) -> Result<(), Error> { let object_ref = u64_to_usize(item.object_ref).expect("internal consistency error"); let is_on_stack = &mut self.object_on_stack[object_ref]; if *is_on_stack { return Err(Error::InvalidData); } *is_on_stack = true; self.stack.push(item); Ok(()) } fn pop_stack_item(&mut self) -> StackItem { let item = self.stack.pop().expect("internal consistency error"); let object_ref = u64_to_usize(item.object_ref).expect("internal consistency error"); self.object_on_stack[object_ref] = false; item } fn read_next(&mut self) -> Result, Error> { let object_ref = if self.ref_size == 0 { // Initialise here rather than in new self.read_trailer()?; self.root_object } else { let maybe_object_ref = if let Some(stack_item) = self.stack.last_mut() { stack_item.child_object_refs.pop() } else { // Finished reading the plist return Ok(None); }; if let Some(object_ref) = maybe_object_ref { object_ref } else { // We're at the end of an array or dict. Pop the top stack item and return. let stack_item = self.pop_stack_item(); match stack_item.ty { StackType::Array => return Ok(Some(Event::EndArray)), StackType::Dict => return Ok(Some(Event::EndDictionary)), } } }; self.seek_to_object(object_ref)?; let token = self.reader.read_u8()?; let ty = (token & 0xf0) >> 4; let size = token & 0x0f; let result = match (ty, size) { (0x0, 0x00) => return Err(Error::InvalidData), // null (0x0, 0x08) => Some(Event::BooleanValue(false)), (0x0, 0x09) => Some(Event::BooleanValue(true)), (0x0, 0x0f) => return Err(Error::InvalidData), // fill (0x1, 0) => Some(Event::IntegerValue(self.reader.read_u8()?.into())), (0x1, 1) => Some(Event::IntegerValue( self.reader.read_u16::()?.into(), )), (0x1, 2) => Some(Event::IntegerValue( self.reader.read_u32::()?.into(), )), (0x1, 3) => Some(Event::IntegerValue(self.reader.read_i64::()?)), (0x1, 4) => return Err(Error::InvalidData), // 128 bit int (0x1, _) => return Err(Error::InvalidData), // variable length int (0x2, 2) => Some(Event::RealValue( self.reader.read_f32::()?.into(), )), (0x2, 3) => Some(Event::RealValue(self.reader.read_f64::()?)), (0x2, _) => return Err(Error::InvalidData), // odd length float (0x3, 3) => { // Date. Seconds since 1/1/2001 00:00:00. let secs = self.reader.read_f64::()?; Some(Event::DateValue( Date::from_seconds_since_plist_epoch(secs).map_err(|()| Error::InvalidData)?, )) } (0x4, n) => { // Data let len = self.read_object_len(n)?; Some(Event::DataValue(self.read_data(len)?)) } (0x5, n) => { // ASCII string let len = self.read_object_len(n)?; let raw = self.read_data(len)?; let string = String::from_utf8(raw)?; Some(Event::StringValue(string)) } (0x6, n) => { // UTF-16 string let len_utf16_codepoints = self.read_object_len(n)?; let mut raw_utf16 = self.allocate_vec(len_utf16_codepoints, size_of::())?; for _ in 0..len_utf16_codepoints { raw_utf16.push(self.reader.read_u16::()?); } let string = String::from_utf16(&raw_utf16)?; Some(Event::StringValue(string)) } (0xa, n) => { // Array let len = self.read_object_len(n)?; let mut child_object_refs = self.read_refs(len)?; // Reverse so we can pop off the end of the stack in order child_object_refs.reverse(); self.push_stack_item_and_check_for_recursion(StackItem { object_ref, ty: StackType::Array, child_object_refs, })?; Some(Event::StartArray(Some(len))) } (0xd, n) => { // Dict let len = self.read_object_len(n)?; let key_refs = self.read_refs(len)?; let value_refs = self.read_refs(len)?; let mut child_object_refs = self.allocate_vec(len * 2, self.ref_size as usize)?; let len = key_refs.len(); for i in 1..len + 1 { // Reverse so we can pop off the end of the stack in order child_object_refs.push(value_refs[len - i]); child_object_refs.push(key_refs[len - i]); } self.push_stack_item_and_check_for_recursion(StackItem { object_ref, ty: StackType::Dict, child_object_refs, })?; Some(Event::StartDictionary(Some(len as u64))) } (_, _) => return Err(Error::InvalidData), }; Ok(result) } } impl Iterator for BinaryReader { type Item = Result; fn next(&mut self) -> Option> { match self.read_next() { Ok(Some(event)) => Some(Ok(event)), Err(err) => { // Mark the plist as finished self.stack.clear(); Some(Err(err)) } Ok(None) => None, } } } #[cfg(test)] mod tests { use humantime::parse_rfc3339_weak; use std::fs::File; use std::path::Path; use super::*; use stream::Event; use stream::Event::*; #[test] fn streaming_parser() { let reader = File::open(&Path::new("./tests/data/binary.plist")).unwrap(); let streaming_parser = BinaryReader::new(reader); let events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); let comparison = &[ StartDictionary(Some(6)), StringValue("Lines".to_owned()), StartArray(Some(2)), StringValue("It is a tale told by an idiot,".to_owned()), StringValue("Full of sound and fury, signifying nothing.".to_owned()), EndArray, StringValue("Death".to_owned()), IntegerValue(1564), StringValue("Height".to_owned()), RealValue(1.60), StringValue("Birthdate".to_owned()), DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), StringValue("Author".to_owned()), StringValue("William Shakespeare".to_owned()), StringValue("Data".to_owned()), DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), EndDictionary, ]; assert_eq!(events, comparison); } #[test] fn utf16_plist() { let reader = File::open(&Path::new("./tests/data/utf16_bplist.plist")).unwrap(); let streaming_parser = BinaryReader::new(reader); let mut events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); assert_eq!(events[2], StringValue("\u{2605} or better".to_owned())); let poem = if let StringValue(ref mut poem) = events[4] { poem } else { panic!("not a string") }; assert_eq!(poem.len(), 643); assert_eq!(poem.pop().unwrap(), '\u{2605}'); } } plist-0.4.2/src/stream/mod.rs010064400007650000024000000053031347751654600143210ustar0000000000000000//! An abstraction of a plist file as a stream of events. Used to support multiple encodings. mod binary_reader; pub use self::binary_reader::BinaryReader; mod xml_reader; pub use self::xml_reader::XmlReader; mod xml_writer; pub use self::xml_writer::XmlWriter; use std::io::{Read, Seek, SeekFrom}; use {Date, Error}; /// 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 Event { // 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(Date), IntegerValue(i64), RealValue(f64), StringValue(String), } pub struct Reader(ReaderInner); enum ReaderInner { Uninitialized(Option), Xml(XmlReader), Binary(BinaryReader), } impl Reader { pub fn new(reader: R) -> Reader { Reader(ReaderInner::Uninitialized(Some(reader))) } fn is_binary(reader: &mut R) -> Result { reader.seek(SeekFrom::Start(0))?; let mut magic = [0; 8]; reader.read_exact(&mut magic)?; reader.seek(SeekFrom::Start(0))?; Ok(&magic == b"bplist00") } } impl Iterator for Reader { type Item = Result; fn next(&mut self) -> Option> { let mut reader = match self.0 { ReaderInner::Xml(ref mut parser) => return parser.next(), ReaderInner::Binary(ref mut parser) => return parser.next(), ReaderInner::Uninitialized(ref mut reader) => reader.take().unwrap(), }; let event_reader = match Reader::is_binary(&mut reader) { Ok(true) => ReaderInner::Binary(BinaryReader::new(reader)), Ok(false) => ReaderInner::Xml(XmlReader::new(reader)), Err(err) => { ::std::mem::replace(&mut self.0, ReaderInner::Uninitialized(Some(reader))); return Some(Err(err)); } }; ::std::mem::replace(&mut self.0, event_reader); self.next() } } /// Supports writing event streams in different plist encodings. pub trait Writer { fn write(&mut self, event: &Event) -> Result<(), Error>; } plist-0.4.2/src/stream/xml_reader.rs010064400007650000024000000200001350001544600156270ustar0000000000000000use base64; use std::io::Read; use std::str::FromStr; use xml_rs::reader::{EventReader, ParserConfig, XmlEvent}; use stream::Event; use {Date, Error}; pub struct XmlReader { xml_reader: EventReader, queued_event: Option, element_stack: Vec, finished: bool, } impl XmlReader { pub fn new(reader: R) -> XmlReader { let config = ParserConfig::new() .trim_whitespace(false) .whitespace_to_characters(true) .cdata_to_characters(true) .ignore_comments(true) .coalesce_characters(true); XmlReader { xml_reader: EventReader::new_with_config(reader, config), queued_event: None, element_stack: Vec::new(), finished: false, } } fn read_content(&mut self, f: F) -> Result where F: FnOnce(String) -> Result, { match self.xml_reader.next() { Ok(XmlEvent::Characters(s)) => f(s), Ok(event @ XmlEvent::EndElement { .. }) => { self.queued_event = Some(event); f("".to_owned()) } _ => Err(Error::InvalidData), } } fn next_event(&mut self) -> ::std::result::Result { if let Some(event) = self.queued_event.take() { Ok(event) } else { self.xml_reader.next().map_err(|_| ()) } } fn read_next(&mut self) -> Option> { loop { match self.next_event() { Ok(XmlEvent::StartElement { name, .. }) => { // Add the current element to the element stack self.element_stack.push(name.local_name.clone()); match &name.local_name[..] { "plist" => (), "array" => return Some(Ok(Event::StartArray(None))), "dict" => return Some(Ok(Event::StartDictionary(None))), "key" => return Some(self.read_content(|s| Ok(Event::StringValue(s)))), "true" => return Some(Ok(Event::BooleanValue(true))), "false" => return Some(Ok(Event::BooleanValue(false))), "data" => { return Some(self.read_content(|mut s| { // Strip whitespace and line endings from input string s.retain(|c| !c.is_ascii_whitespace()); let data = base64::decode(&s).map_err(|_| Error::InvalidData)?; Ok(Event::DataValue(data)) })); } "date" => { return Some(self.read_content(|s| { Ok(Event::DateValue( Date::from_rfc3339(&s).map_err(|()| Error::InvalidData)?, )) })); } "integer" => { return Some(self.read_content(|s| { if s.starts_with("0x") { // NetBSD dialect adds the `0x` numeric objects, // which are always unsigned. // See the `PROP_NUMBER(3)` man page let s = s.trim_left_matches("0x"); Ok(Event::IntegerValue( i64::from_str_radix(s, 16) .map_err(|_| Error::InvalidData)?, )) } else { Ok(Event::IntegerValue( i64::from_str(&s).map_err(|_| Error::InvalidData)?, )) } })); } "real" => { return Some(self.read_content(|s| match FromStr::from_str(&s) { Ok(f) => Ok(Event::RealValue(f)), Err(_) => Err(Error::InvalidData), })); } "string" => return Some(self.read_content(|s| Ok(Event::StringValue(s)))), _ => return Some(Err(Error::InvalidData)), } } Ok(XmlEvent::EndElement { name, .. }) => { // Check the corrent element is being closed match self.element_stack.pop() { Some(ref open_name) if &name.local_name == open_name => (), Some(ref _open_name) => return Some(Err(Error::InvalidData)), None => return Some(Err(Error::InvalidData)), } match &name.local_name[..] { "array" => return Some(Ok(Event::EndArray)), "dict" => return Some(Ok(Event::EndDictionary)), "plist" => (), _ => (), } } Ok(XmlEvent::EndDocument) => { if self.element_stack.is_empty() { return None; } else { return Some(Err(Error::UnexpectedEof)); } } Err(_) => return Some(Err(Error::InvalidData)), _ => (), } } } } impl Iterator for XmlReader { type Item = Result; fn next(&mut self) -> Option> { if self.finished { None } else { match self.read_next() { Some(Ok(event)) => Some(Ok(event)), Some(Err(err)) => { self.finished = true; Some(Err(err)) } None => { self.finished = true; None } } } } } #[cfg(test)] mod tests { use humantime::parse_rfc3339_weak; use std::fs::File; use std::path::Path; use super::*; use stream::Event; use stream::Event::*; #[test] fn streaming_parser() { let reader = File::open(&Path::new("./tests/data/xml.plist")).unwrap(); let streaming_parser = XmlReader::new(reader); let events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); let comparison = &[ 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("Death".to_owned()), IntegerValue(1564), StringValue("Height".to_owned()), RealValue(1.60), StringValue("Data".to_owned()), DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), StringValue("Birthdate".to_owned()), DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), StringValue("Blank".to_owned()), StringValue("".to_owned()), StringValue("HexademicalNumber".to_owned()), IntegerValue(0xdead_beef_i64), EndDictionary, ]; assert_eq!(events, comparison); } #[test] fn bad_data() { let reader = File::open(&Path::new("./tests/data/xml_error.plist")).unwrap(); let streaming_parser = XmlReader::new(reader); let events: Vec<_> = streaming_parser.collect(); assert!(events.last().unwrap().is_err()); } } plist-0.4.2/src/stream/xml_writer.rs010064400007650000024000000237101347751654600157400ustar0000000000000000use base64; use line_wrap; use std::borrow::Cow; use std::io::Write; use xml_rs::name::Name; use xml_rs::namespace::Namespace; use xml_rs::writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent}; use stream::{Event, Writer}; use Error; static XML_PROLOGUE: &str = r#" "#; impl From for Error { fn from(err: XmlWriterError) -> Error { match err { XmlWriterError::Io(err) => Error::Io(err), _ => Error::InvalidData, } } } #[derive(PartialEq)] enum Element { Dictionary, Array, } pub struct XmlWriter { xml_writer: EventWriter, stack: Vec, expecting_key: bool, written_prologue: bool, // Not very nice empty_namespace: Namespace, } impl XmlWriter { pub fn new(writer: W) -> XmlWriter { let config = EmitterConfig::new() .line_separator("\n") .indent_string("\t") .perform_indent(true) .write_document_declaration(false) .normalize_empty_elements(true) .cdata_to_characters(true) .keep_element_names_stack(false) .autopad_comments(true); XmlWriter { xml_writer: EventWriter::new_with_config(writer, config), stack: Vec::new(), expecting_key: false, written_prologue: false, empty_namespace: Namespace::empty(), } } fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<(), Error> { self.start_element(name)?; self.write_value(value)?; self.end_element(name)?; Ok(()) } fn start_element(&mut self, name: &str) -> Result<(), Error> { self.xml_writer.write(XmlEvent::StartElement { name: Name::local(name), attributes: Cow::Borrowed(&[]), namespace: Cow::Borrowed(&self.empty_namespace), })?; Ok(()) } fn end_element(&mut self, name: &str) -> Result<(), Error> { self.xml_writer.write(XmlEvent::EndElement { name: Some(Name::local(name)), })?; Ok(()) } fn write_value(&mut self, value: &str) -> Result<(), Error> { self.xml_writer.write(XmlEvent::Characters(value))?; Ok(()) } pub fn write(&mut self, event: &Event) -> Result<(), Error> { ::write(self, event) } pub fn into_inner(self) -> W { self.xml_writer.into_inner() } } impl Writer for XmlWriter { fn write(&mut self, event: &Event) -> Result<(), Error> { if !self.written_prologue { self.xml_writer .inner_mut() .write_all(XML_PROLOGUE.as_bytes())?; self.written_prologue = true; } if self.expecting_key { match *event { Event::EndDictionary => match self.stack.pop() { Some(Element::Dictionary) => { self.end_element("dict")?; self.expecting_key = self.stack.last() == Some(&Element::Dictionary); } _ => return Err(Error::InvalidData), }, Event::StringValue(ref value) => { self.write_element_and_value("key", &*value)?; self.expecting_key = false; } _ => return Err(Error::InvalidData), } } else { match *event { Event::StartArray(_) => { self.start_element("array")?; self.stack.push(Element::Array); } Event::EndArray => match self.stack.pop() { Some(Element::Array) => self.end_element("array")?, _ => return Err(Error::InvalidData), }, Event::StartDictionary(_) => { self.start_element("dict")?; self.stack.push(Element::Dictionary); } Event::EndDictionary => return Err(Error::InvalidData), Event::BooleanValue(true) => { self.start_element("true")?; self.end_element("true")?; } Event::BooleanValue(false) => { self.start_element("false")?; self.end_element("false")?; } Event::DataValue(ref value) => { let base64_data = base64_encode_plist(&value, self.stack.len()); self.write_element_and_value("data", &base64_data)?; } Event::DateValue(ref value) => { self.write_element_and_value("date", &value.to_rfc3339())? } Event::IntegerValue(ref value) => { self.write_element_and_value("integer", &value.to_string())? } Event::RealValue(ref value) => { self.write_element_and_value("real", &value.to_string())? } Event::StringValue(ref value) => self.write_element_and_value("string", &*value)?, }; self.expecting_key = self.stack.last() == Some(&Element::Dictionary); } // If there are no more open tags then write the element if self.stack.len() == 0 { // We didn't tell the xml_writer about the tag so we'll skip telling it // about the tag as well. self.xml_writer.inner_mut().write_all(b"\n")?; } Ok(()) } } fn base64_encode_plist(data: &[u8], indent: usize) -> String { // XML plist data elements are always formatted by apple tools as // // AAAA..AA (68 characters per line) // // Allocate space for base 64 string and line endings up front const LINE_LEN: usize = 68; let mut line_ending = Vec::with_capacity(1 + indent); line_ending.push(b'\n'); (0..indent).for_each(|_| line_ending.push(b'\t')); // Find the max length of `data` encoded as a base 64 string with padding let base64_max_string_len = data.len() * 4 / 3 + 4; // Find the max length of the formatted base 64 string as: max length of the base 64 string // + line endings and indents at the start of the string and after every line let base64_max_string_len_with_formatting = base64_max_string_len + (2 + base64_max_string_len / LINE_LEN) * line_ending.len(); let mut output = vec![0; base64_max_string_len_with_formatting]; // Start output with a line ending and indent &mut output[..line_ending.len()].copy_from_slice(&line_ending); // Encode `data` as a base 64 string let base64_string_len = base64::encode_config_slice(data, base64::STANDARD, &mut output[line_ending.len()..]); // Line wrap the base 64 encoded string let line_wrap_len = line_wrap::line_wrap( &mut output[line_ending.len()..], base64_string_len, LINE_LEN, &line_wrap::SliceLineEnding::new(&line_ending), ); // Add the final line ending and indent &mut output[line_ending.len() + base64_string_len + line_wrap_len..][..line_ending.len()] .copy_from_slice(&line_ending); // Ensure output is the correct length output.truncate(base64_string_len + line_wrap_len + 2 * line_ending.len()); String::from_utf8(output).expect("base 64 string must be valid utf8") } #[cfg(test)] mod tests { use humantime::parse_rfc3339_weak; use std::io::Cursor; use super::*; use stream::Event::*; #[test] fn streaming_parser() { let plist = &[ 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()), DataValue((0..128).collect::>()), EndArray, StringValue("Death".to_owned()), IntegerValue(1564), StringValue("Height".to_owned()), RealValue(1.60), StringValue("Data".to_owned()), DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), StringValue("Birthdate".to_owned()), DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), StringValue("Comment".to_owned()), StringValue("2 < 3".to_owned()), // make sure characters are escaped EndDictionary, ]; let mut cursor = Cursor::new(Vec::new()); { let mut plist_w = XmlWriter::new(&mut cursor); for item in plist { plist_w.write(item).unwrap(); } } let comparison = " \tAuthor \tWilliam Shakespeare \tLines \t \t\tIt is a tale told by an idiot, \t\tFull of sound and fury, signifying nothing. \t\t \t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEy \t\tMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2Rl \t\tZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8= \t\t \t \tDeath \t1564 \tHeight \t1.6 \tData \t \tAAAAvgAAAAMAAAAeAAAA \t \tBirthdate \t1981-05-16T11:32:06Z \tComment \t2 < 3 "; let s = String::from_utf8(cursor.into_inner()).unwrap(); assert_eq!(s, comparison); } } plist-0.4.2/src/value.rs010064400007650000024000000360661347751654600133750ustar0000000000000000use std::collections::BTreeMap; use std::fs::File; use std::io::Write; use std::io::{BufReader, Read, Seek}; use std::path::Path; use stream::{Event, Reader, Writer, XmlReader, XmlWriter}; use {u64_to_usize, Date, Error}; /// Represents any plist value. #[derive(Clone, Debug, PartialEq)] pub enum Value { Array(Vec), Dictionary(BTreeMap), Boolean(bool), Data(Vec), Date(Date), Real(f64), Integer(i64), String(String), } impl Value { /// Reads a `Value` from a plist file of any encoding. pub fn from_file>(path: P) -> Result { let file = File::open(path)?; Value::from_reader(BufReader::new(file)) } /// Reads a `Value` from a seekable byte stream containing a plist file of any encoding. pub fn from_reader(reader: R) -> Result { let reader = Reader::new(reader); Value::from_events(reader) } /// Reads a `Value` from a seekable byte stream containing an XML encoded plist file. pub fn from_reader_xml(reader: R) -> Result { let reader = XmlReader::new(reader); Value::from_events(reader) } /// Serializes the given data structure as an XML encoded plist file. pub fn to_writer_xml(&self, writer: W) -> Result<(), Error> { let mut writer = XmlWriter::new(writer); self.to_writer_xml_inner(&mut writer) } fn to_writer_xml_inner(&self, writer: &mut Writer) -> Result<(), Error> { let events = self.clone().into_events(); for event in events { writer.write(&event)?; } Ok(()) } /// Creates a `Value` from an event source. pub fn from_events(events: T) -> Result where T: IntoIterator>, { Builder::new(events.into_iter()).build() } /// Converts a `Value` into an `Event` stream. 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 { Value::Array(array) => { events.push(Event::StartArray(Some(array.len() as u64))); for value in array { value.into_events_inner(events); } events.push(Event::EndArray); } Value::Dictionary(dict) => { events.push(Event::StartDictionary(Some(dict.len() as u64))); for (key, value) in dict { events.push(Event::StringValue(key)); value.into_events_inner(events); } events.push(Event::EndDictionary); } Value::Boolean(value) => events.push(Event::BooleanValue(value)), Value::Data(value) => events.push(Event::DataValue(value)), Value::Date(value) => events.push(Event::DateValue(value)), Value::Real(value) => events.push(Event::RealValue(value)), Value::Integer(value) => events.push(Event::IntegerValue(value)), Value::String(value) => events.push(Event::StringValue(value)), } } /// If the `Value` is an Array, returns the associated `Vec`. /// /// Returns `None` otherwise. pub fn as_array(&self) -> Option<&Vec> { match *self { Value::Array(ref array) => Some(array), _ => None, } } /// If the `Value` is an Array, returns the associated mutable `Vec`. /// /// Returns `None` otherwise. pub fn as_array_mut(&mut self) -> Option<&mut Vec> { match *self { Value::Array(ref mut array) => Some(array), _ => None, } } /// If the `Value` is a Dictionary, returns the associated `BTreeMap`. /// /// Returns `None` otherwise. pub fn as_dictionary(&self) -> Option<&BTreeMap> { match *self { Value::Dictionary(ref map) => Some(map), _ => None, } } /// If the `Value` is a Dictionary, returns the associated mutable `BTreeMap`. /// /// Returns `None` otherwise. pub fn as_dictionary_mut(&mut self) -> Option<&mut BTreeMap> { match *self { Value::Dictionary(ref mut map) => Some(map), _ => None, } } /// If the `Value` is a Boolean, returns the associated `bool`. /// /// Returns `None` otherwise. pub fn as_boolean(&self) -> Option { match *self { Value::Boolean(v) => Some(v), _ => None, } } /// If the `Value` is a Data, returns the underlying `Vec`. /// /// Returns `None` otherwise. /// /// This method consumes the `Value`. If this is not desired, please use /// `as_data` method. pub fn into_data(self) -> Option> { match self { Value::Data(data) => Some(data), _ => None, } } /// If the `Value` is a Data, returns the associated `Vec`. /// /// Returns `None` otherwise. pub fn as_data(&self) -> Option<&[u8]> { match *self { Value::Data(ref data) => Some(data), _ => None, } } /// If the `Value` is a Date, returns the associated `Date`. /// /// Returns `None` otherwise. pub fn as_date(&self) -> Option { match *self { Value::Date(date) => Some(date), _ => None, } } /// If the `Value` is a Real, returns the associated `f64`. /// /// Returns `None` otherwise. pub fn as_real(&self) -> Option { match *self { Value::Real(v) => Some(v), _ => None, } } /// If the `Value` is an Integer, returns the associated `i64`. /// /// Returns `None` otherwise. pub fn as_integer(&self) -> Option { match *self { Value::Integer(v) => Some(v), _ => None, } } /// If the `Value` is a String, returns the underlying `String`. /// /// Returns `None` otherwise. /// /// This method consumes the `Value`. If this is not desired, please use /// `as_string` method. pub fn into_string(self) -> Option { match self { Value::String(v) => Some(v), _ => None, } } /// If the `Value` is a String, returns the associated `str`. /// /// Returns `None` otherwise. pub fn as_string(&self) -> Option<&str> { match *self { Value::String(ref v) => Some(v), _ => None, } } } impl From> for Value { fn from(from: Vec) -> Value { Value::Array(from) } } impl From> for Value { fn from(from: BTreeMap) -> Value { Value::Dictionary(from) } } impl From for Value { fn from(from: bool) -> Value { Value::Boolean(from) } } impl<'a> From<&'a bool> for Value { fn from(from: &'a bool) -> Value { Value::Boolean(*from) } } impl From for Value { fn from(from: Date) -> Value { Value::Date(from) } } impl<'a> From<&'a Date> for Value { fn from(from: &'a Date) -> Value { Value::Date(*from) } } impl From for Value { fn from(from: f64) -> Value { Value::Real(from) } } impl From for Value { fn from(from: f32) -> Value { Value::Real(from.into()) } } impl From for Value { fn from(from: i64) -> Value { Value::Integer(from) } } impl From for Value { fn from(from: i32) -> Value { Value::Integer(from.into()) } } impl From for Value { fn from(from: i16) -> Value { Value::Integer(from.into()) } } impl From for Value { fn from(from: i8) -> Value { Value::Integer(from.into()) } } impl From for Value { fn from(from: u32) -> Value { Value::Integer(from.into()) } } impl From for Value { fn from(from: u16) -> Value { Value::Integer(from.into()) } } impl From for Value { fn from(from: u8) -> Value { Value::Integer(from.into()) } } impl<'a> From<&'a f64> for Value { fn from(from: &'a f64) -> Value { Value::Real(*from) } } impl<'a> From<&'a f32> for Value { fn from(from: &'a f32) -> Value { Value::Real((*from).into()) } } impl<'a> From<&'a i64> for Value { fn from(from: &'a i64) -> Value { Value::Integer(*from) } } impl<'a> From<&'a i32> for Value { fn from(from: &'a i32) -> Value { Value::Integer((*from).into()) } } impl<'a> From<&'a i16> for Value { fn from(from: &'a i16) -> Value { Value::Integer((*from).into()) } } impl<'a> From<&'a i8> for Value { fn from(from: &'a i8) -> Value { Value::Integer((*from).into()) } } impl<'a> From<&'a u32> for Value { fn from(from: &'a u32) -> Value { Value::Integer((*from).into()) } } impl<'a> From<&'a u16> for Value { fn from(from: &'a u16) -> Value { Value::Integer((*from).into()) } } impl<'a> From<&'a u8> for Value { fn from(from: &'a u8) -> Value { Value::Integer((*from).into()) } } impl From for Value { fn from(from: String) -> Value { Value::String(from) } } impl<'a> From<&'a str> for Value { fn from(from: &'a str) -> Value { Value::String(from.into()) } } struct Builder { stream: T, token: Option, } impl>> Builder { fn new(stream: T) -> Builder { Builder { stream, token: None, } } fn build(mut self) -> Result { self.bump()?; let plist = self.build_value()?; // Ensure the stream has been fully consumed self.bump()?; match self.token { None => Ok(plist), _ => Err(Error::InvalidData), } } fn bump(&mut self) -> Result<(), Error> { 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(Event::StartArray(len)) => Ok(Value::Array(self.build_array(len)?)), Some(Event::StartDictionary(len)) => Ok(Value::Dictionary(self.build_dict(len)?)), Some(Event::BooleanValue(b)) => Ok(Value::Boolean(b)), Some(Event::DataValue(d)) => Ok(Value::Data(d)), Some(Event::DateValue(d)) => Ok(Value::Date(d)), Some(Event::IntegerValue(i)) => Ok(Value::Integer(i)), Some(Event::RealValue(f)) => Ok(Value::Real(f)), Some(Event::StringValue(s)) => Ok(Value::String(s)), Some(Event::EndArray) => Err(Error::InvalidData), Some(Event::EndDictionary) => Err(Error::InvalidData), // The stream should not have ended here None => Err(Error::InvalidData), } } fn build_array(&mut self, len: Option) -> Result, Error> { let mut values = match len.and_then(u64_to_usize) { Some(len) => Vec::with_capacity(len), None => Vec::new(), }; loop { self.bump()?; if let Some(Event::EndArray) = self.token { self.token.take(); return Ok(values); } values.push(self.build_value()?); } } fn build_dict(&mut self, _len: Option) -> Result, Error> { let mut values = BTreeMap::new(); loop { self.bump()?; match self.token.take() { Some(Event::EndDictionary) => return Ok(values), Some(Event::StringValue(s)) => { self.bump()?; values.insert(s, self.build_value()?); } _ => { // Only string keys are supported in plists return Err(Error::InvalidData); } } } } } #[cfg(test)] mod tests { use std::collections::BTreeMap; use std::time::SystemTime; use super::*; use stream::Event::*; use {Date, Value}; #[test] fn value_accessors() { let vec = vec![Value::Real(0.0)]; let mut array = Value::Array(vec.clone()); assert_eq!(array.as_array(), Some(&vec.clone())); assert_eq!(array.as_array_mut(), Some(&mut vec.clone())); let mut map = BTreeMap::new(); map.insert("key1".to_owned(), Value::String("value1".to_owned())); let mut dict = Value::Dictionary(map.clone()); assert_eq!(dict.as_dictionary(), Some(&map.clone())); assert_eq!(dict.as_dictionary_mut(), Some(&mut map.clone())); assert_eq!(Value::Boolean(true).as_boolean(), Some(true)); let slice: &[u8] = &[1, 2, 3]; assert_eq!(Value::Data(slice.to_vec()).as_data(), Some(slice)); assert_eq!( Value::Data(slice.to_vec()).into_data(), Some(slice.to_vec()) ); let date: Date = SystemTime::now().into(); assert_eq!(Value::Date(date.clone()).as_date(), Some(date)); assert_eq!(Value::Real(0.0).as_real(), Some(0.0)); assert_eq!(Value::Integer(1).as_integer(), Some(1)); assert_eq!(Value::String("2".to_owned()).as_string(), Some("2")); assert_eq!( Value::String("t".to_owned()).into_string(), Some("t".to_owned()) ); } #[test] fn builder() { // 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(Value::String("It is a tale told by an idiot,".to_owned())); lines.push(Value::String( "Full of sound and fury, signifying nothing.".to_owned(), )); let mut dict = BTreeMap::new(); dict.insert( "Author".to_owned(), Value::String("William Shakespeare".to_owned()), ); dict.insert("Lines".to_owned(), Value::Array(lines)); dict.insert("Birthdate".to_owned(), Value::Integer(1564)); dict.insert("Height".to_owned(), Value::Real(1.60)); assert_eq!(plist.unwrap(), Value::Dictionary(dict)); } } plist-0.4.2/tests/data/binary.plist010064400007650000024000000003741347751654600155310ustar0000000000000000bplist00 ULinesUDeathVHeightYBirthdateVAuthorTData _It is a tale told by an idiot,_+Full of sound and fury, signifying nothing.#?3ve_William ShakespeareO!(29>Abplist-0.4.2/tests/data/binary_circular_array.plist010064400007650000024000000007031310304000700205510ustar0000000000000000bplist00 LinesUDeathVHeightYBirthdateVAuthorTData _It is a tale Tolda ateVAuthorTData _>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>It is a tale Tolda y bn idiot,_+Full of sound and fury, signif sound and fury, signifying nthng.#?6v!eareOm ShakespeareO!(28>Abplist-0.4.2/tests/data/binary_zero_offset_size.plist010064400007650000024000000004241310303546700211440ustar0000000000000000bplist00 ULinesUDeathVHeightYBirthdate signifying nthing.#?#?3ve_William ShakespeareO!(29>Abte signifying nthing.#?#?3ve_William ShakespeareO!(plist-0.4.2/tests/data/utf16_bplist.plist010064400007650000024000000025421276362066400165600ustar0000000000000000bplist00TnameXlongTextk& or betteroThe sun was shining on the sea, Shining with all his might: He did his very best to make The billows smooth and bright -- And this was odd, because it was The middle of the night. The moon was shining sulkily, Because she thought the sun Had got no business to be there After the day was done -- "It's very rude of him," she said, "To come and spoil the fun!" The sea was wet as wet could be, The sands were dry as dry. You could not see a cloud, because No cloud was in the sky: No birds were flying overhead -- There were no birds to fly. In a Wonderland they lie Dreaming as the days go by, Dreaming as the summer die. & 28plist-0.4.2/tests/data/xml.plist010064400007650000024000000012701347751707600150400ustar0000000000000000 Author William Shakespeare Lines It is a tale told by an idiot, Full of sound and fury, signifying nothing. Death 1564 Height 1.6 Data AAAAvgAAAA MAAAAeAAAA Birthdate 1981-05-16T11:32:06Z Blank HexademicalNumber 0xDEADBEEF plist-0.4.2/tests/data/xml_error.plist010064400007650000024000000007411260060366100162320ustar0000000000000000 Author William Shakespeare Lines It is a tale told by an idiot, Full of sound and fury, signifying nothing. Death 1564 Height 1.6 DataAb\x90\x93\x9c\xa5\xbb\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcd"; test_fuzzer_data_err(data); } #[test] fn binary_zero_offset_size() { let data = include_bytes!("data/binary_zero_offset_size.plist"); test_fuzzer_data_err(data); } #[test] fn binary_nan_date() { let data = b"bplist00\xd6\x01\x02\x01\x04\x05\x06\x07\x0a\x0b\x0c\x0d\x0eULinesUDeathVHeightYBthridateVAuthorTData\xa2\x08\x09_\x10\x1eIt is a tale told by an idiot,_\x10+Full of sound and fury, signifying nothing.\x11\x06\x1c#?\xf9\x99\x99\x99\x99\x99\x9a3\xff\xff\xff\xffe\x00\x00\x00_\x13\x10William ShakespeareO\x10\xe5\x00\x00\x00\xbe\x00\x00\x00\x03\x00\x00\x00\x1e\x00\x00\x00\x08\x15\x1b!(14>Ab\x90\x93\x9c\xa5\xbb\xd4\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcd"; test_fuzzer_data_err(data); } #[test] fn binary_circular_array() { let data = include_bytes!("data/binary_circular_array.plist"); test_fuzzer_data_err(data); } // Issue 20 - not found by fuzzing but this is a convenient place to put the test. #[test] fn issue_20_binary_with_data_in_trailer() { let data = b"bplist00\xd0\x08\0\0\0\0\0\0\x01\x01\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t"; test_fuzzer_data_ok(data); } #[test] fn issue_22_binary_with_byte_ref_size() { let data = b"bplist00\xd1\x01\x02TTestQ1\x08\x0b\x10\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12"; test_fuzzer_data_ok(data); } fn test_fuzzer_data(data: &[u8]) -> Result { let cursor = Cursor::new(data); Value::from_reader(cursor) } fn test_fuzzer_data_ok(data: &[u8]) { assert!(test_fuzzer_data(data).is_ok()); } fn test_fuzzer_data_err(data: &[u8]) { assert!(test_fuzzer_data(data).is_err()); } plist-0.4.2/tests/serde_tests/mod.rs010064400007650000024000000142711347751654600157310ustar0000000000000000use plist::stream::Event; use plist::stream::Event::*; use plist::stream::Writer; use plist::{Date, Deserializer, Error, Serializer}; use serde::de::DeserializeOwned; use serde::ser::Serialize; use std::fmt::Debug; use std::time::SystemTime; struct VecWriter { events: Vec, } impl VecWriter { pub fn new() -> VecWriter { VecWriter { events: Vec::new() } } pub fn into_inner(self) -> Vec { self.events } } impl Writer for VecWriter { fn write(&mut self, event: &Event) -> Result<(), Error> { self.events.push(event.clone()); Ok(()) } } fn new_serializer() -> Serializer { Serializer::new(VecWriter::new()) } fn new_deserializer(events: Vec) -> Deserializer>> { let result_events = events.into_iter().map(Ok).collect(); Deserializer::new(result_events) } fn assert_roundtrip(obj: T, comparison: Option<&[Event]>) where T: Debug + DeserializeOwned + PartialEq + Serialize, { let mut se = new_serializer(); obj.serialize(&mut se).unwrap(); let events = se.into_inner().into_inner(); if let Some(comparison) = comparison { assert_eq!(&events[..], comparison); } let mut de = new_deserializer(events); let new_obj = T::deserialize(&mut de).unwrap(); assert_eq!(new_obj, obj); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] enum Animal { Cow, Dog(DogOuter), Frog(Result, Vec), Cat { age: usize, name: String, firmware: Option>, }, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct DogOuter { inner: Vec, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct DogInner { a: (), b: usize, c: Vec, } #[test] fn cow() { let cow = Animal::Cow; let comparison = &[ StartDictionary(Some(1)), StringValue("Cow".to_owned()), StringValue("".to_owned()), EndDictionary, ]; assert_roundtrip(cow, Some(comparison)); } #[test] fn dog() { let dog = Animal::Dog(DogOuter { inner: vec![DogInner { a: (), b: 12, c: vec!["a".to_string(), "b".to_string()], }], }); let comparison = &[ StartDictionary(Some(1)), StringValue("Dog".to_owned()), StartDictionary(None), StringValue("inner".to_owned()), StartArray(Some(1)), StartDictionary(None), StringValue("a".to_owned()), StringValue("".to_owned()), StringValue("b".to_owned()), IntegerValue(12), StringValue("c".to_owned()), StartArray(Some(2)), StringValue("a".to_owned()), StringValue("b".to_owned()), EndArray, EndDictionary, EndArray, EndDictionary, EndDictionary, ]; assert_roundtrip(dog, Some(comparison)); } #[test] fn frog() { let frog = Animal::Frog( Ok("hello".to_owned()), vec![1.0, 2.0, 3.14159, 0.000000001, 1.27e31], ); let comparison = &[ StartDictionary(Some(1)), StringValue("Frog".to_owned()), StartArray(Some(2)), StartDictionary(Some(1)), StringValue("Ok".to_owned()), StringValue("hello".to_owned()), EndDictionary, StartArray(Some(5)), RealValue(1.0), RealValue(2.0), RealValue(3.14159), RealValue(0.000000001), RealValue(1.27e31), EndArray, EndArray, EndDictionary, ]; assert_roundtrip(frog, Some(comparison)); } #[test] fn cat() { let cat = Animal::Cat { age: 12, name: "Paws".to_owned(), firmware: Some(vec![0, 1, 2, 3, 4, 5, 6, 7, 8]), }; let comparison = &[ StartDictionary(Some(1)), StringValue("Cat".to_owned()), StartDictionary(None), StringValue("age".to_owned()), IntegerValue(12), StringValue("name".to_owned()), StringValue("Paws".to_owned()), StringValue("firmware".to_owned()), StartArray(Some(9)), IntegerValue(0), IntegerValue(1), IntegerValue(2), IntegerValue(3), IntegerValue(4), IntegerValue(5), IntegerValue(6), IntegerValue(7), IntegerValue(8), EndArray, EndDictionary, EndDictionary, ]; assert_roundtrip(cat, Some(comparison)); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct NewtypeStruct(NewtypeInner); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct NewtypeInner(u8, u8, u8); #[test] fn newtype_struct() { let newtype = NewtypeStruct(NewtypeInner(34, 32, 13)); let comparison = &[ StartArray(Some(3)), IntegerValue(34), IntegerValue(32), IntegerValue(13), EndArray, ]; assert_roundtrip(newtype, Some(comparison)); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct TypeWithOptions { a: Option, b: Option, c: Option>, } #[test] fn type_with_options() { let inner = TypeWithOptions { a: None, b: Some(12), c: None, }; let obj = TypeWithOptions { a: Some("hello".to_owned()), b: None, c: Some(Box::new(inner)), }; let comparison = &[ StartDictionary(None), StringValue("a".to_owned()), StringValue("hello".to_owned()), StringValue("c".to_owned()), StartDictionary(None), StringValue("b".to_owned()), IntegerValue(12), EndDictionary, EndDictionary, ]; assert_roundtrip(obj, Some(comparison)); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct TypeWithDate { a: Option, b: Option, } #[test] fn type_with_date() { let date: Date = SystemTime::now().into(); let obj = TypeWithDate { a: Some(28), b: Some(date.clone()), }; let comparison = &[ StartDictionary(None), StringValue("a".to_owned()), IntegerValue(28), StringValue("b".to_owned()), DateValue(date), EndDictionary, ]; assert_roundtrip(obj, Some(comparison)); } plist-0.4.2/tests/tests.rs010064400007650000024000000002611335667663300137620ustar0000000000000000extern crate plist; #[cfg(feature = "serde")] extern crate serde; #[cfg(feature = "serde")] #[macro_use] extern crate serde_derive; #[cfg(feature = "serde")] mod serde_tests; plist-0.4.2/.cargo_vcs_info.json0000644000000001120000000000000122230ustar00{ "git": { "sha1": "214b2438ee8fa58786ef980d55a80fef73f7021b" } }