plist-1.0.0/.editorconfig010064400007650000024000000004311263624407700135500ustar0000000000000000# 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-1.0.0/.gitignore010064400007650000024000000000301361166352100130470ustar0000000000000000target Cargo.lock .idea plist-1.0.0/.travis.yml010064400007650000024000000011011364656204000131720ustar0000000000000000os: - linux - mac - windows sudo: false language: rust rust: - nightly - beta - stable - 1.34.0 matrix: include: - os: linux rust: stable-i686-unknown-linux-gnu addons: apt: packages: - gcc-multilib script: - cargo check --no-default-features - cargo check --no-default-features --features enable_unstable_features_that_may_break_with_minor_version_bumps - cargo check --no-default-features --features serde - cargo check --all-features - cargo test --all-features - cargo test --release --all-features plist-1.0.0/Cargo.toml.orig010064400007650000024000000014401364656174200137660ustar0000000000000000[package] name = "plist" version = "1.0.0" 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/1.0.0/plist/" keywords = ["plist", "parser"] categories = ["config", "encoding", "parser-implementations"] edition = "2018" [features] default = ["serde"] enable_unstable_features_that_may_break_with_minor_version_bumps = [] [dependencies] base64 = "0.12.0" chrono = { version = "0.4.11", default-features = false, features = ["std"] } indexmap = "1.0.2" line-wrap = "0.1.1" xml_rs = { package = "xml-rs", version = "0.8.0" } serde = { version = "1.0.2", optional = true } [dev-dependencies] serde_derive = { version = "1.0.2" } plist-1.0.0/Cargo.toml0000644000000025671364657031000103000ustar00# 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] edition = "2018" name = "plist" version = "1.0.0" authors = ["Ed Barnard "] description = "A rusty plist parser. Supports Serde serialization." documentation = "https://docs.rs/plist/1.0.0/plist/" keywords = ["plist", "parser"] categories = ["config", "encoding", "parser-implementations"] license = "MIT" repository = "https://github.com/ebarnard/rust-plist/" [dependencies.base64] version = "0.12.0" [dependencies.chrono] version = "0.4.11" features = ["std"] default-features = false [dependencies.indexmap] version = "1.0.2" [dependencies.line-wrap] version = "0.1.1" [dependencies.serde] version = "1.0.2" optional = true [dependencies.xml_rs] version = "0.8.0" package = "xml-rs" [dev-dependencies.serde_derive] version = "1.0.2" [features] default = ["serde"] enable_unstable_features_that_may_break_with_minor_version_bumps = [] plist-1.0.0/LICENCE010064400007650000024000000020411260060366100120440ustar0000000000000000Copyright (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-1.0.0/README.md010064400007650000024000000011141351012552500123350ustar0000000000000000# Plist A rusty plist parser. Many features from previous versions are now hidden behind the `enable_unstable_features_that_may_break_with_minor_version_bumps` feature. These will break in minor version releases after the 1.0 release. If you really really must use them you should specify a tilde requirement e.g. `plist = "~1.0.3"` in you `Cargo.toml` so that the plist crate is not automatically updated to version 1.1. [![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-1.0.0/rustfmt.toml010064400007650000024000000000251350673612000134630ustar0000000000000000merge_imports = true plist-1.0.0/src/date.rs010064400007650000024000000117741364656172300131630ustar0000000000000000use chrono::{DateTime, FixedOffset, SecondsFormat, Utc}; use std::{ fmt, time::{Duration, SystemTime, UNIX_EPOCH}, }; /// A UTC timestamp used for serialization to and from the plist date type. /// /// Note that while this type implements `Serialize` and `Deserialize` it will behave strangely if /// used with serializers from outside this crate. #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct Date { inner: SystemTime, } pub(crate) struct InfiniteOrNanDate; impl Date { /// The unix timestamp of the plist epoch. const PLIST_EPOCH_UNIX_TIMESTAMP: Duration = Duration::from_secs(978_307_200); pub(crate) fn from_rfc3339(date: &str) -> Result { let offset: DateTime = DateTime::parse_from_rfc3339(date).map_err(|_| ())?; Ok(Date { inner: offset.with_timezone(&Utc).into(), }) } pub(crate) fn to_rfc3339(&self) -> String { let datetime: DateTime = self.inner.into(); datetime.to_rfc3339_opts(SecondsFormat::Secs, true) } 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. let plist_epoch = UNIX_EPOCH + Date::PLIST_EPOCH_UNIX_TIMESTAMP; if !timestamp.is_finite() { return Err(InfiniteOrNanDate); } 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 }) } pub(crate) fn to_seconds_since_plist_epoch(&self) -> f64 { // needed until #![feature(duration_float)] is stabilized fn as_secs_f64(d: Duration) -> f64 { const NANOS_PER_SEC: f64 = 1_000_000_000.00; (d.as_secs() as f64) + f64::from(d.subsec_nanos()) / NANOS_PER_SEC } let plist_epoch = UNIX_EPOCH + Date::PLIST_EPOCH_UNIX_TIMESTAMP; match self.inner.duration_since(plist_epoch) { Ok(dur_since_plist_epoch) => as_secs_f64(dur_since_plist_epoch), Err(err) => -as_secs_f64(err.duration()), } } } impl fmt::Debug for Date { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{}", self.to_rfc3339()) } } 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}, ser::{Serialize, Serializer}, }; use std::fmt; use crate::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) } } } #[cfg(test)] mod testing { use super::*; #[test] fn date_roundtrip() { let date_str = "1981-05-16T11:32:06Z"; let date = Date::from_rfc3339(date_str).expect("should parse"); let generated_str = date.to_rfc3339(); assert_eq!(date_str, generated_str); } #[test] fn far_past_date() { let date_str = "1920-01-01T00:00:00Z"; Date::from_rfc3339(date_str).expect("should parse"); } } plist-1.0.0/src/de.rs010064400007650000024000000261651361166045400126270ustar0000000000000000use serde::de; use std::{ fmt::Display, fs::File, io::{BufReader, Cursor, Read, Seek}, iter::Peekable, mem, path::Path, }; use crate::{ error::{self, Error, ErrorKind, EventKind}, stream::{self, Event}, u64_to_usize, }; macro_rules! expect { ($next:expr, $kind:expr) => { match $next { Some(Ok(ref event)) if EventKind::of_event(event) != $kind => { return Err(error::unexpected_event_type($kind, event))?; } Some(Ok(event)) => event, Some(Err(err)) => return Err(err), None => return Err(ErrorKind::UnexpectedEndOfEventStream.without_position()), } }; } macro_rules! try_next { ($next:expr) => { match $next { Some(Ok(event)) => event, Some(Err(err)) => return Err(err)?, None => return Err(ErrorKind::UnexpectedEndOfEventStream.without_position())?, } }; } #[doc(hidden)] impl de::Error for Error { fn custom(msg: T) -> Self { ErrorKind::Serde(msg.to_string()).without_position() } } enum OptionMode { Root, StructField, Explicit, } /// A structure that deserializes plist event streams into Rust values. pub struct Deserializer where I: IntoIterator>, { events: Peekable<::IntoIter>, option_mode: OptionMode, } impl Deserializer where I: IntoIterator>, { pub fn new(iter: I) -> Deserializer { Deserializer { events: iter.into_iter().peekable(), option_mode: OptionMode::Root, } } fn with_option_mode) -> Result>( &mut self, option_mode: OptionMode, f: F, ) -> Result { let prev_option_mode = mem::replace(&mut self.option_mode, option_mode); let ret = f(&mut *self); self.option_mode = prev_option_mode; ret } } 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(), EventKind::EndCollection); Ok(ret) } 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(), EventKind::EndCollection); Ok(ret) } event @ Event::EndCollection => Err(error::unexpected_event_type( EventKind::ValueOrStartCollection, &event, )), Event::Boolean(v) => visitor.visit_bool(v), Event::Data(v) => visitor.visit_byte_buf(v), Event::Date(v) => visitor.visit_string(v.to_rfc3339()), Event::Integer(v) => { if let Some(v) = v.as_unsigned() { visitor.visit_u64(v) } else if let Some(v) = v.as_signed() { visitor.visit_i64(v) } else { unreachable!() } } Event::Real(v) => visitor.visit_f64(v), Event::String(v) => visitor.visit_string(v), Event::Uid(v) => visitor.visit_u64(v.get()), Event::__Nonexhaustive => unreachable!(), } } 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(), EventKind::String); visitor.visit_unit() } fn deserialize_option(self, visitor: V) -> Result where V: de::Visitor<'de>, { match self.option_mode { OptionMode::Root => { if self.events.peek().is_none() { visitor.visit_none::() } else { self.with_option_mode(OptionMode::Explicit, |this| visitor.visit_some(this)) } } OptionMode::StructField => { // None struct values are ignored so if we're here the value must be Some. self.with_option_mode(OptionMode::Explicit, |this| Ok(visitor.visit_some(this)?)) } OptionMode::Explicit => { expect!(self.events.next(), EventKind::StartDictionary); let ret = match try_next!(self.events.next()) { Event::String(ref s) if &s[..] == "None" => { expect!(self.events.next(), EventKind::String); visitor.visit_none::()? } Event::String(ref s) if &s[..] == "Some" => visitor.visit_some(&mut *self)?, event => return Err(error::unexpected_event_type(EventKind::String, &event))?, }; expect!(self.events.next(), EventKind::EndCollection); 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(), EventKind::StartDictionary); let ret = visitor.visit_map(MapAndSeqAccess::new(self, true, None))?; expect!(self.events.next(), EventKind::EndCollection); 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(), EventKind::StartDictionary); let ret = visitor.visit_enum(&mut *self)?; expect!(self.events.next(), EventKind::EndCollection); 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), 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<(), Error> { 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>, { de::Deserializer::deserialize_tuple(self, len, visitor) } fn struct_variant( self, fields: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { let name = ""; de::Deserializer::deserialize_struct(self, name, fields, 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, Error> where T: de::DeserializeSeed<'de>, { if let Some(&Ok(Event::EndCollection)) = self.de.events.peek() { return Ok(None); } self.remaining = self.remaining.map(|r| r.saturating_sub(1)); self.de .with_option_mode(OptionMode::Explicit, |this| seed.deserialize(this)) .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, Error> where K: de::DeserializeSeed<'de>, { if let Some(&Ok(Event::EndCollection)) = self.de.events.peek() { return Ok(None); } self.remaining = self.remaining.map(|r| r.saturating_sub(1)); self.de .with_option_mode(OptionMode::Explicit, |this| seed.deserialize(this)) .map(Some) } fn next_value_seed(&mut self, seed: V) -> Result where V: de::DeserializeSeed<'de>, { let option_mode = if self.is_struct { OptionMode::StructField } else { OptionMode::Explicit }; self.de .with_option_mode(option_mode, |this| Ok(seed.deserialize(this)?)) } fn size_hint(&self) -> Option { self.remaining } } /// Deserializes an instance of type `T` from a byte slice. pub fn from_bytes(bytes: &[u8]) -> Result { let cursor = Cursor::new(bytes); from_reader(cursor) } /// 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).map_err(error::from_io_without_position)?; from_reader(BufReader::new(file)) } /// Deserializes an instance of type `T` from a seekable byte stream containing a plist 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. 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-1.0.0/src/dictionary.rs010064400007650000024000000444131364655506700144130ustar0000000000000000//! A map of String to plist::Value. //! //! The map is currently backed by an [`IndexMap`]. This may be changed in a future minor release. //! //! [`IndexMap`]: https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html //use serde::{de, ser}; use indexmap::{map, IndexMap}; use std::{ fmt::{self, Debug}, iter::FromIterator, ops, }; use crate::Value; /// Represents a plist dictionary type. pub struct Dictionary { map: IndexMap, } impl Dictionary { /// Makes a new empty `Dictionary`. #[inline] pub fn new() -> Self { Dictionary { map: IndexMap::new(), } } /// Clears the dictionary, removing all values. #[inline] pub fn clear(&mut self) { self.map.clear() } /// Returns a reference to the value corresponding to the key. #[inline] pub fn get(&self, key: &str) -> Option<&Value> { self.map.get(key) } /// Returns true if the dictionary contains a value for the specified key. #[inline] pub fn contains_key(&self, key: &str) -> bool { self.map.contains_key(key) } /// Returns a mutable reference to the value corresponding to the key. #[inline] pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> { self.map.get_mut(key) } /// Inserts a key-value pair into the dictionary. /// /// If the dictionary did not have this key present, `None` is returned. /// /// If the dictionary did have this key present, the value is updated, and the old value is /// returned. #[inline] pub fn insert(&mut self, k: String, v: Value) -> Option { self.map.insert(k, v) } /// Removes a key from the dictionary, returning the value at the key if the key was previously /// in the dictionary. #[inline] pub fn remove(&mut self, key: &str) -> Option { self.map.remove(key) } /// Gets the given key's corresponding entry in the dictionary for in-place manipulation. // Entry functionality is unstable until I can figure out how to use either Cow or // T: AsRef + Into #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] pub fn entry(&mut self, key: S) -> Entry where S: Into, { match self.map.entry(key.into()) { map::Entry::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant }), map::Entry::Occupied(occupied) => Entry::Occupied(OccupiedEntry { occupied }), } } /// Returns the number of elements in the dictionary. #[inline] pub fn len(&self) -> usize { self.map.len() } /// Returns true if the dictionary contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.map.is_empty() } /// Gets an iterator over the entries of the dictionary. #[inline] pub fn iter(&self) -> Iter { Iter { iter: self.map.iter(), } } /// Gets a mutable iterator over the entries of the dictionary. #[inline] pub fn iter_mut(&mut self) -> IterMut { IterMut { iter: self.map.iter_mut(), } } /// Gets an iterator over the keys of the dictionary. #[inline] pub fn keys(&self) -> Keys { Keys { iter: self.map.keys(), } } /// Gets an iterator over the values of the dictionary. #[inline] pub fn values(&self) -> Values { Values { iter: self.map.values(), } } /// Gets an iterator over mutable values of the dictionary. #[inline] pub fn values_mut(&mut self) -> ValuesMut { ValuesMut { iter: self.map.values_mut(), } } } impl Default for Dictionary { #[inline] fn default() -> Self { Dictionary { map: Default::default(), } } } impl Clone for Dictionary { #[inline] fn clone(&self) -> Self { Dictionary { map: self.map.clone(), } } } impl PartialEq for Dictionary { #[inline] fn eq(&self, other: &Self) -> bool { self.map.eq(&other.map) } } /// Access an element of this dictionary. Panics if the given key is not present in the dictionary. /// /// ``` /// # use plist::Value; /// # /// # let val = &Value::String("".to_owned()); /// # let _ = /// match *val { /// Value::Array(ref arr) => arr[0].as_string(), /// Value::Dictionary(ref dict) => dict["type"].as_string(), /// Value::String(ref s) => Some(s.as_str()), /// _ => None, /// } /// # ; /// ``` impl<'a> ops::Index<&'a str> for Dictionary { type Output = Value; fn index(&self, index: &str) -> &Value { self.map.index(index) } } /// Mutably access an element of this dictionary. Panics if the given key is not present in the /// dictionary. /// /// ``` /// # let mut dict = plist::Dictionary::new(); /// # dict.insert("key".to_owned(), plist::Value::Boolean(false)); /// # /// dict["key"] = "value".into(); /// ``` impl<'a> ops::IndexMut<&'a str> for Dictionary { fn index_mut(&mut self, index: &str) -> &mut Value { self.map.get_mut(index).expect("no entry found for key") } } impl Debug for Dictionary { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.map.fmt(formatter) } } /*impl ser::Serialize for Dictionary { #[inline] fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { use serde::ser::SerializeMap; let mut map = try!(serializer.serialize_map(Some(self.len()))); for (k, v) in self { try!(map.serialize_key(k)); try!(map.serialize_value(v)); } map.end() } } impl<'de> de::Deserialize<'de> for Dictionary { #[inline] fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Dictionary; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map") } #[inline] fn visit_unit(self) -> Result where E: de::Error, { Ok(Dictionary::new()) } #[inline] fn visit_map(self, mut visitor: V) -> Result where V: de::MapAccess<'de>, { let mut values = Dictionary::new(); while let Some((key, value)) = try!(visitor.next_entry()) { values.insert(key, value); } Ok(values) } } deserializer.deserialize_map(Visitor) } }*/ impl FromIterator<(String, Value)> for Dictionary { fn from_iter(iter: T) -> Self where T: IntoIterator, { Dictionary { map: FromIterator::from_iter(iter), } } } impl Extend<(String, Value)> for Dictionary { fn extend(&mut self, iter: T) where T: IntoIterator, { self.map.extend(iter); } } macro_rules! delegate_iterator { (($name:ident $($generics:tt)*) => $item:ty) => { impl $($generics)* Iterator for $name $($generics)* { type Item = $item; #[inline] fn next(&mut self) -> Option { self.iter.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } /*impl $($generics)* DoubleEndedIterator for $name $($generics)* { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back() } }*/ impl $($generics)* ExactSizeIterator for $name $($generics)* { #[inline] fn len(&self) -> usize { self.iter.len() } } } } ////////////////////////////////////////////////////////////////////////////// /// A view into a single entry in a dictionary, which may either be vacant or occupied. /// This enum is constructed from the [`entry`] method on [`Dictionary`]. /// /// [`entry`]: struct.Dictionary.html#method.entry /// [`Dictionary`]: struct.Dictionary.html #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] pub enum Entry<'a> { /// A vacant Entry. Vacant(VacantEntry<'a>), /// An occupied Entry. Occupied(OccupiedEntry<'a>), } /// A vacant Entry. It is part of the [`Entry`] enum. /// /// [`Entry`]: enum.Entry.html #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] pub struct VacantEntry<'a> { vacant: map::VacantEntry<'a, String, Value>, } /// An occupied Entry. It is part of the [`Entry`] enum. /// /// [`Entry`]: enum.Entry.html #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] pub struct OccupiedEntry<'a> { occupied: map::OccupiedEntry<'a, String, Value>, } #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] impl<'a> Entry<'a> { /// Returns a reference to this entry's key. /// /// # Examples /// /// ``` /// let mut dict = plist::Dictionary::new(); /// assert_eq!(dict.entry("serde").key(), &"serde"); /// ``` pub fn key(&self) -> &String { match *self { Entry::Vacant(ref e) => e.key(), Entry::Occupied(ref e) => e.key(), } } /// Ensures a value is in the entry by inserting the default if empty, and returns a mutable /// reference to the value in the entry. /// /// # Examples /// /// ``` /// let mut dict = plist::Dictionary::new(); /// dict.entry("serde").or_insert(12.into()); /// /// assert_eq!(dict["serde"], 12.into()); /// ``` pub fn or_insert(self, default: Value) -> &'a mut Value { match self { Entry::Vacant(entry) => entry.insert(default), Entry::Occupied(entry) => entry.into_mut(), } } /// Ensures a value is in the entry by inserting the result of the default function if empty, /// and returns a mutable reference to the value in the entry. /// /// # Examples /// /// ``` /// let mut dict = plist::Dictionary::new(); /// dict.entry("serde").or_insert_with(|| "hoho".into()); /// /// assert_eq!(dict["serde"], "hoho".into()); /// ``` pub fn or_insert_with(self, default: F) -> &'a mut Value where F: FnOnce() -> Value, { match self { Entry::Vacant(entry) => entry.insert(default()), Entry::Occupied(entry) => entry.into_mut(), } } } #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] impl<'a> VacantEntry<'a> { /// Gets a reference to the key that would be used when inserting a value through the /// VacantEntry. /// /// # Examples /// /// ``` /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// /// match dict.entry("serde") { /// Entry::Vacant(vacant) => assert_eq!(vacant.key(), &"serde"), /// Entry::Occupied(_) => unimplemented!(), /// } /// ``` #[inline] pub fn key(&self) -> &String { self.vacant.key() } /// Sets the value of the entry with the VacantEntry's key, and returns a mutable reference /// to it. /// /// # Examples /// /// ``` /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// /// match dict.entry("serde") { /// Entry::Vacant(vacant) => vacant.insert("hoho".into()), /// Entry::Occupied(_) => unimplemented!(), /// }; /// ``` #[inline] pub fn insert(self, value: Value) -> &'a mut Value { self.vacant.insert(value) } } #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] impl<'a> OccupiedEntry<'a> { /// Gets a reference to the key in the entry. /// /// # Examples /// /// ``` /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), 12.into()); /// /// match dict.entry("serde") { /// Entry::Occupied(occupied) => assert_eq!(occupied.key(), &"serde"), /// Entry::Vacant(_) => unimplemented!(), /// } /// ``` #[inline] pub fn key(&self) -> &String { self.occupied.key() } /// Gets a reference to the value in the entry. /// /// # Examples /// /// ``` /// use plist::Value; /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), 12.into()); /// /// match dict.entry("serde") { /// Entry::Occupied(occupied) => assert_eq!(occupied.get(), &Value::from(12)), /// Entry::Vacant(_) => unimplemented!(), /// } /// ``` #[inline] pub fn get(&self) -> &Value { self.occupied.get() } /// Gets a mutable reference to the value in the entry. /// /// # Examples /// /// ``` /// use plist::Value; /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), Value::Array(vec![1.into(), 2.into(), 3.into()])); /// /// match dict.entry("serde") { /// Entry::Occupied(mut occupied) => { /// occupied.get_mut().as_array_mut().unwrap().push(4.into()); /// } /// Entry::Vacant(_) => unimplemented!(), /// } /// /// assert_eq!(dict["serde"].as_array().unwrap().len(), 4); /// ``` #[inline] pub fn get_mut(&mut self) -> &mut Value { self.occupied.get_mut() } /// Converts the entry into a mutable reference to its value. /// /// # Examples /// /// ``` /// use plist::Value; /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), Value::Array(vec![1.into(), 2.into(), 3.into()])); /// /// match dict.entry("serde") { /// Entry::Occupied(mut occupied) => { /// occupied.into_mut().as_array_mut().unwrap().push(4.into()); /// } /// Entry::Vacant(_) => unimplemented!(), /// } /// /// assert_eq!(dict["serde"].as_array().unwrap().len(), 4); /// ``` #[inline] pub fn into_mut(self) -> &'a mut Value { self.occupied.into_mut() } /// Sets the value of the entry with the `OccupiedEntry`'s key, and returns /// the entry's old value. /// /// # Examples /// /// ``` /// use plist::Value; /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), 12.into()); /// /// match dict.entry("serde") { /// Entry::Occupied(mut occupied) => { /// assert_eq!(occupied.insert(13.into()), 12.into()); /// assert_eq!(occupied.get(), &Value::from(13)); /// } /// Entry::Vacant(_) => unimplemented!(), /// } /// ``` #[inline] pub fn insert(&mut self, value: Value) -> Value { self.occupied.insert(value) } /// Takes the value of the entry out of the dictionary, and returns it. /// /// # Examples /// /// ``` /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), 12.into()); /// /// match dict.entry("serde") { /// Entry::Occupied(occupied) => assert_eq!(occupied.remove(), 12.into()), /// Entry::Vacant(_) => unimplemented!(), /// } /// ``` #[inline] pub fn remove(self) -> Value { self.occupied.remove() } } ////////////////////////////////////////////////////////////////////////////// impl<'a> IntoIterator for &'a Dictionary { type Item = (&'a String, &'a Value); type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { Iter { iter: self.map.iter(), } } } /// An iterator over a plist::Dictionary's entries. pub struct Iter<'a> { iter: IterImpl<'a>, } type IterImpl<'a> = map::Iter<'a, String, Value>; delegate_iterator!((Iter<'a>) => (&'a String, &'a Value)); ////////////////////////////////////////////////////////////////////////////// impl<'a> IntoIterator for &'a mut Dictionary { type Item = (&'a String, &'a mut Value); type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { IterMut { iter: self.map.iter_mut(), } } } /// A mutable iterator over a plist::Dictionary's entries. pub struct IterMut<'a> { iter: map::IterMut<'a, String, Value>, } delegate_iterator!((IterMut<'a>) => (&'a String, &'a mut Value)); ////////////////////////////////////////////////////////////////////////////// impl IntoIterator for Dictionary { type Item = (String, Value); type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { iter: self.map.into_iter(), } } } /// An owning iterator over a plist::Dictionary's entries. pub struct IntoIter { iter: map::IntoIter, } delegate_iterator!((IntoIter) => (String, Value)); ////////////////////////////////////////////////////////////////////////////// /// An iterator over a plist::Dictionary's keys. pub struct Keys<'a> { iter: map::Keys<'a, String, Value>, } delegate_iterator!((Keys<'a>) => &'a String); ////////////////////////////////////////////////////////////////////////////// /// An iterator over a plist::Dictionary's values. pub struct Values<'a> { iter: map::Values<'a, String, Value>, } delegate_iterator!((Values<'a>) => &'a Value); ////////////////////////////////////////////////////////////////////////////// /// A mutable iterator over a plist::Dictionary's values. pub struct ValuesMut<'a> { iter: map::ValuesMut<'a, String, Value>, } delegate_iterator!((ValuesMut<'a>) => &'a mut Value); plist-1.0.0/src/error.rs010064400007650000024000000126761352034520000133560ustar0000000000000000use std::{error, fmt, io}; use crate::stream::Event; /// This type represents all possible errors that can occur when working with plist data. #[derive(Debug)] pub struct Error { inner: Box, } #[derive(Debug)] pub(crate) struct ErrorImpl { kind: ErrorKind, file_position: Option, } #[derive(Debug)] pub(crate) enum ErrorKind { UnexpectedEof, UnexpectedEndOfEventStream, UnexpectedEventType { expected: EventKind, found: EventKind, }, // Xml format-specific errors UnclosedXmlElement, UnpairedXmlClosingTag, UnexpectedXmlCharactersExpectedElement, UnexpectedXmlOpeningTag, UnknownXmlElement, InvalidXmlSyntax, InvalidXmlUtf8, InvalidDataString, InvalidDateString, InvalidIntegerString, InvalidRealString, UidNotSupportedInXmlPlist, // Binary format-specific errors ObjectTooLarge, InvalidMagic, InvalidTrailerObjectOffsetSize, // the size of byte offsets to objects in the object table InvalidTrailerObjectReferenceSize, // the size of indices into the object table InvalidObjectLength, ObjectReferenceTooLarge, ObjectOffsetTooLarge, RecursiveObject, NullObjectUnimplemented, FillObjectUnimplemented, IntegerOutOfRange, InfiniteOrNanDate, InvalidUtf8String, InvalidUtf16String, UnknownObjectType(u8), Io(io::Error), Serde(String), } #[derive(Debug)] pub(crate) enum FilePosition { LineColumn(u64, u64), Offset(u64), } #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(crate) enum EventKind { StartArray, StartDictionary, EndCollection, Boolean, Data, Date, Integer, Real, String, Uid, ValueOrStartCollection, DictionaryKeyOrEndCollection, } impl Error { /// Returns true if this error was caused by a failure to read or write bytes on an IO stream. pub fn is_io(&self) -> bool { self.as_io().is_some() } /// Returns true if this error was caused by prematurely reaching the end of the input data. pub fn is_eof(&self) -> bool { if let ErrorKind::UnexpectedEof = self.inner.kind { true } else { false } } /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO /// stream. pub fn as_io(&self) -> Option<&io::Error> { if let ErrorKind::Io(err) = &self.inner.kind { Some(err) } else { None } } /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO /// stream or `self` if it was not. pub fn into_io(self) -> Result { if let ErrorKind::Io(err) = self.inner.kind { Ok(err) } else { Err(self) } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self.inner.kind { ErrorKind::Io(err) => Some(err), _ => None, } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.inner.kind, f) } } impl ErrorKind { pub fn with_byte_offset(self, offset: u64) -> Error { self.with_position(FilePosition::Offset(offset)) } pub fn with_position(self, pos: FilePosition) -> Error { Error { inner: Box::new(ErrorImpl { kind: self, file_position: Some(pos), }), } } pub fn without_position(self) -> Error { Error { inner: Box::new(ErrorImpl { kind: self, file_position: None, }), } } } impl EventKind { pub fn of_event(event: &Event) -> EventKind { match event { Event::StartArray(_) => EventKind::StartArray, Event::StartDictionary(_) => EventKind::StartDictionary, Event::EndCollection => EventKind::EndCollection, Event::Boolean(_) => EventKind::Boolean, Event::Data(_) => EventKind::Data, Event::Date(_) => EventKind::Date, Event::Integer(_) => EventKind::Integer, Event::Real(_) => EventKind::Real, Event::String(_) => EventKind::String, Event::Uid(_) => EventKind::Uid, Event::__Nonexhaustive => unreachable!(), } } } impl fmt::Display for EventKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { EventKind::StartArray => "StartArray", EventKind::StartDictionary => "StartDictionary", EventKind::EndCollection => "EndCollection", EventKind::Boolean => "Boolean", EventKind::Data => "Data", EventKind::Date => "Date", EventKind::Integer => "Integer", EventKind::Real => "Real", EventKind::String => "String", EventKind::Uid => "Uid", EventKind::ValueOrStartCollection => "value or start collection", EventKind::DictionaryKeyOrEndCollection => "dictionary key or end collection", } .fmt(f) } } pub(crate) fn from_io_without_position(err: io::Error) -> Error { ErrorKind::Io(err).without_position() } pub(crate) fn unexpected_event_type(expected: EventKind, found: &Event) -> Error { let found = EventKind::of_event(&found); ErrorKind::UnexpectedEventType { expected, found }.without_position() } plist-1.0.0/src/integer.rs010064400007650000024000000116321364655500500136700ustar0000000000000000use std::{fmt, num::ParseIntError}; /// An integer that can be represented by either an `i64` or a `u64`. #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Integer { value: i128, } impl Integer { /// Returns the value as an `i64` if it can be represented by that type. pub fn as_signed(self) -> Option { if self.value >= i128::from(i64::min_value()) && self.value <= i128::from(i64::max_value()) { Some(self.value as i64) } else { None } } /// Returns the value as a `u64` if it can be represented by that type. pub fn as_unsigned(self) -> Option { if self.value >= 0 && self.value <= i128::from(u64::max_value()) { Some(self.value as u64) } else { None } } pub(crate) fn from_str(s: &str) -> Result { 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_start_matches("0x"); u64::from_str_radix(s, 16).map(Into::into) } else { // Match Apple's implementation in CFPropertyList.h - always try to parse as an i64 first. // TODO: Use IntErrorKind once stable and retry parsing on overflow only. Ok(match s.parse::() { Ok(v) => v.into(), Err(_) => s.parse::()?.into(), }) } } } impl fmt::Debug for Integer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } impl fmt::Display for Integer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } impl From for Integer { fn from(value: i64) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: i32) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: i16) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: i8) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: u64) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: u32) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: u16) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: u8) -> Integer { Integer { value: value.into(), } } } #[cfg(feature = "serde")] pub mod serde_impls { use serde::{ de::{Deserialize, Deserializer, Error, Visitor}, ser::{Serialize, Serializer}, }; use std::fmt; use crate::Integer; impl Serialize for Integer { fn serialize(&self, serializer: S) -> Result where S: Serializer, { if let Some(v) = self.as_unsigned() { serializer.serialize_u64(v) } else if let Some(v) = self.as_signed() { serializer.serialize_i64(v) } else { unreachable!(); } } } struct IntegerVisitor; impl<'de> Visitor<'de> for IntegerVisitor { type Value = Integer; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a plist integer") } fn visit_i64(self, v: i64) -> Result where E: Error, { Ok(Integer::from(v)) } fn visit_u64(self, v: u64) -> Result where E: Error, { Ok(Integer::from(v)) } } impl<'de> Deserialize<'de> for Integer { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(IntegerVisitor) } } } #[cfg(test)] mod tests { use super::Integer; #[test] fn from_str_limits() { assert_eq!(Integer::from_str("-1"), Ok((-1).into())); assert_eq!(Integer::from_str("0"), Ok(0.into())); assert_eq!(Integer::from_str("1"), Ok(1.into())); assert_eq!( Integer::from_str("-9223372036854775808"), Ok((-9223372036854775808i64).into()) ); assert!(Integer::from_str("-9223372036854775809").is_err()); assert_eq!( Integer::from_str("18446744073709551615"), Ok(18446744073709551615u64.into()) ); assert!(Integer::from_str("18446744073709551616").is_err()); } } plist-1.0.0/src/lib.rs010064400007650000024000000056431364656172300130120ustar0000000000000000//! # Plist //! //! A rusty plist parser. //! //! ## Usage //! //! Put this in your `Cargo.toml`: //! //! ```toml //! [dependencies] //! plist = "1" //! ``` //! //! And put this in your crate root: //! //! ```rust //! extern crate plist; //! ``` //! //! ## Examples //! //! ### Using `serde` //! //! ```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 Book { //! title: String, //! author: String, //! excerpt: String, //! copies_sold: u64, //! } //! //! let book: Book = plist::from_file("tests/data/book.plist") //! .expect("failed to read book.plist"); //! //! assert_eq!(book.title, "Great Expectations"); //! # } //! # //! # #[cfg(not(feature = "serde"))] //! # fn main() {} //! ``` //! //! ### Using `Value` //! //! ```rust //! use plist::Value; //! //! let book = Value::from_file("tests/data/book.plist") //! .expect("failed to read book.plist"); //! //! let title = book //! .as_dictionary() //! .and_then(|dict| dict.get("Title")) //! .and_then(|title| title.as_string()); //! //! assert_eq!(title, Some("Great Expectations")); //! ``` //! //! ## Unstable Features //! //! Many features from previous versions are now hidden behind the //! `enable_unstable_features_that_may_break_with_minor_version_bumps` feature. These will break in //! minor version releases after the 1.0 release. If you really really must use them you should //! specify a tilde requirement e.g. `plist = "~1.0.3"` in you `Cargo.toml` so that the plist crate //! is not automatically updated to version 1.1. pub mod dictionary; #[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")] pub mod stream; #[cfg(not(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps"))] mod stream; mod date; mod error; mod integer; mod uid; mod value; pub use date::Date; pub use dictionary::Dictionary; pub use error::Error; pub use integer::Integer; pub use uid::Uid; 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(all( feature = "serde", any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ) ))] pub use self::{de::Deserializer, ser::Serializer}; #[cfg(feature = "serde")] pub use self::{ de::{from_bytes, from_file, from_reader, from_reader_xml}, ser::{to_file_binary, to_file_xml, to_writer_binary, to_writer_xml}, }; #[cfg(all(test, feature = "serde"))] #[macro_use] extern crate serde_derive; #[cfg(all(test, feature = "serde"))] mod serde_tests; 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-1.0.0/src/ser.rs010064400007650000024000000553271351654463100130340ustar0000000000000000use serde::ser; use std::{ fmt::Display, fs::File, io::{BufWriter, Write}, mem, path::Path, }; use crate::{ date::serde_impls::DATE_NEWTYPE_STRUCT_NAME, error::{self, Error, ErrorKind}, stream::{self, Writer}, uid::serde_impls::UID_NEWTYPE_STRUCT_NAME, Date, Integer, Uid, }; #[doc(hidden)] impl ser::Error for Error { fn custom(msg: T) -> Self { ErrorKind::Serde(msg.to_string()).without_position() } } enum OptionMode { Root, StructField(&'static str), StructFieldNameWritten, Explicit, } /// A structure that serializes Rust values plist event streams. pub struct Serializer { writer: W, option_mode: OptionMode, } impl Serializer { pub fn new(writer: W) -> Serializer { Serializer { writer, option_mode: OptionMode::Root, } } pub fn into_inner(self) -> W { self.writer } fn serialize_with_option_mode( &mut self, option_mode: OptionMode, value: &T, ) -> Result<(), Error> { let prev_option_mode = mem::replace(&mut self.option_mode, option_mode); let result = value.serialize(&mut *self); self.option_mode = prev_option_mode; result } fn maybe_write_pending_struct_field_name(&mut self) -> Result<(), Error> { if let OptionMode::StructField(field_name) = self.option_mode { self.option_mode = OptionMode::StructFieldNameWritten; self.writer.write_string(field_name)?; } Ok(()) } fn write_start_array(&mut self, len: Option) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_start_array(len) } fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_start_dictionary(len) } fn write_end_collection(&mut self) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_end_collection() } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_boolean(value) } fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_data(value) } fn write_date(&mut self, value: Date) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_date(value) } fn write_integer(&mut self, value: Integer) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_integer(value) } fn write_real(&mut self, value: f64) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_real(value) } fn write_string(&mut self, value: &str) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_string(value) } fn write_uid(&mut self, value: Uid) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_uid(value) } } 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.write_boolean(v) } fn serialize_i8(self, v: i8) -> Result<(), Error> { self.serialize_i64(v.into()) } fn serialize_i16(self, v: i16) -> Result<(), Error> { self.serialize_i64(v.into()) } fn serialize_i32(self, v: i32) -> Result<(), Error> { self.serialize_i64(v.into()) } fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { self.write_integer(v.into()) } fn serialize_u8(self, v: u8) -> Result<(), Error> { self.serialize_u64(v.into()) } fn serialize_u16(self, v: u16) -> Result<(), Error> { self.serialize_u64(v.into()) } fn serialize_u32(self, v: u32) -> Result<(), Error> { self.serialize_u64(v.into()) } fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { self.write_integer(v.into()) } fn serialize_f32(self, v: f32) -> Result<(), Error> { self.serialize_f64(v.into()) } fn serialize_f64(self, v: f64) -> Result<(), Error> { self.write_real(v) } fn serialize_char(self, v: char) -> Result<(), Self::Error> { let mut buf = [0; 4]; let v = v.encode_utf8(&mut buf); self.write_string(v) } fn serialize_str(self, v: &str) -> Result<(), Error> { self.write_string(v) } fn serialize_bytes(self, v: &[u8]) -> Result<(), Error> { self.write_data(v) } fn serialize_none(self) -> Result<(), Error> { match self.option_mode { OptionMode::Root | OptionMode::StructField(_) => (), OptionMode::StructFieldNameWritten => unreachable!(), OptionMode::Explicit => { self.write_start_dictionary(Some(1))?; self.write_string("None")?; self.serialize_unit()?; self.write_end_collection()?; } } Ok(()) } fn serialize_some(self, value: &T) -> Result<(), Error> { match self.option_mode { OptionMode::Root => self.serialize_with_option_mode(OptionMode::Explicit, value)?, OptionMode::StructField(field_name) => { self.option_mode = OptionMode::StructFieldNameWritten; self.write_string(field_name)?; self.serialize_with_option_mode(OptionMode::Explicit, value)?; } OptionMode::StructFieldNameWritten => unreachable!(), OptionMode::Explicit => { self.write_start_dictionary(Some(1))?; self.write_string("Some")?; value.serialize(&mut *self)?; self.write_end_collection()?; } } Ok(()) } fn serialize_unit(self) -> Result<(), Error> { self.write_string("") } fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> { self.serialize_unit() } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result<(), Error> { self.write_start_dictionary(Some(1))?; self.write_string(variant)?; self.serialize_unit()?; self.write_end_collection() } fn serialize_newtype_struct( self, name: &'static str, value: &T, ) -> Result<(), Error> { match name { DATE_NEWTYPE_STRUCT_NAME => value.serialize(DateSerializer { ser: &mut *self }), UID_NEWTYPE_STRUCT_NAME => value.serialize(UidSerializer { ser: &mut *self }), _ => value.serialize(self), } } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result<(), Error> { self.write_start_dictionary(Some(1))?; self.write_string(variant)?; value.serialize(&mut *self)?; self.write_end_collection() } fn serialize_seq(self, len: Option) -> Result { let len = len.map(|len| len as u64); self.write_start_array(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.write_start_dictionary(Some(1))?; self.write_string(variant)?; self.serialize_tuple(len) } fn serialize_map(self, len: Option) -> Result { let len = len.map(|len| len as u64); self.write_start_dictionary(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.write_start_dictionary(Some(1))?; self.write_string(variant)?; self.serialize_struct(name, 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<(), Error> { Err(self.expecting_date_error()) } fn serialize_i8(self, _: i8) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_i16(self, _: i16) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_i32(self, _: i32) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_i64(self, _: i64) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_u8(self, _: u8) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_u16(self, _: u16) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_u32(self, _: u32) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_u64(self, _: u64) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_f32(self, _: f32) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_f64(self, _: f64) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_char(self, _: char) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_str(self, v: &str) -> Result<(), Error> { let date = Date::from_rfc3339(v).map_err(|()| self.expecting_date_error())?; self.ser.write_date(date) } fn serialize_bytes(self, _: &[u8]) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_none(self) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_some(self, _: &T) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_unit(self) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_unit_struct(self, _: &'static str) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_newtype_struct( self, _: &'static str, _: &T, ) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_newtype_variant( self, _: &'static str, _: u32, _: &'static str, _: &T, ) -> Result<(), 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()) } } struct UidSerializer<'a, W: 'a + Writer> { ser: &'a mut Serializer, } impl<'a, W: Writer> UidSerializer<'a, W> { fn expecting_uid_error(&self) -> Error { ser::Error::custom("plist uid expected") } } impl<'a, W: Writer> ser::Serializer for UidSerializer<'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<(), Error> { Err(self.expecting_uid_error()) } fn serialize_i8(self, _: i8) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_i16(self, _: i16) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_i32(self, _: i32) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_i64(self, _: i64) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_u8(self, _: u8) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_u16(self, _: u16) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_u32(self, _: u32) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_u64(self, v: u64) -> Result<(), Error> { self.ser.write_uid(Uid::new(v)) } fn serialize_f32(self, _: f32) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_f64(self, _: f64) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_char(self, _: char) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_str(self, _: &str) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_bytes(self, _: &[u8]) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_none(self) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_some(self, _: &T) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_unit(self) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_unit_struct(self, _: &'static str) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_newtype_struct( self, _: &'static str, _: &T, ) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_newtype_variant( self, _: &'static str, _: u32, _: &'static str, _: &T, ) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_seq(self, _: Option) -> Result { Err(self.expecting_uid_error()) } fn serialize_tuple(self, _: usize) -> Result { Err(self.expecting_uid_error()) } fn serialize_tuple_struct( self, _: &'static str, _: usize, ) -> Result { Err(self.expecting_uid_error()) } fn serialize_tuple_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(self.expecting_uid_error()) } fn serialize_map(self, _: Option) -> Result { Err(self.expecting_uid_error()) } fn serialize_struct(self, _: &'static str, _: usize) -> Result { Err(self.expecting_uid_error()) } fn serialize_struct_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(self.expecting_uid_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<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeTuple for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result<(), Error> { self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeTupleStruct for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result<(), Error> { self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeTupleVariant for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { self.ser.write_end_collection()?; self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeMap for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_key(&mut self, key: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, key) } fn serialize_value(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { self.ser.write_end_collection() } } 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<(), 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. self.ser .serialize_with_option_mode(OptionMode::StructField(key), value) } fn end(self) -> Result<(), Error> { self.ser.write_end_collection() } } 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<(), Error> { self.ser .serialize_with_option_mode(OptionMode::StructField(key), value) } fn end(self) -> Result<(), Error> { self.ser.write_end_collection()?; self.ser.write_end_collection() } } /// Serializes the given data structure to a file as a binary encoded plist. pub fn to_file_binary, T: ser::Serialize>(path: P, value: &T) -> Result<(), Error> { let mut file = File::create(path).map_err(error::from_io_without_position)?; to_writer_binary(BufWriter::new(&mut file), value)?; file.sync_all().map_err(error::from_io_without_position)?; Ok(()) } /// Serializes the given data structure to a file as an XML encoded plist. pub fn to_file_xml, T: ser::Serialize>(path: P, value: &T) -> Result<(), Error> { let mut file = File::create(path).map_err(error::from_io_without_position)?; to_writer_xml(BufWriter::new(&mut file), value)?; file.sync_all().map_err(error::from_io_without_position)?; Ok(()) } /// Serializes the given data structure to a byte stream as a binary encoded plist. pub fn to_writer_binary(writer: W, value: &T) -> Result<(), Error> { let writer = stream::BinaryWriter::new(writer); let mut ser = Serializer::new(writer); value.serialize(&mut ser) } /// Serializes the given data structure to a byte stream as an XML encoded plist. 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-1.0.0/src/serde_tests.rs010064400007650000024000000320441364656172300145630ustar0000000000000000use serde::{de::DeserializeOwned, ser::Serialize}; use std::{collections::BTreeMap, fmt::Debug}; use crate::{ stream::{private::Sealed, Event, Writer}, Date, Deserializer, Error, Integer, Serializer, Uid, }; 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_start_array(&mut self, len: Option) -> Result<(), Error> { self.events.push(Event::StartArray(len)); Ok(()) } fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error> { self.events.push(Event::StartDictionary(len)); Ok(()) } fn write_end_collection(&mut self) -> Result<(), Error> { self.events.push(Event::EndCollection); Ok(()) } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.events.push(Event::Boolean(value)); Ok(()) } fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { self.events.push(Event::Data(value.to_owned())); Ok(()) } fn write_date(&mut self, value: Date) -> Result<(), Error> { self.events.push(Event::Date(value)); Ok(()) } fn write_integer(&mut self, value: Integer) -> Result<(), Error> { self.events.push(Event::Integer(value)); Ok(()) } fn write_real(&mut self, value: f64) -> Result<(), Error> { self.events.push(Event::Real(value)); Ok(()) } fn write_string(&mut self, value: &str) -> Result<(), Error> { self.events.push(Event::String(value.to_owned())); Ok(()) } fn write_uid(&mut self, value: Uid) -> Result<(), Error> { self.events.push(Event::Uid(value)); Ok(()) } } impl Sealed for VecWriter {} 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, Option>), Cat { age: Integer, 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, d: Option, } #[test] fn cow() { let cow = Animal::Cow; let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Cow".to_owned()), Event::String("".to_owned()), Event::EndCollection, ]; 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()], d: Some(Uid::new(42)), }], }); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Dog".to_owned()), Event::StartDictionary(None), Event::String("inner".to_owned()), Event::StartArray(Some(1)), Event::StartDictionary(None), Event::String("a".to_owned()), Event::String("".to_owned()), Event::String("b".to_owned()), Event::Integer(12.into()), Event::String("c".to_owned()), Event::StartArray(Some(2)), Event::String("a".to_owned()), Event::String("b".to_owned()), Event::EndCollection, Event::String("d".to_owned()), Event::Uid(Uid::new(42)), Event::EndCollection, Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(dog, Some(comparison)); } #[test] fn frog() { let frog = Animal::Frog( Ok("hello".to_owned()), Some(vec![1.0, 2.0, 3.14159, 0.000000001, 1.27e31]), ); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Frog".to_owned()), Event::StartArray(Some(2)), Event::StartDictionary(Some(1)), Event::String("Ok".to_owned()), Event::String("hello".to_owned()), Event::EndCollection, Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::StartArray(Some(5)), Event::Real(1.0), Event::Real(2.0), Event::Real(3.14159), Event::Real(0.000000001), Event::Real(1.27e31), Event::EndCollection, Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(frog, Some(comparison)); } #[test] fn cat_with_firmware() { let cat = Animal::Cat { age: 12.into(), name: "Paws".to_owned(), firmware: Some(vec![0, 1, 2, 3, 4, 5, 6, 7, 8]), }; let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Cat".to_owned()), Event::StartDictionary(None), Event::String("age".to_owned()), Event::Integer(12.into()), Event::String("name".to_owned()), Event::String("Paws".to_owned()), Event::String("firmware".to_owned()), Event::StartArray(Some(9)), Event::Integer(0.into()), Event::Integer(1.into()), Event::Integer(2.into()), Event::Integer(3.into()), Event::Integer(4.into()), Event::Integer(5.into()), Event::Integer(6.into()), Event::Integer(7.into()), Event::Integer(8.into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(cat, Some(comparison)); } #[test] fn cat_without_firmware() { let cat = Animal::Cat { age: Integer::from(-12), name: "Paws".to_owned(), firmware: None, }; let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Cat".to_owned()), Event::StartDictionary(None), Event::String("age".to_owned()), Event::Integer(Integer::from(-12)), Event::String("name".to_owned()), Event::String("Paws".to_owned()), Event::EndCollection, Event::EndCollection, ]; 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 = &[ Event::StartArray(Some(3)), Event::Integer(34.into()), Event::Integer(32.into()), Event::Integer(13.into()), Event::EndCollection, ]; 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(Some(12)), c: None, }; let obj = TypeWithOptions { a: Some("hello".to_owned()), b: Some(None), c: Some(Box::new(inner)), }; let comparison = &[ Event::StartDictionary(None), Event::String("a".to_owned()), Event::String("hello".to_owned()), Event::String("b".to_owned()), Event::StartDictionary(Some(1)), Event::String("None".to_owned()), Event::String("".to_owned()), Event::EndCollection, Event::String("c".to_owned()), Event::StartDictionary(None), Event::String("b".to_owned()), Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::Integer(12.into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; 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::from_rfc3339("1920-01-01T00:10:00Z").unwrap(); let obj = TypeWithDate { a: Some(28), b: Some(date.clone()), }; let comparison = &[ Event::StartDictionary(None), Event::String("a".to_owned()), Event::Integer(28.into()), Event::String("b".to_owned()), Event::Date(date), Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_some() { let obj = Some(12); let comparison = &[Event::Integer(12.into())]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_none() { let obj: Option = None; let comparison = &[]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_some_some() { let obj = Some(Some(12)); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::Integer(12.into()), Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_some_none() { let obj: Option> = Some(None); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("None".to_owned()), Event::String("".to_owned()), Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_dictionary_values() { let mut obj = BTreeMap::new(); obj.insert("a".to_owned(), None); obj.insert("b".to_owned(), Some(None)); obj.insert("c".to_owned(), Some(Some(144))); let comparison = &[ Event::StartDictionary(Some(3)), Event::String("a".to_owned()), Event::StartDictionary(Some(1)), Event::String("None".to_owned()), Event::String("".to_owned()), Event::EndCollection, Event::String("b".to_owned()), Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::StartDictionary(Some(1)), Event::String("None".to_owned()), Event::String("".to_owned()), Event::EndCollection, Event::EndCollection, Event::String("c".to_owned()), Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::Integer(144.into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_dictionary_keys() { let mut obj = BTreeMap::new(); obj.insert(None, 1); obj.insert(Some(None), 2); obj.insert(Some(Some(144)), 3); let comparison = &[ Event::StartDictionary(Some(3)), Event::StartDictionary(Some(1)), Event::String("None".to_owned()), Event::String("".to_owned()), Event::EndCollection, Event::Integer(1.into()), Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::StartDictionary(Some(1)), Event::String("None".to_owned()), Event::String("".to_owned()), Event::EndCollection, Event::EndCollection, Event::Integer(2.into()), Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::Integer(144.into()), Event::EndCollection, Event::EndCollection, Event::Integer(3.into()), Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } #[test] fn option_array() { let obj = vec![None, Some(None), Some(Some(144))]; let comparison = &[ Event::StartArray(Some(3)), Event::StartDictionary(Some(1)), Event::String("None".to_owned()), Event::String("".to_owned()), Event::EndCollection, Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::StartDictionary(Some(1)), Event::String("None".to_owned()), Event::String("".to_owned()), Event::EndCollection, Event::EndCollection, Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::StartDictionary(Some(1)), Event::String("Some".to_owned()), Event::Integer(144.into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(obj, Some(comparison)); } plist-1.0.0/src/stream/binary_reader.rs010064400007650000024000000422061364656172300163410ustar0000000000000000use std::{ io::{self, Read, Seek, SeekFrom}, mem::size_of, }; use crate::{ date::{Date, InfiniteOrNanDate}, error::{Error, ErrorKind}, stream::Event, u64_to_usize, Uid, }; 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: PosReader, ref_size: u8, root_object: u64, trailer_start_offset: u64, } struct PosReader { reader: R, pos: u64, } impl PosReader { fn read_all(&mut self, buf: &mut [u8]) -> Result<(), Error> { self.read_exact(buf) .map_err(|err| ErrorKind::Io(err).with_byte_offset(self.pos))?; Ok(()) } fn seek(&mut self, pos: SeekFrom) -> Result { self.pos = self .reader .seek(pos) .map_err(|err| ErrorKind::Io(err).with_byte_offset(self.pos))?; Ok(self.pos) } } impl Read for PosReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { let count = self.reader.read(buf)?; self.pos .checked_add(count as u64) .expect("file cannot be larger than `u64::max_value()` bytes"); Ok(count) } } impl BinaryReader { pub fn new(reader: R) -> BinaryReader { BinaryReader { stack: Vec::new(), object_offsets: Vec::new(), object_on_stack: Vec::new(), reader: PosReader { reader, pos: 0 }, ref_size: 0, root_object: 0, trailer_start_offset: 0, } } fn allocate_vec(&self, len: u64, size: usize) -> Result, Error> { // Check we are not reading past the start of the plist trailer let inner = |len: u64, size: usize| { let byte_len = len.checked_mul(size as u64)?; let end_offset = self.reader.pos.checked_add(byte_len)?; if end_offset <= self.trailer_start_offset { Some(()) } else { None } }; inner(len, size).ok_or_else(|| self.with_pos(ErrorKind::ObjectOffsetTooLarge))?; Ok(Vec::with_capacity(len as usize)) } fn read_trailer(&mut self) -> Result<(), Error> { self.reader.seek(SeekFrom::Start(0))?; let mut magic = [0; 8]; self.reader.read_all(&mut magic)?; if &magic != b"bplist00" { return Err(self.with_pos(ErrorKind::InvalidMagic)); } self.trailer_start_offset = self.reader.seek(SeekFrom::End(-32))?; // Trailer starts with 6 bytes of padding let mut zeros = [0; 6]; self.reader.read_all(&mut zeros)?; let offset_size = self.read_u8()?; match offset_size { 1 | 2 | 4 | 8 => (), _ => return Err(self.with_pos(ErrorKind::InvalidTrailerObjectOffsetSize)), } self.ref_size = self.read_u8()?; match self.ref_size { 1 | 2 | 4 | 8 => (), _ => return Err(self.with_pos(ErrorKind::InvalidTrailerObjectReferenceSize)), } let num_objects = self.read_be_u64()?; self.root_object = self.read_be_u64()?; let offset_table_offset = self.read_be_u64()?; // 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(()) } /// Reads a list of `len` big-endian integers of `size` bytes from the reader. 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.read_u8()?.into()), 2 => ints.push(self.read_be_u16()?.into()), 4 => ints.push(self.read_be_u32()?.into()), 8 => ints.push(self.read_be_u64()?), _ => unreachable!("size is either self.ref_size or offset_size both of which are already validated") } } Ok(ints) } /// Reads a list of `len` offsets into the object table from the reader. fn read_refs(&mut self, len: u64) -> Result, Error> { let ref_size = self.ref_size; self.read_ints(len, ref_size) } /// Reads a compressed value length from the reader. `len` must contain the low 4 bits of the /// object token. fn read_object_len(&mut self, len: u8) -> Result { if (len & 0x0f) == 0x0f { let len_power_of_two = self.read_u8()? & 0x03; Ok(match len_power_of_two { 0 => self.read_u8()?.into(), 1 => self.read_be_u16()?.into(), 2 => self.read_be_u32()?.into(), 3 => self.read_be_u64()?, _ => return Err(self.with_pos(ErrorKind::InvalidObjectLength)), }) } else { Ok(len.into()) } } /// Reads `len` bytes from the reader. 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_all(&mut data)?; Ok(data) } fn seek_to_object(&mut self, object_ref: u64) -> Result { let object_ref = u64_to_usize(object_ref) .ok_or_else(|| self.with_pos(ErrorKind::ObjectReferenceTooLarge))?; let offset = *self .object_offsets .get(object_ref) .ok_or_else(|| self.with_pos(ErrorKind::ObjectReferenceTooLarge))?; if offset >= self.trailer_start_offset { return Err(self.with_pos(ErrorKind::ObjectOffsetTooLarge)); } 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(self.with_pos(ErrorKind::RecursiveObject)); } *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 | StackType::Dict => return Ok(Some(Event::EndCollection)), } } }; self.seek_to_object(object_ref)?; let token = self.read_u8()?; let ty = (token & 0xf0) >> 4; let size = token & 0x0f; let result = match (ty, size) { (0x0, 0x00) => return Err(self.with_pos(ErrorKind::NullObjectUnimplemented)), (0x0, 0x08) => Some(Event::Boolean(false)), (0x0, 0x09) => Some(Event::Boolean(true)), (0x0, 0x0f) => return Err(self.with_pos(ErrorKind::FillObjectUnimplemented)), (0x1, 0) => Some(Event::Integer(self.read_u8()?.into())), (0x1, 1) => Some(Event::Integer(self.read_be_u16()?.into())), (0x1, 2) => Some(Event::Integer(self.read_be_u32()?.into())), (0x1, 3) => Some(Event::Integer(self.read_be_i64()?.into())), (0x1, 4) => { let value = self.read_be_i128()?; if value < 0 || value > u64::max_value().into() { return Err(self.with_pos(ErrorKind::IntegerOutOfRange)); } Some(Event::Integer((value as u64).into())) } (0x1, _) => return Err(self.with_pos(ErrorKind::UnknownObjectType(token))), // variable length int (0x2, 2) => Some(Event::Real(f32::from_bits(self.read_be_u32()?).into())), (0x2, 3) => Some(Event::Real(f64::from_bits(self.read_be_u64()?))), (0x2, _) => return Err(self.with_pos(ErrorKind::UnknownObjectType(token))), // odd length float (0x3, 3) => { // Date. Seconds since 1/1/2001 00:00:00. let secs = f64::from_bits(self.read_be_u64()?); let date = Date::from_seconds_since_plist_epoch(secs) .map_err(|InfiniteOrNanDate| self.with_pos(ErrorKind::InfiniteOrNanDate))?; Some(Event::Date(date)) } (0x4, n) => { // Data let len = self.read_object_len(n)?; Some(Event::Data(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) .map_err(|_| self.with_pos(ErrorKind::InvalidUtf8String))?; Some(Event::String(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.read_be_u16()?); } let string = String::from_utf16(&raw_utf16) .map_err(|_| self.with_pos(ErrorKind::InvalidUtf16String))?; Some(Event::String(string)) } (0x8, n) if n < 8 => { // Uid let mut buf = [0; 8]; // `len_bytes` is at most 8. let len_bytes = n as usize + 1; // Values are stored in big-endian so we must put the least significant bytes at // the end of the buffer. self.reader.read_all(&mut buf[8 - len_bytes..])?; let value = u64::from_be_bytes(buf); Some(Event::Uid(Uid::new(value))) } (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 keys_and_values_len = len .checked_mul(2) .ok_or_else(|| self.with_pos(ErrorKind::ObjectTooLarge))?; let mut child_object_refs = self.allocate_vec(keys_and_values_len, self.ref_size as usize)?; let len = key_refs.len(); for i in 1..=len { // 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(self.with_pos(ErrorKind::UnknownObjectType(token))), }; Ok(result) } fn read_u8(&mut self) -> Result { let mut buf = [0; 1]; self.reader.read_all(&mut buf)?; Ok(buf[0]) } fn read_be_u16(&mut self) -> Result { let mut buf = [0; 2]; self.reader.read_all(&mut buf)?; Ok(u16::from_be_bytes(buf)) } fn read_be_u32(&mut self) -> Result { let mut buf = [0; 4]; self.reader.read_all(&mut buf)?; Ok(u32::from_be_bytes(buf)) } fn read_be_u64(&mut self) -> Result { let mut buf = [0; 8]; self.reader.read_all(&mut buf)?; Ok(u64::from_be_bytes(buf)) } fn read_be_i64(&mut self) -> Result { let mut buf = [0; 8]; self.reader.read_all(&mut buf)?; Ok(i64::from_be_bytes(buf)) } fn read_be_i128(&mut self) -> Result { let mut buf = [0; 16]; self.reader.read_all(&mut buf)?; Ok(i128::from_be_bytes(buf)) } fn with_pos(&self, kind: ErrorKind) -> Error { kind.with_byte_offset(self.reader.pos) } } 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 std::{fs::File, path::Path}; use super::*; use crate::{stream::Event, Uid}; #[test] fn streaming_parser() { use crate::stream::Event::*; 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(13)), String("Author".into()), String("William Shakespeare".into()), String("Birthdate".into()), Date(super::Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap()), String("EmptyArray".into()), StartArray(Some(0)), EndCollection, String("IsNotFalse".into()), Boolean(false), String("SmallestNumber".into()), Integer((-9223372036854775808i64).into()), String("EmptyDictionary".into()), StartDictionary(Some(0)), EndCollection, String("Height".into()), Real(1.6), String("Lines".into()), StartArray(Some(2)), String("It is a tale told by an idiot,".into()), String("Full of sound and fury, signifying nothing.".into()), EndCollection, String("Death".into()), Integer(1564.into()), String("Blank".into()), String("".into()), String("BiggestNumber".into()), Integer(18446744073709551615u64.into()), String("IsTrue".into()), Boolean(true), String("Data".into()), Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), EndCollection, ]; 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], Event::String("\u{2605} or better".to_owned())); let poem = if let Event::String(ref mut poem) = events[4] { poem } else { panic!("not a string") }; assert_eq!(poem.len(), 643); assert_eq!(poem.pop().unwrap(), '\u{2605}'); } #[test] fn nskeyedarchiver_plist() { let reader = File::open(&Path::new("./tests/data/binary_NSKeyedArchiver.plist")).unwrap(); let streaming_parser = BinaryReader::new(reader); let events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); assert_eq!(events[10], Event::Uid(Uid::new(4))); assert_eq!(events[12], Event::Uid(Uid::new(2))); assert_eq!(events[18], Event::Uid(Uid::new(3))); assert_eq!(events[46], Event::Uid(Uid::new(1))); } } plist-1.0.0/src/stream/binary_writer.rs010064400007650000024000000651161364656172300164200ustar0000000000000000// TODO: Revisit the design of `Event` once the `HashMap` raw interface is stabilised. // Ideally `Value`s would be stored inline in `Event`. use indexmap::IndexMap; use std::{ borrow::Cow, io::{self, Write}, mem, num::NonZeroUsize, }; use crate::{ error::{self, Error, ErrorKind, EventKind}, stream::Writer, Date, Integer, Uid, }; pub struct BinaryWriter { writer: PosWriter, events: Vec, dictionary_key_events: Vec, values: IndexMap, ValueState>, /// Pointers into `events` for each of the currently unclosed `Collection` events. collection_stack: Vec, /// The number of `Collection` and unique `Value` events in `events`. num_objects: usize, } struct PosWriter { writer: W, pos: usize, } #[derive(Clone)] struct ObjectRef(NonZeroUsize); /// An array of `len` elements is stored as a `Collection` event followed by `skip_len` events /// containing the contents of the array. e.g. /// /// Collection(ty: Array, len: 2, skip_len: 2) /// Value /// Value /// /// If the array contains another array or dictionary `len` and `skip_len` will differ. e.g. /// /// Collection(ty: Array, len: 2, skip_len: 3) /// Value /// Collection(ty: Array, len: 1, skip_len: 1) /// Value /// /// A dictionary of `len` (key, value) pairs is stored as a `Collection` event followed by /// `skip_len` events containing the contents of the dictionary. The dictionary values are stored /// first. These are followed by a `DictionaryKeys` event and then the keys themselves. e.g. /// /// Collection(ty: Dictionary, len: 2, skip_len: 6) /// Value /// Collection(ty: Array, len: 1, skip_len: 1) /// Value /// DictionaryKeys(2) /// Value (Key) /// Value (Key) /// /// This arrangement simplifies writing dictionaries as they must be written in the order /// (key, key, value, value) instead of (key, value, key, value) as they are passed to the writer. /// Unclosed dictionaries have their keys stored in `dictionary_key_events` and these are only /// moved to the end of the `BinaryWriter::events` array once the dictionary is closed in /// `write_end_collection`. enum Event { Collection(Collection), /// Index of the value in the `values` map. Value(usize), /// The number of dictionary keys following this event. DictionaryKeys(usize), } struct Collection { ty: CollectionType, /// The number of elements in an array or (key, value) pairs in a dictionary. /// Unclosed dictionaries have a `len` equal to the number of keys plus the number of values /// written so far. This is fixed up in `write_end_collection`. len: usize, /// The number of events to skip to get to the next element after the collection. skip: usize, object_ref: Option, } #[derive(Eq, PartialEq)] enum CollectionType { Array, Dictionary, } #[derive(Eq, Hash, PartialEq)] enum Value<'a> { Boolean(bool), Data(Cow<'a, [u8]>), Date(Date), Integer(Integer), /// Floats are deduplicated based on their bitwise value. Real(u64), String(Cow<'a, str>), Uid(Uid), } enum ValueState { /// The value has not been assigned an object reference. Unassigned, /// The value has been assigned an object reference but has not yet been written. Unwritten(ObjectRef), /// The value has been written with the given object reference. Written(ObjectRef), } impl BinaryWriter { pub fn new(writer: W) -> BinaryWriter { BinaryWriter { writer: PosWriter { writer, pos: 0 }, events: Vec::new(), dictionary_key_events: Vec::new(), values: IndexMap::new(), collection_stack: Vec::new(), num_objects: 0, } } fn write_start_collection(&mut self, ty: CollectionType) -> Result<(), Error> { if self.expecting_dictionary_key() { let ty_event_kind = match ty { CollectionType::Array => EventKind::StartArray, CollectionType::Dictionary => EventKind::StartDictionary, }; return Err(ErrorKind::UnexpectedEventType { expected: EventKind::DictionaryKeyOrEndCollection, found: ty_event_kind, } .without_position()); } self.increment_current_collection_len(); self.collection_stack.push(self.events.len()); self.events.push(Event::Collection(Collection { ty, len: 0, skip: 0, object_ref: None, })); self.num_objects += 1; Ok(()) } fn write_end_collection(&mut self) -> Result<(), Error> { let collection_event_index = self.collection_stack.pop().ok_or_else(|| { ErrorKind::UnexpectedEventType { expected: EventKind::ValueOrStartCollection, found: EventKind::EndCollection, } .without_position() })?; let current_event_index = self.events.len() - 1; let c = if let Event::Collection(c) = &mut self.events[collection_event_index] { c } else { unreachable!("items in `collection_stack` always point to a collection event"); }; c.skip = current_event_index - collection_event_index; if let CollectionType::Dictionary = c.ty { // Ensure that every dictionary key is paired with a value. if !is_even(c.len) { return Err(ErrorKind::UnexpectedEventType { expected: EventKind::DictionaryKeyOrEndCollection, found: EventKind::EndCollection, } .without_position()); } // Fix up the dictionary length. It should contain the number of key-value pairs, // not the number of keys and values. c.len /= 2; // To skip past a dictionary we also need to skip the `DictionaryKeys` event and the // keys that follow it. c.skip += 1 + c.len; let len = c.len; self.events.push(Event::DictionaryKeys(len)); // Move the cached dictionary keys to the end of the events array. let keys_start_index = self.dictionary_key_events.len() - len; self.events.extend( self.dictionary_key_events .drain(keys_start_index..) .map(Event::Value), ); } if self.collection_stack.is_empty() { self.write_plist()?; } Ok(()) } fn write_value(&mut self, value: Value) -> Result<(), Error> { let expecting_dictionary_key = self.expecting_dictionary_key(); // Ensure that all dictionary keys are strings. match (&value, expecting_dictionary_key) { (Value::String(_), true) | (_, false) => (), (_, true) => { return Err(ErrorKind::UnexpectedEventType { expected: EventKind::DictionaryKeyOrEndCollection, found: value.event_kind(), } .without_position()) } } // Deduplicate `value`. There is one entry in `values` for each unqiue `Value` in the // plist. let value_index = if let Some((value_index, _, _)) = self.values.get_full(&value) { value_index } else { self.num_objects += 1; let value = value.into_owned(); let (value_index, _) = self.values.insert_full(value, ValueState::Unassigned); value_index }; // Dictionary keys are buffered in `dictionary_key_events` until the dictionary is closed // in `write_end_collection` when they are moved to the end of the `events` array. if expecting_dictionary_key { self.dictionary_key_events.push(value_index); } else { self.events.push(Event::Value(value_index)); } self.increment_current_collection_len(); if self.collection_stack.is_empty() { self.write_plist()?; } Ok(()) } fn expecting_dictionary_key(&self) -> bool { if let Some(&event_index) = self.collection_stack.last() { if let Event::Collection(c) = &self.events[event_index] { c.ty == CollectionType::Dictionary && is_even(c.len) } else { unreachable!("items in `collection_stack` always point to a collection event"); } } else { false } } fn increment_current_collection_len(&mut self) { if let Some(&event_index) = self.collection_stack.last() { if let Event::Collection(c) = &mut self.events[event_index] { c.len += 1; } else { unreachable!("items in `collection_stack` always point to a collection event"); } } } fn write_plist(&mut self) -> Result<(), Error> { assert!(self.collection_stack.is_empty()); // Write header self.writer.write_exact(b"bplist00")?; // Write objects let mut events_vec = mem::replace(&mut self.events, Vec::new()); let mut events = &mut events_vec[..]; let ref_size = plist_ref_size(self.num_objects - 1); let mut offset_table = vec![0; self.num_objects]; // Assign the first (root) event an object reference of zero. let mut next_object_ref = ObjectRef::zero(); match &mut events[0] { Event::Value(value_index) => { let (_, value_state) = value_mut(&mut self.values, *value_index); *value_state = ValueState::Unwritten(next_object_ref.clone_and_increment_self()); } Event::Collection(c) => { c.object_ref = Some(next_object_ref.clone_and_increment_self()); } Event::DictionaryKeys(_) => { unreachable!("`events` starts with a value or collection event") } } while let Some((event, rest)) = events.split_first_mut() { events = rest; match event { Event::Collection(c) => { let collection_events = &mut events[..c.skip]; self.write_plist_collection( c, collection_events, ref_size, &mut next_object_ref, &mut offset_table, )?; } Event::Value(value_index) => { self.write_plist_value(*value_index, &mut offset_table)?; } // Dictionary keys will have already been written in `write_plist_collection` so we // skip over them here. Event::DictionaryKeys(len) => { events = &mut events[*len..]; } } } // Write object offset table let offset_table_offset = self.writer.pos; let offset_size = plist_ref_size(offset_table_offset); for &offset in &offset_table { write_plist_ref(&mut self.writer, offset_size, offset)?; } // Write trailer // 6 zero bytes padding // 1 byte offset size // 1 byte object ref size // 8 bytes number of objects // 8 bytes root object ref (always zero) // 8 bytes file offset of the object offset table let mut trailer = [0; 32]; trailer[6] = offset_size; trailer[7] = ref_size; trailer[8..16].copy_from_slice(&(self.num_objects as u64).to_be_bytes()); trailer[24..32].copy_from_slice(&(offset_table_offset as u64).to_be_bytes()); self.writer.write_exact(&trailer)?; self.writer .flush() .map_err(error::from_io_without_position)?; // Reset plist writer self.writer.pos = 0; events_vec.clear(); self.events = events_vec; self.values.clear(); self.num_objects = 0; Ok(()) } fn write_plist_collection( &mut self, collection: &Collection, events: &mut [Event], ref_size: u8, next_object_ref: &mut ObjectRef, offset_table: &mut Vec, ) -> Result<(), Error> { if let Some(object_ref) = &collection.object_ref { offset_table[object_ref.value()] = self.writer.pos; } else { unreachable!("collection object refs are assigned before this function is called"); } // Split the events in the current collection into keys and values (arrays contain only // values). This is required as dictionary keys appear after values in the `events array // but all keys must be written before any values. let (keys, values, ty) = match collection.ty { CollectionType::Array => (&mut [][..], events, 0xa0), CollectionType::Dictionary => { let keys_start_offset = events.len() - collection.len - 1; let (values, keys) = events.split_at_mut(keys_start_offset); (&mut keys[1..], values, 0xd0) } }; let mut collection_events = keys.iter_mut().chain(values); // Collections are written as a length prefixed array of object references. For an array // the length is the number of elements. For a dictionary it is the number of (key, value) // pairs. write_plist_value_ty_and_size(&mut self.writer, ty, collection.len)?; while let Some(event) = collection_events.next() { let object_ref = match event { Event::Collection(c) => { // We only want to write references to top level elements in the collection so // we skip over the contents of any sub-collections. if c.skip > 0 { let _ = collection_events.nth(c.skip - 1); } // Collections are not deduplicated so they must be assigned an object // reference here. assert!(c.object_ref.is_none()); let object_ref = next_object_ref.clone_and_increment_self(); c.object_ref = Some(object_ref.clone()); object_ref } Event::Value(value_index) => { // Values are deduplicated so we only assign an object reference if we have not // already done so previously. let (_, value_state) = value_mut(&mut self.values, *value_index); match value_state { ValueState::Unassigned => { let object_ref = next_object_ref.clone_and_increment_self(); *value_state = ValueState::Unwritten(object_ref.clone()); object_ref } ValueState::Unwritten(object_ref) | ValueState::Written(object_ref) => { object_ref.clone() } } } Event::DictionaryKeys(_) => unreachable!( "`DictionaryKeys` events are specifically excluded from the iterator" ), }; write_plist_ref(&mut self.writer, ref_size, object_ref.value())?; } // We write dictionary keys here as they appear after values in the `events` array but // should come before values in the plist stream to reduce seeking on read. for key in keys { if let Event::Value(value_index) = key { self.write_plist_value(*value_index, offset_table)?; } else { unreachable!("dictionary keys are assigned as values in `write_end_collection`"); } } Ok(()) } fn write_plist_value( &mut self, value_index: usize, offset_table: &mut Vec, ) -> Result<(), Error> { let (value, value_state) = value_mut(&mut self.values, value_index); let object_ref = match value_state { ValueState::Unassigned => { unreachable!("value object refs are assigned before this function is called"); } ValueState::Unwritten(object_ref) => object_ref.clone(), ValueState::Written(_) => return Ok(()), }; offset_table[object_ref.value()] = self.writer.pos; *value_state = ValueState::Written(object_ref); match value { Value::Boolean(true) => { self.writer.write_exact(&[0x09])?; } Value::Boolean(false) => { self.writer.write_exact(&[0x08])?; } Value::Data(v) => { write_plist_value_ty_and_size(&mut self.writer, 0x40, v.len())?; self.writer.write_exact(&v[..])?; } Value::Date(v) => { let secs = v.to_seconds_since_plist_epoch(); let mut buf: [_; 9] = [0x33, 0, 0, 0, 0, 0, 0, 0, 0]; buf[1..].copy_from_slice(&secs.to_bits().to_be_bytes()); self.writer.write_exact(&buf)?; } Value::Integer(v) => { if let Some(v) = v.as_signed() { if v >= 0 && v <= i64::from(u8::max_value()) { self.writer.write_exact(&[0x10, v as u8])?; } else if v >= 0 && v <= i64::from(u16::max_value()) { let mut buf: [_; 3] = [0x11, 0, 0]; buf[1..].copy_from_slice(&(v as u16).to_be_bytes()); self.writer.write_exact(&buf)?; } else if v >= 0 && v <= i64::from(u32::max_value()) { let mut buf: [_; 5] = [0x12, 0, 0, 0, 0]; buf[1..].copy_from_slice(&(v as u32).to_be_bytes()); self.writer.write_exact(&buf)?; } else { let mut buf: [_; 9] = [0x13, 0, 0, 0, 0, 0, 0, 0, 0]; buf[1..].copy_from_slice(&v.to_be_bytes()); self.writer.write_exact(&buf)?; } } else if let Some(v) = v.as_unsigned() { // `u64`s larger than `i64::max_value()` are stored as signed 128 bit // integers. let mut buf: [_; 17] = [0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; buf[1..].copy_from_slice(&i128::from(v).to_be_bytes()); self.writer.write_exact(&buf)?; } else { unreachable!("an integer can be represented as either an i64 or u64"); } } Value::Real(v) => { let mut buf: [_; 9] = [0x23, 0, 0, 0, 0, 0, 0, 0, 0]; buf[1..].copy_from_slice(&v.to_be_bytes()); self.writer.write_exact(&buf)?; } Value::String(v) if v.is_ascii() => { let ascii = v.as_bytes(); write_plist_value_ty_and_size(&mut self.writer, 0x50, ascii.len())?; self.writer.write_exact(ascii)?; } Value::String(v) => { let utf16_len = v.encode_utf16().count(); write_plist_value_ty_and_size(&mut self.writer, 0x60, utf16_len)?; for c in v.encode_utf16() { self.writer.write_exact(&c.to_be_bytes())?; } } Value::Uid(v) => { let v = v.get(); if v <= u64::from(u8::max_value()) { self.writer.write_exact(&[0x80, v as u8])?; } else if v <= u64::from(u16::max_value()) { let mut buf: [_; 3] = [0x81, 0, 0]; buf[1..].copy_from_slice(&(v as u16).to_be_bytes()); self.writer.write_exact(&buf)?; } else if v <= u64::from(u32::max_value()) { let mut buf: [_; 5] = [0x83, 0, 0, 0, 0]; buf[1..].copy_from_slice(&(v as u32).to_be_bytes()); self.writer.write_exact(&buf)?; } else { let mut buf: [_; 9] = [0x87, 0, 0, 0, 0, 0, 0, 0, 0]; buf[1..].copy_from_slice(&(v as u64).to_be_bytes()); self.writer.write_exact(&buf)?; } } } Ok(()) } } impl Writer for BinaryWriter { fn write_start_array(&mut self, _len: Option) -> Result<(), Error> { self.write_start_collection(CollectionType::Array) } fn write_start_dictionary(&mut self, _len: Option) -> Result<(), Error> { self.write_start_collection(CollectionType::Dictionary) } fn write_end_collection(&mut self) -> Result<(), Error> { self.write_end_collection() } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.write_value(Value::Boolean(value)) } fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { self.write_value(Value::Data(Cow::Borrowed(value))) } fn write_date(&mut self, value: Date) -> Result<(), Error> { self.write_value(Value::Date(value)) } fn write_integer(&mut self, value: Integer) -> Result<(), Error> { self.write_value(Value::Integer(value)) } fn write_real(&mut self, value: f64) -> Result<(), Error> { self.write_value(Value::Real(value.to_bits())) } fn write_string(&mut self, value: &str) -> Result<(), Error> { self.write_value(Value::String(Cow::Borrowed(value))) } fn write_uid(&mut self, value: Uid) -> Result<(), Error> { self.write_value(Value::Uid(value)) } } fn is_even(value: usize) -> bool { value & 1 == 0 } fn value_mut<'a>( values: &'a mut IndexMap, ValueState>, value_index: usize, ) -> (&'a mut Value<'static>, &'a mut ValueState) { values .get_index_mut(value_index) .expect("internal consistency error") } fn write_plist_value_ty_and_size( writer: &mut PosWriter, token: u8, size: usize, ) -> Result<(), Error> { if size < 0x0f { writer.write_exact(&[token | (size as u8)])?; } else if size <= u8::max_value() as usize { writer.write_exact(&[token | 0x0f, 0x10, size as u8])?; } else if size <= u16::max_value() as usize { let mut buf: [_; 4] = [token | 0x0f, 0x11, 0, 0]; buf[2..].copy_from_slice(&(size as u16).to_be_bytes()); writer.write_exact(&buf)?; } else if size <= u32::max_value() as usize { let mut buf: [_; 6] = [token | 0x0f, 0x12, 0, 0, 0, 0]; buf[2..].copy_from_slice(&(size as u32).to_be_bytes()); writer.write_exact(&buf)?; } else { let mut buf: [_; 10] = [token | 0x0f, 0x13, 0, 0, 0, 0, 0, 0, 0, 0]; buf[2..].copy_from_slice(&(size as u64).to_be_bytes()); writer.write_exact(&buf)?; } Ok(()) } fn plist_ref_size(max_value: usize) -> u8 { let significant_bits = 64 - (max_value as u64).leading_zeros() as u8; // Convert to number of bytes let significant_bytes = (significant_bits + 7) / 8; // Round up to the next integer byte size which must be power of two. significant_bytes.next_power_of_two() } fn write_plist_ref( writer: &mut PosWriter, ref_size: u8, value: usize, ) -> Result<(), Error> { match ref_size { 1 => writer.write_exact(&[value as u8]), 2 => writer.write_exact(&(value as u16).to_be_bytes()), 4 => writer.write_exact(&(value as u32).to_be_bytes()), 8 => writer.write_exact(&(value as u64).to_be_bytes()), _ => unreachable!("`ref_size` is a power of two less than or equal to 8"), } } impl PosWriter { fn write_exact(&mut self, buf: &[u8]) -> Result<(), Error> { self.write_all(buf) .map_err(error::from_io_without_position)?; Ok(()) } } impl Write for PosWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let count = self.writer.write(buf)?; self.pos = self .pos .checked_add(count) .expect("binary plist cannot be larger than `usize::max_value()` bytes"); Ok(count) } fn flush(&mut self) -> io::Result<()> { self.writer.flush() } } impl ObjectRef { fn zero() -> ObjectRef { ObjectRef(NonZeroUsize::new(1).unwrap()) } fn clone_and_increment_self(&mut self) -> ObjectRef { let current = self.0; self.0 = NonZeroUsize::new(current.get() + 1).unwrap(); ObjectRef(current) } fn value(&self) -> usize { self.0.get() - 1 } } impl<'a> Value<'a> { fn into_owned(self) -> Value<'static> { match self { Value::Boolean(v) => Value::Boolean(v), Value::Data(v) => Value::Data(Cow::Owned(v.into_owned())), Value::Date(v) => Value::Date(v), Value::Integer(v) => Value::Integer(v), Value::Real(v) => Value::Real(v), Value::String(v) => Value::String(Cow::Owned(v.into_owned())), Value::Uid(v) => Value::Uid(v), } } fn event_kind(&self) -> EventKind { match self { Value::Boolean(_) => EventKind::Boolean, Value::Data(_) => EventKind::Data, Value::Date(_) => EventKind::Date, Value::Integer(_) => EventKind::Integer, Value::Real(_) => EventKind::Real, Value::String(_) => EventKind::String, Value::Uid(_) => EventKind::Uid, } } } #[cfg(test)] mod tests { use std::{fs::File, io::Cursor, path::Path}; use crate::{stream::BinaryReader, Value}; fn test_roundtrip(path: &Path) { let reader = File::open(path).unwrap(); let streaming_parser = BinaryReader::new(reader); let value_to_encode = Value::from_events(streaming_parser).unwrap(); let mut buf = Cursor::new(Vec::new()); value_to_encode.to_writer_binary(&mut buf).unwrap(); let buf_inner = buf.into_inner(); let streaming_parser = BinaryReader::new(Cursor::new(buf_inner)); let events: Vec> = streaming_parser.collect(); let value_decoded_from_encode = Value::from_events(events.into_iter()).unwrap(); assert_eq!(value_to_encode, value_decoded_from_encode); } #[test] fn bplist_roundtrip() { test_roundtrip(&Path::new("./tests/data/binary.plist")) } #[test] fn utf16_roundtrip() { test_roundtrip(&Path::new("./tests/data/utf16_bplist.plist")) } #[test] fn nskeyedarchiver_roundtrip() { test_roundtrip(&Path::new("./tests/data/binary_NSKeyedArchiver.plist")) } } plist-1.0.0/src/stream/mod.rs010064400007650000024000000166201352033142500142740ustar0000000000000000//! 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 binary_writer; pub use self::binary_writer::BinaryWriter; mod xml_reader; pub use self::xml_reader::XmlReader; mod xml_writer; pub use self::xml_writer::XmlWriter; use std::{ io::{self, Read, Seek, SeekFrom}, vec, }; use crate::{ dictionary, error::{Error, ErrorKind}, Date, Integer, Uid, 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 /// String("Height") // Key /// Real(181.2) // Value /// String("Age") // Key /// Integer(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), StartDictionary(Option), EndCollection, Boolean(bool), Data(Vec), Date(Date), Integer(Integer), Real(f64), String(String), Uid(Uid), #[doc(hidden)] __Nonexhaustive, } /// An `Event` stream returned by `Value::into_events`. pub struct IntoEvents { stack: Vec, } enum StackItem { Root(Value), Array(vec::IntoIter), Dict(dictionary::IntoIter), DictValue(Value), } impl IntoEvents { pub(crate) fn new(value: Value) -> IntoEvents { IntoEvents { stack: vec![StackItem::Root(value)], } } } impl Iterator for IntoEvents { type Item = Event; fn next(&mut self) -> Option { fn handle_value(value: Value, stack: &mut Vec) -> Event { match value { Value::Array(array) => { let len = array.len(); let iter = array.into_iter(); stack.push(StackItem::Array(iter)); Event::StartArray(Some(len as u64)) } Value::Dictionary(dict) => { let len = dict.len(); let iter = dict.into_iter(); stack.push(StackItem::Dict(iter)); Event::StartDictionary(Some(len as u64)) } Value::Boolean(value) => Event::Boolean(value), Value::Data(value) => Event::Data(value), Value::Date(value) => Event::Date(value), Value::Real(value) => Event::Real(value), Value::Integer(value) => Event::Integer(value), Value::String(value) => Event::String(value), Value::Uid(value) => Event::Uid(value), Value::__Nonexhaustive => unreachable!(), } } Some(match self.stack.pop()? { StackItem::Root(value) => handle_value(value, &mut self.stack), StackItem::Array(mut array) => { if let Some(value) = array.next() { // There might still be more items in the array so return it to the stack. self.stack.push(StackItem::Array(array)); handle_value(value, &mut self.stack) } else { Event::EndCollection } } StackItem::Dict(mut dict) => { if let Some((key, value)) = dict.next() { // There might still be more items in the dictionary so return it to the stack. self.stack.push(StackItem::Dict(dict)); // The next event to be returned must be the dictionary value. self.stack.push(StackItem::DictValue(value)); // Return the key event now. Event::String(key) } else { Event::EndCollection } } StackItem::DictValue(value) => handle_value(value, &mut self.stack), }) } } 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 { fn from_io_offset_0(err: io::Error) -> Error { ErrorKind::Io(err).with_byte_offset(0) } reader.seek(SeekFrom::Start(0)).map_err(from_io_offset_0)?; let mut magic = [0; 8]; reader.read_exact(&mut magic).map_err(from_io_offset_0)?; reader.seek(SeekFrom::Start(0)).map_err(from_io_offset_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: private::Sealed { fn write(&mut self, event: &Event) -> Result<(), Error> { match event { Event::StartArray(len) => self.write_start_array(*len), Event::StartDictionary(len) => self.write_start_dictionary(*len), Event::EndCollection => self.write_end_collection(), Event::Boolean(value) => self.write_boolean(*value), Event::Data(value) => self.write_data(value), Event::Date(value) => self.write_date(*value), Event::Integer(value) => self.write_integer(*value), Event::Real(value) => self.write_real(*value), Event::String(value) => self.write_string(value), Event::Uid(value) => self.write_uid(*value), Event::__Nonexhaustive => unreachable!(), } } fn write_start_array(&mut self, len: Option) -> Result<(), Error>; fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error>; fn write_end_collection(&mut self) -> Result<(), Error>; fn write_boolean(&mut self, value: bool) -> Result<(), Error>; fn write_data(&mut self, value: &[u8]) -> Result<(), Error>; fn write_date(&mut self, value: Date) -> Result<(), Error>; fn write_integer(&mut self, value: Integer) -> Result<(), Error>; fn write_real(&mut self, value: f64) -> Result<(), Error>; fn write_string(&mut self, value: &str) -> Result<(), Error>; fn write_uid(&mut self, value: Uid) -> Result<(), Error>; } pub(crate) mod private { use std::io::Write; pub trait Sealed {} impl Sealed for super::BinaryWriter {} impl Sealed for super::XmlWriter {} } plist-1.0.0/src/stream/xml_reader.rs010064400007650000024000000244221364656172300156550ustar0000000000000000use base64; use std::{ io::{self, Read}, str::FromStr, }; use xml_rs::{ common::{is_whitespace_str, Position}, reader::{ Error as XmlReaderError, ErrorKind as XmlReaderErrorKind, EventReader, ParserConfig, XmlEvent, }, }; use crate::{ error::{Error, ErrorKind, FilePosition}, stream::Event, Date, Integer, }; 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) -> Result { loop { match self.xml_reader.next() { Ok(XmlEvent::Characters(s)) => return Ok(s), Ok(event @ XmlEvent::EndElement { .. }) => { self.queued_event = Some(event); return Ok("".to_owned()); } Ok(XmlEvent::EndDocument) => { return Err(self.with_pos(ErrorKind::UnclosedXmlElement)) } Ok(XmlEvent::StartElement { .. }) => { return Err(self.with_pos(ErrorKind::UnexpectedXmlOpeningTag)); } Ok(XmlEvent::ProcessingInstruction { .. }) => (), Ok(XmlEvent::StartDocument { .. }) | Ok(XmlEvent::CData(_)) | Ok(XmlEvent::Comment(_)) | Ok(XmlEvent::Whitespace(_)) => { unreachable!("parser does not output CData, Comment or Whitespace events"); } Err(err) => return Err(from_xml_error(err)), } } } fn next_event(&mut self) -> Result { if let Some(event) = self.queued_event.take() { Ok(event) } else { self.xml_reader.next() } } fn read_next(&mut self) -> Result, Error> { loop { match self.next_event() { Ok(XmlEvent::StartDocument { .. }) => {} 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 Ok(Some(Event::StartArray(None))), "dict" => return Ok(Some(Event::StartDictionary(None))), "key" => return Ok(Some(Event::String(self.read_content()?))), "true" => return Ok(Some(Event::Boolean(true))), "false" => return Ok(Some(Event::Boolean(false))), "data" => { let mut s = self.read_content()?; // Strip whitespace and line endings from input string s.retain(|c| !c.is_ascii_whitespace()); let data = base64::decode(&s) .map_err(|_| self.with_pos(ErrorKind::InvalidDataString))?; return Ok(Some(Event::Data(data))); } "date" => { let s = self.read_content()?; let date = Date::from_rfc3339(&s) .map_err(|()| self.with_pos(ErrorKind::InvalidDateString))?; return Ok(Some(Event::Date(date))); } "integer" => { let s = self.read_content()?; match Integer::from_str(&s) { Ok(i) => return Ok(Some(Event::Integer(i))), Err(_) => { return Err(self.with_pos(ErrorKind::InvalidIntegerString)) } } } "real" => { let s = self.read_content()?; match f64::from_str(&s) { Ok(f) => return Ok(Some(Event::Real(f))), Err(_) => return Err(self.with_pos(ErrorKind::InvalidRealString)), } } "string" => return Ok(Some(Event::String(self.read_content()?))), _ => return Err(self.with_pos(ErrorKind::UnknownXmlElement)), } } 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 Err(self.with_pos(ErrorKind::UnclosedXmlElement)) } None => return Err(self.with_pos(ErrorKind::UnpairedXmlClosingTag)), } match &name.local_name[..] { "array" | "dict" => return Ok(Some(Event::EndCollection)), "plist" | _ => (), } } Ok(XmlEvent::EndDocument) => { if self.element_stack.is_empty() { return Ok(None); } else { return Err(self.with_pos(ErrorKind::UnclosedXmlElement)); } } Ok(XmlEvent::Characters(c)) => { if !is_whitespace_str(&c) { return Err( self.with_pos(ErrorKind::UnexpectedXmlCharactersExpectedElement) ); } } Ok(XmlEvent::CData(_)) | Ok(XmlEvent::Comment(_)) | Ok(XmlEvent::Whitespace(_)) => { unreachable!("parser does not output CData, Comment or Whitespace events") } Ok(XmlEvent::ProcessingInstruction { .. }) => (), Err(err) => return Err(from_xml_error(err)), } } } fn with_pos(&self, kind: ErrorKind) -> Error { kind.with_position(convert_xml_pos(self.xml_reader.position())) } } impl Iterator for XmlReader { type Item = Result; fn next(&mut self) -> Option> { if self.finished { None } else { match self.read_next() { Ok(Some(event)) => Some(Ok(event)), Ok(None) => { self.finished = true; None } Err(err) => { self.finished = true; Some(Err(err)) } } } } } fn convert_xml_pos(pos: xml_rs::common::TextPosition) -> FilePosition { // TODO: pos.row and pos.column counts from 0. what do we want to do? FilePosition::LineColumn(pos.row, pos.column) } fn from_xml_error(err: XmlReaderError) -> Error { let kind = match err.kind() { XmlReaderErrorKind::Io(err) if err.kind() == io::ErrorKind::UnexpectedEof => { ErrorKind::UnexpectedEof } XmlReaderErrorKind::Io(err) => { let err = if let Some(code) = err.raw_os_error() { io::Error::from_raw_os_error(code) } else { io::Error::new(err.kind(), err.to_string()) }; ErrorKind::Io(err) } XmlReaderErrorKind::Syntax(_) => ErrorKind::InvalidXmlSyntax, XmlReaderErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof, XmlReaderErrorKind::Utf8(_) => ErrorKind::InvalidXmlUtf8, }; kind.with_position(convert_xml_pos(err.position())) } #[cfg(test)] mod tests { use std::{fs::File, path::Path}; use super::*; use crate::stream::Event::{self, *}; #[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), String("Author".to_owned()), String("William Shakespeare".to_owned()), String("Lines".to_owned()), StartArray(None), String("It is a tale told by an idiot,".to_owned()), String("Full of sound and fury, signifying nothing.".to_owned()), EndCollection, String("Death".to_owned()), Integer(1564.into()), String("Height".to_owned()), Real(1.60), String("Data".to_owned()), Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), String("Birthdate".to_owned()), Date(super::Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap()), String("Blank".to_owned()), String("".to_owned()), String("BiggestNumber".to_owned()), Integer(18446744073709551615u64.into()), String("SmallestNumber".to_owned()), Integer((-9223372036854775808i64).into()), String("HexademicalNumber".to_owned()), Integer(0xdead_beef_u64.into()), String("IsTrue".into()), Boolean(true), String("IsNotFalse".into()), Boolean(false), EndCollection, ]; 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-1.0.0/src/stream/xml_writer.rs010064400007650000024000000310101364656172300157160ustar0000000000000000use base64; use line_wrap; use std::{borrow::Cow, io::Write}; use xml_rs::{ name::Name, namespace::Namespace, writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent}, }; use crate::{ error::{self, Error, ErrorKind, EventKind}, stream::Writer, Date, Integer, Uid, }; static XML_PROLOGUE: &str = r#" "#; #[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), }) .map_err(from_xml_error)?; Ok(()) } fn end_element(&mut self, name: &str) -> Result<(), Error> { self.xml_writer .write(XmlEvent::EndElement { name: Some(Name::local(name)), }) .map_err(from_xml_error)?; Ok(()) } fn write_value(&mut self, value: &str) -> Result<(), Error> { self.xml_writer .write(XmlEvent::Characters(value)) .map_err(from_xml_error)?; Ok(()) } pub fn into_inner(self) -> W { self.xml_writer.into_inner() } fn write_event Result<(), Error>>( &mut self, f: F, ) -> Result<(), Error> { if !self.written_prologue { self.xml_writer .inner_mut() .write_all(XML_PROLOGUE.as_bytes()) .map_err(error::from_io_without_position)?; self.written_prologue = true; } f(self)?; // If there are no more open tags then write the element if self.stack.is_empty() { // 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") .map_err(error::from_io_without_position)?; self.xml_writer .inner_mut() .flush() .map_err(error::from_io_without_position)?; } Ok(()) } fn write_value_event Result<(), Error>>( &mut self, event_kind: EventKind, f: F, ) -> Result<(), Error> { self.write_event(|this| { if this.expecting_key { return Err(ErrorKind::UnexpectedEventType { expected: EventKind::DictionaryKeyOrEndCollection, found: event_kind, } .without_position()); } f(this)?; this.expecting_key = this.stack.last() == Some(&Element::Dictionary); Ok(()) }) } } impl Writer for XmlWriter { fn write_start_array(&mut self, _len: Option) -> Result<(), Error> { self.write_value_event(EventKind::StartArray, |this| { this.start_element("array")?; this.stack.push(Element::Array); Ok(()) }) } fn write_start_dictionary(&mut self, _len: Option) -> Result<(), Error> { self.write_value_event(EventKind::StartDictionary, |this| { this.start_element("dict")?; this.stack.push(Element::Dictionary); Ok(()) }) } fn write_end_collection(&mut self) -> Result<(), Error> { self.write_event(|this| { match (this.stack.pop(), this.expecting_key) { (Some(Element::Dictionary), true) => { this.end_element("dict")?; } (Some(Element::Array), _) => { this.end_element("array")?; } (Some(Element::Dictionary), false) | (None, _) => { return Err(ErrorKind::UnexpectedEventType { expected: EventKind::ValueOrStartCollection, found: EventKind::EndCollection, } .without_position()); } } this.expecting_key = this.stack.last() == Some(&Element::Dictionary); Ok(()) }) } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.write_value_event(EventKind::Boolean, |this| { let value_str = if value { "true" } else { "false" }; this.start_element(value_str)?; this.end_element(value_str) }) } fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { self.write_value_event(EventKind::Data, |this| { let base64_data = base64_encode_plist(&value, this.stack.len()); this.write_element_and_value("data", &base64_data) }) } fn write_date(&mut self, value: Date) -> Result<(), Error> { self.write_value_event(EventKind::Date, |this| { this.write_element_and_value("date", &value.to_rfc3339()) }) } fn write_integer(&mut self, value: Integer) -> Result<(), Error> { self.write_value_event(EventKind::Integer, |this| { this.write_element_and_value("integer", &value.to_string()) }) } fn write_real(&mut self, value: f64) -> Result<(), Error> { self.write_value_event(EventKind::Real, |this| { this.write_element_and_value("real", &value.to_string()) }) } fn write_string(&mut self, value: &str) -> Result<(), Error> { self.write_event(|this| { if this.expecting_key { this.write_element_and_value("key", &*value)?; this.expecting_key = false; } else { this.write_element_and_value("string", &*value)?; this.expecting_key = this.stack.last() == Some(&Element::Dictionary); } Ok(()) }) } fn write_uid(&mut self, _value: Uid) -> Result<(), Error> { Err(ErrorKind::UidNotSupportedInXmlPlist.without_position()) } } pub(crate) fn from_xml_error(err: XmlWriterError) -> Error { match err { XmlWriterError::Io(err) => ErrorKind::Io(err).without_position(), XmlWriterError::DocumentStartAlreadyEmitted | XmlWriterError::LastElementNameNotAvailable | XmlWriterError::EndElementNameIsNotEqualToLastStartElementName | XmlWriterError::EndElementNameIsNotSpecified => unreachable!(), } } 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 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 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 std::io::Cursor; use super::*; use crate::stream::Event; #[test] fn streaming_parser() { let plist = &[ Event::StartDictionary(None), Event::String("Author".to_owned()), Event::String("William Shakespeare".to_owned()), Event::String("Lines".to_owned()), Event::StartArray(None), Event::String("It is a tale told by an idiot,".to_owned()), Event::String("Full of sound and fury, signifying nothing.".to_owned()), Event::Data((0..128).collect::>()), Event::EndCollection, Event::String("Death".to_owned()), Event::Integer(1564.into()), Event::String("Height".to_owned()), Event::Real(1.60), Event::String("Data".to_owned()), Event::Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), Event::String("Birthdate".to_owned()), Event::Date(super::Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap()), Event::String("Comment".to_owned()), Event::String("2 < 3".to_owned()), // make sure characters are escaped Event::String("BiggestNumber".to_owned()), Event::Integer(18446744073709551615u64.into()), Event::String("SmallestNumber".to_owned()), Event::Integer((-9223372036854775808i64).into()), Event::String("IsTrue".into()), Event::Boolean(true), Event::String("IsNotFalse".into()), Event::Boolean(false), Event::EndCollection, ]; 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 \tBiggestNumber \t18446744073709551615 \tSmallestNumber \t-9223372036854775808 \tIsTrue \t \tIsNotFalse \t "; let s = String::from_utf8(cursor.into_inner()).unwrap(); assert_eq!(s, comparison); } } plist-1.0.0/src/uid.rs010064400007650000024000000042411351556162500130120ustar0000000000000000use std::fmt; /// A plist `uid` value. These are found exclusively in plists created by `NSKeyedArchiver`. #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct Uid { value: u64, } impl Uid { /// Creates a new `Uid` containing the given value. pub fn new(value: u64) -> Uid { Uid { value } } /// Returns the value as a `u64`. pub fn get(self) -> u64 { self.value } } impl fmt::Debug for Uid { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.value.fmt(f) } } #[cfg(feature = "serde")] pub mod serde_impls { use serde::{ de::{Deserialize, Deserializer, Error, Visitor}, ser::{Serialize, Serializer}, }; use std::fmt; use crate::Uid; pub const UID_NEWTYPE_STRUCT_NAME: &str = "PLIST-UID"; impl Serialize for Uid { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_newtype_struct(UID_NEWTYPE_STRUCT_NAME, &self.get()) } } struct UidNewtypeVisitor; impl<'de> Visitor<'de> for UidNewtypeVisitor { type Value = Uid; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a plist uid") } fn visit_newtype_struct(self, deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(UidU64Visitor) } } struct UidU64Visitor; impl<'de> Visitor<'de> for UidU64Visitor { type Value = Uid; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a plist uid") } fn visit_u64(self, v: u64) -> Result where E: Error, { Ok(Uid::new(v)) } } impl<'de> Deserialize<'de> for Uid { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_newtype_struct(UID_NEWTYPE_STRUCT_NAME, UidNewtypeVisitor) } } } plist-1.0.0/src/value.rs010064400007650000024000000427621364656172300133630ustar0000000000000000use std::{ fs::File, io::{BufReader, BufWriter, Read, Seek, Write}, path::Path, }; use crate::{ error::{self, Error, ErrorKind, EventKind}, stream::{BinaryWriter, Event, IntoEvents, Reader, Writer, XmlReader, XmlWriter}, u64_to_usize, Date, Dictionary, Integer, Uid, }; /// Represents any plist value. #[derive(Clone, Debug, PartialEq)] pub enum Value { Array(Vec), Dictionary(Dictionary), Boolean(bool), Data(Vec), Date(Date), Real(f64), Integer(Integer), String(String), Uid(Uid), #[doc(hidden)] __Nonexhaustive, } impl Value { /// Reads a `Value` from a plist file of any encoding. pub fn from_file>(path: P) -> Result { let file = File::open(path).map_err(error::from_io_without_position)?; Value::from_reader(BufReader::new(file)) } /// Reads a `Value` from a seekable byte stream containing a plist 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. pub fn from_reader_xml(reader: R) -> Result { let reader = XmlReader::new(reader); Value::from_events(reader) } /// Serializes a `Value` to a file as a binary encoded plist. pub fn to_file_binary>(&self, path: P) -> Result<(), Error> { let mut file = File::create(path).map_err(error::from_io_without_position)?; self.to_writer_binary(BufWriter::new(&mut file))?; file.sync_all().map_err(error::from_io_without_position)?; Ok(()) } /// Serializes a `Value` to a file as an XML encoded plist. pub fn to_file_xml>(&self, path: P) -> Result<(), Error> { let mut file = File::create(path).map_err(error::from_io_without_position)?; self.to_writer_xml(BufWriter::new(&mut file))?; file.sync_all().map_err(error::from_io_without_position)?; Ok(()) } /// Serializes a `Value` to a byte stream as a binary encoded plist. pub fn to_writer_binary(&self, writer: W) -> Result<(), Error> { let mut writer = BinaryWriter::new(writer); self.to_writer_inner(&mut writer) } /// Serializes a `Value` to a byte stream as an XML encoded plist. pub fn to_writer_xml(&self, writer: W) -> Result<(), Error> { let mut writer = XmlWriter::new(writer); self.to_writer_inner(&mut writer) } fn to_writer_inner(&self, writer: &mut dyn Writer) -> Result<(), Error> { let events = self.clone().into_events(); for event in events { writer.write(&event)?; } Ok(()) } /// Builds a single `Value` from an `Event` iterator. /// On success any excess `Event`s will remain in the iterator. #[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")] pub fn from_events(events: T) -> Result where T: IntoIterator>, { Builder::new(events.into_iter()).build() } /// Builds a single `Value` from an `Event` iterator. /// On success any excess `Event`s will remain in the iterator. #[cfg(not(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps"))] pub(crate) fn from_events(events: T) -> Result where T: IntoIterator>, { Builder::new(events.into_iter()).build() } /// Converts a `Value` into an `Event` iterator. #[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")] pub fn into_events(self) -> IntoEvents { IntoEvents::new(self) } /// Converts a `Value` into an `Event` iterator. #[cfg(not(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps"))] pub(crate) fn into_events(self) -> IntoEvents { IntoEvents::new(self) } /// If the `Value` is a Array, returns the underlying `Vec`. /// /// Returns `None` otherwise. /// /// This method consumes the `Value`. To get a reference instead, use /// `as_array`. pub fn into_array(self) -> Option> { match self { Value::Array(dict) => Some(dict), _ => None, } } /// 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. /// /// This method consumes the `Value`. To get a reference instead, use /// `as_dictionary`. pub fn into_dictionary(self) -> Option { match self { Value::Dictionary(dict) => Some(dict), _ => None, } } /// If the `Value` is a Dictionary, returns the associated `BTreeMap`. /// /// Returns `None` otherwise. pub fn as_dictionary(&self) -> Option<&Dictionary> { match *self { Value::Dictionary(ref dict) => Some(dict), _ => None, } } /// If the `Value` is a Dictionary, returns the associated mutable `BTreeMap`. /// /// Returns `None` otherwise. pub fn as_dictionary_mut(&mut self) -> Option<&mut Dictionary> { match *self { Value::Dictionary(ref mut dict) => Some(dict), _ => 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 a signed Integer, returns the associated `i64`. /// /// Returns `None` otherwise. pub fn as_signed_integer(&self) -> Option { match *self { Value::Integer(v) => v.as_signed(), _ => None, } } /// If the `Value` is an unsigned Integer, returns the associated `u64`. /// /// Returns `None` otherwise. pub fn as_unsigned_integer(&self) -> Option { match *self { Value::Integer(v) => v.as_unsigned(), _ => 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: Dictionary) -> 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(Integer::from(from)) } } impl From for Value { fn from(from: i32) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: i16) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: i8) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u64) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u32) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u16) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u8) -> Value { Value::Integer(Integer::from(from)) } } 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(Integer::from(*from)) } } impl<'a> From<&'a i32> for Value { fn from(from: &'a i32) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a i16> for Value { fn from(from: &'a i16) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a i8> for Value { fn from(from: &'a i8) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a u64> for Value { fn from(from: &'a u64) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a u32> for Value { fn from(from: &'a u32) -> Value { Value::Integer(Integer::from(*from)) } } 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()?; self.build_value() } 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::Boolean(b)) => Ok(Value::Boolean(b)), Some(Event::Data(d)) => Ok(Value::Data(d)), Some(Event::Date(d)) => Ok(Value::Date(d)), Some(Event::Integer(i)) => Ok(Value::Integer(i)), Some(Event::Real(f)) => Ok(Value::Real(f)), Some(Event::String(s)) => Ok(Value::String(s)), Some(Event::Uid(u)) => Ok(Value::Uid(u)), Some(event @ Event::EndCollection) => Err(error::unexpected_event_type( EventKind::ValueOrStartCollection, &event, )), Some(Event::__Nonexhaustive) => unreachable!(), None => Err(ErrorKind::UnexpectedEndOfEventStream.without_position()), } } 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::EndCollection) = self.token { self.token.take(); return Ok(values); } values.push(self.build_value()?); } } fn build_dict(&mut self, _len: Option) -> Result { let mut dict = Dictionary::new(); loop { self.bump()?; match self.token.take() { Some(Event::EndCollection) => return Ok(dict), Some(Event::String(s)) => { self.bump()?; dict.insert(s, self.build_value()?); } Some(event) => { return Err(error::unexpected_event_type( EventKind::DictionaryKeyOrEndCollection, &event, )) } None => return Err(ErrorKind::UnexpectedEndOfEventStream.without_position()), } } } } #[cfg(test)] mod tests { use std::time::SystemTime; use super::*; use crate::{stream::Event::*, Date, Dictionary, 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 = Dictionary::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.into()).as_signed_integer(), Some(1)); assert_eq!(Value::Integer(1.into()).as_unsigned_integer(), Some(1)); assert_eq!(Value::Integer((-1).into()).as_unsigned_integer(), None); assert_eq!( Value::Integer((i64::max_value() as u64 + 1).into()).as_signed_integer(), None ); 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), String("Author".to_owned()), String("William Shakespeare".to_owned()), String("Lines".to_owned()), StartArray(None), String("It is a tale told by an idiot,".to_owned()), String("Full of sound and fury, signifying nothing.".to_owned()), EndCollection, String("Birthdate".to_owned()), Integer(1564.into()), String("Height".to_owned()), Real(1.60), EndCollection, ]; 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 = Dictionary::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.into())); dict.insert("Height".to_owned(), Value::Real(1.60)); assert_eq!(plist.unwrap(), Value::Dictionary(dict)); } } plist-1.0.0/tests/data/binary.plist010064400007650000024000000006461364656172300155210ustar0000000000000000bplist00 VAuthorYBirthdateZEmptyArrayZIsNotFalse^SmallestNumber_EmptyDictionaryVHeightULinesUDeathUBlank]BiggestNumberVIsTrueTData_William Shakespeare3ve#?_It is a tale told by an idiot,_+Full of sound and fury, signifying nothing.P O#*4?JYkrx~$'(9:Lplist-1.0.0/tests/data/binary_circular_array.plist010064400007650000024000000007031310304000700205440ustar0000000000000000bplist00 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-1.0.0/tests/data/binary_NSKeyedArchiver.plist010064400007650000024000000007501351556162500205570ustar0000000000000000bplist00 X$versionX$objectsY$archiverT$topU$null \NSRangeCountV$class[NSRangeData* WNS.dataOg ,035<@GLSV[dox|@Z$classnameX$classes]NSMutableDataVNSDataXNSObject_NSMutableIndexSet_NSMutableIndexSetZNSIndexSet_NSKeyedArchiver!"ZfoundItems#-27=CJW^jlnpu}$)=AU`ru#plist-1.0.0/tests/data/binary_zero_offset_size.plist010064400007650000024000000004241310303546700211370ustar0000000000000000bplist00 ULinesUDeathVHeightYBirthdate signifying nthing.#?#?3ve_William ShakespeareO!(29>Abte signifying nthing.#?#?3ve_William ShakespeareO!(plist-1.0.0/tests/data/book.plist010064400007650000024000000012211352034333700151430ustar0000000000000000 Title Great Expectations Author Charles Dickens Excerpt Whether I should have made out this object so soon, if there had been no fine lady sitting at it, I cannot say. In an armchair, with an elbow resting on the table and her head leaning on that hand, sat the strangest lady I have ever seen, or shall ever see. CopiesSold 123456789 plist-1.0.0/tests/data/utf16_bplist.plist010064400007650000024000000025421276362066400165530ustar0000000000000000bplist00TnameXlongTextk& 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-1.0.0/tests/data/xml.plist010064400007650000024000000016241364656172300150320ustar0000000000000000 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 BiggestNumber 18446744073709551615 SmallestNumber -9223372036854775808 HexademicalNumber 0xDEADBEEF IsTrue IsNotFalse plist-1.0.0/tests/data/xml_error.plist010064400007650000024000000007411260060366100162250ustar0000000000000000 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]) { test_fuzzer_data(data).unwrap(); } fn test_fuzzer_data_err(data: &[u8]) { assert!(test_fuzzer_data(data).is_err()); } plist-1.0.0/.cargo_vcs_info.json0000644000000001121364657031000122620ustar00{ "git": { "sha1": "f27f779f153f2faca2a850eda660602968d31ade" } }