summaryrefslogtreecommitdiffstats
path: root/third_party/rust/plist
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/plist')
-rw-r--r--third_party/rust/plist/.cargo-checksum.json1
-rw-r--r--third_party/rust/plist/Cargo.toml51
-rw-r--r--third_party/rust/plist/LICENCE19
-rw-r--r--third_party/rust/plist/README.md7
-rw-r--r--third_party/rust/plist/rustfmt.toml1
-rw-r--r--third_party/rust/plist/src/date.rs190
-rw-r--r--third_party/rust/plist/src/de.rs427
-rw-r--r--third_party/rust/plist/src/dictionary.rs757
-rw-r--r--third_party/rust/plist/src/error.rs220
-rw-r--r--third_party/rust/plist/src/integer.rs202
-rw-r--r--third_party/rust/plist/src/lib.rs133
-rw-r--r--third_party/rust/plist/src/ser.rs804
-rw-r--r--third_party/rust/plist/src/serde_tests.rs887
-rw-r--r--third_party/rust/plist/src/stream/binary_reader.rs492
-rw-r--r--third_party/rust/plist/src/stream/binary_writer.rs736
-rw-r--r--third_party/rust/plist/src/stream/mod.rs266
-rw-r--r--third_party/rust/plist/src/stream/xml_reader.rs275
-rw-r--r--third_party/rust/plist/src/stream/xml_writer.rs391
-rw-r--r--third_party/rust/plist/src/uid.rs97
-rw-r--r--third_party/rust/plist/src/value.rs816
-rw-r--r--third_party/rust/plist/tests/data/binary.plistbin0 -> 422 bytes
-rw-r--r--third_party/rust/plist/tests/data/binary_NSKeyedArchiver.plistbin0 -> 488 bytes
-rw-r--r--third_party/rust/plist/tests/data/binary_circular_array.plistbin0 -> 451 bytes
-rw-r--r--third_party/rust/plist/tests/data/binary_zero_offset_size.plistbin0 -> 276 bytes
-rw-r--r--third_party/rust/plist/tests/data/book.plist14
-rw-r--r--third_party/rust/plist/tests/data/utf16_bplist.plistbin0 -> 1378 bytes
-rw-r--r--third_party/rust/plist/tests/data/xml.plist36
-rw-r--r--third_party/rust/plist/tests/data/xml_error.plist17
-rw-r--r--third_party/rust/plist/tests/fuzzer.rs87
29 files changed, 6926 insertions, 0 deletions
diff --git a/third_party/rust/plist/.cargo-checksum.json b/third_party/rust/plist/.cargo-checksum.json
new file mode 100644
index 0000000000..8c6ea304de
--- /dev/null
+++ b/third_party/rust/plist/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"2bd39244fcce5c34bc384f3ffc5100c8f50c46d4f8d32322998a7ed0d0ed1f47","LICENCE":"5b0ae40d1a35f7ae6591a28e44771240e6a88cb03a66c9189a45b9681639b466","README.md":"25091b2b53ea578f6fc369259070938eaeea06a0b7ba7a81b50da503198345ed","rustfmt.toml":"9bbb759b3914c49c8060bcdeeb7786f4aec083d30bcfe236bbd48d8388d06103","src/date.rs":"6bb495efde6e4d02dc0886f5489b83cfb2dfe4dbd2d491572a871d75ae185caa","src/de.rs":"6c68d93d533661f48e7a5e3ded576e64889a9df121ab7afadd7604b6585fb0dd","src/dictionary.rs":"cb4cde524eb4bcdcee5703429a6ce64234afce17cd0d37d0ff61c6224e8c928e","src/error.rs":"0d8975a4db0acfd4a2c636560780b3d6d7d8a008beaf0982a18de5e5c793ff36","src/integer.rs":"131f528878adc5c0f33c8ad1563c06b397cb2ae5148c2bb9318203a2477d70e0","src/lib.rs":"893b8c3e019c772edc116c907069eeb55874f245fb26d71b95d6f25c938e00f6","src/ser.rs":"960ee6ecc00a2b0478b1e2f5e65fd192af9474af9921c1b871659707ac4a33a5","src/serde_tests.rs":"905a97806256e005b8fe10bca57e371475e98c029ecb46cb6eda3879855edaa0","src/stream/binary_reader.rs":"c2506a3f62dbaf23e8238969222d1582f04cf74d333cc94d766a74f717d5eab8","src/stream/binary_writer.rs":"1b10bbf287ad48be3a82c36cebbc32f7232fff42995142e09f98d2a2eb1fb628","src/stream/mod.rs":"1442640f5f4d2ae1c3d7319bc96d99765c72906c463c093430f44cdd7a56c5e1","src/stream/xml_reader.rs":"5461e9601ad4e4a2853bab6ffb24062f9053f89a33cbcb91eca9566b20183354","src/stream/xml_writer.rs":"b39b4d67c62233b2761573b5c1256448c4af21d3deca4391b0e7d15cc015dac8","src/uid.rs":"3c9770e8e17767f8cf2dab886839f620b70054f418d3b02c9729f33c96f14925","src/value.rs":"44e23dfd9efb998e339556428de1dbba0b77bf2e59ba1d157df17520d8e11acb","tests/data/binary.plist":"728985bb53486a92fdcc901a3c08a904835f0225efab7b85be2734e2dcd4af83","tests/data/binary_NSKeyedArchiver.plist":"54dc8b87acd364de6b2160ff805514bc04bf0577e4f57d08d7f3578d218362f0","tests/data/binary_circular_array.plist":"825aed6ce83a8abdbe15d4686ce35157f4eb861bd792f0ce790115fb4ec48805","tests/data/binary_zero_offset_size.plist":"020953c8211989d01b5edf42e025560f46ece3b604ceda03708819bd2587a1a1","tests/data/book.plist":"3b18050f073cab956f38f1775e89dedc88d1e56dd388bc99e277efb19e50dffd","tests/data/utf16_bplist.plist":"c0b7d33001021df98d8631c604c8471e74e4b4599bac51a8bed149ca82adbcd5","tests/data/xml.plist":"9669e8ad25a661ca052d30d8d74b7495e859a0a7faf01f2aeade7fcebb2aa5b7","tests/data/xml_error.plist":"3718f7dd2c716a4a6c36d1f7055b78d86c982c812c19964f85a6f62eff1589ea","tests/fuzzer.rs":"01222f75eedb0b09ffbc4970a0f5598103d71447612aed791dc7f922ee87ad67"},"package":"bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225"} \ No newline at end of file
diff --git a/third_party/rust/plist/Cargo.toml b/third_party/rust/plist/Cargo.toml
new file mode 100644
index 0000000000..4829298f4f
--- /dev/null
+++ b/third_party/rust/plist/Cargo.toml
@@ -0,0 +1,51 @@
+# 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 are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "plist"
+version = "1.3.1"
+authors = ["Ed Barnard <eabarnard@gmail.com>"]
+description = "A rusty plist parser. Supports Serde serialization."
+documentation = "https://docs.rs/plist/1.3.1/plist/"
+keywords = ["plist", "parser"]
+categories = ["config", "encoding", "parser-implementations"]
+license = "MIT"
+repository = "https://github.com/ebarnard/rust-plist/"
+[dependencies.base64]
+version = "0.13.0"
+
+[dependencies.indexmap]
+version = "1.0.2"
+
+[dependencies.line-wrap]
+version = "0.1.1"
+
+[dependencies.serde]
+version = "1.0.2"
+optional = true
+
+[dependencies.time]
+version = "0.3.3"
+features = ["parsing", "formatting"]
+
+[dependencies.xml_rs]
+version = "0.8.2"
+package = "xml-rs"
+[dev-dependencies.serde_derive]
+version = "1.0.2"
+
+[dev-dependencies.serde_yaml]
+version = "0.8.21"
+
+[features]
+default = ["serde"]
+enable_unstable_features_that_may_break_with_minor_version_bumps = []
diff --git a/third_party/rust/plist/LICENCE b/third_party/rust/plist/LICENCE
new file mode 100644
index 0000000000..33953bb617
--- /dev/null
+++ b/third_party/rust/plist/LICENCE
@@ -0,0 +1,19 @@
+Copyright (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. \ No newline at end of file
diff --git a/third_party/rust/plist/README.md b/third_party/rust/plist/README.md
new file mode 100644
index 0000000000..2a3f9eb3fa
--- /dev/null
+++ b/third_party/rust/plist/README.md
@@ -0,0 +1,7 @@
+# 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.
+
+[Documentation](https://docs.rs/plist/)
diff --git a/third_party/rust/plist/rustfmt.toml b/third_party/rust/plist/rustfmt.toml
new file mode 100644
index 0000000000..7d2cf549dc
--- /dev/null
+++ b/third_party/rust/plist/rustfmt.toml
@@ -0,0 +1 @@
+merge_imports = true
diff --git a/third_party/rust/plist/src/date.rs b/third_party/rust/plist/src/date.rs
new file mode 100644
index 0000000000..ff9839243b
--- /dev/null
+++ b/third_party/rust/plist/src/date.rs
@@ -0,0 +1,190 @@
+use std::{
+ fmt,
+ time::{Duration, SystemTime, UNIX_EPOCH},
+};
+use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset};
+
+/// 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<Self, ()> {
+ let offset: OffsetDateTime = OffsetDateTime::parse(date, &Rfc3339)
+ .map_err(|_| ())?
+ .to_offset(UtcOffset::UTC);
+ Ok(Date {
+ inner: offset.into(),
+ })
+ }
+
+ pub(crate) fn to_rfc3339(&self) -> String {
+ let datetime: OffsetDateTime = self.inner.into();
+ datetime.format(&Rfc3339).unwrap()
+ }
+
+ pub(crate) fn from_seconds_since_plist_epoch(
+ timestamp: f64,
+ ) -> Result<Date, InfiniteOrNanDate> {
+ // `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.checked_sub(dur_since_plist_epoch)
+ } else {
+ plist_epoch.checked_add(dur_since_plist_epoch)
+ };
+
+ let inner = inner.ok_or(InfiniteOrNanDate)?;
+
+ 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<SystemTime> for Date {
+ fn from(date: SystemTime) -> Self {
+ Date { inner: date }
+ }
+}
+
+impl Into<SystemTime> 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ 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_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ DateStrVisitor.visit_str(v)
+ }
+
+ fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+ 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<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Date::from_rfc3339(v).map_err(|()| E::invalid_value(Unexpected::Str(v), &self))
+ }
+ }
+
+ impl<'de> Deserialize<'de> for Date {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ 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");
+ }
+}
diff --git a/third_party/rust/plist/src/de.rs b/third_party/rust/plist/src/de.rs
new file mode 100644
index 0000000000..12bdb06fa3
--- /dev/null
+++ b/third_party/rust/plist/src/de.rs
@@ -0,0 +1,427 @@
+use serde::de::{
+ self,
+ value::{MapAccessDeserializer, MapDeserializer},
+ IntoDeserializer,
+};
+use std::{
+ array::IntoIter,
+ fmt::Display,
+ fs::File,
+ io::{BufReader, Cursor, Read, Seek},
+ iter::Peekable,
+ mem,
+ path::Path,
+};
+
+use crate::{
+ date::serde_impls::DATE_NEWTYPE_STRUCT_NAME,
+ error::{self, Error, ErrorKind, EventKind},
+ stream::{self, Event, OwnedEvent},
+ u64_to_usize,
+ uid::serde_impls::UID_NEWTYPE_STRUCT_NAME,
+ value::serde_impls::VALUE_NEWTYPE_STRUCT_NAME,
+};
+
+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<T: Display>(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<I>
+where
+ I: IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ events: Peekable<<I as IntoIterator>::IntoIter>,
+ option_mode: OptionMode,
+ in_plist_value: bool,
+}
+
+impl<I> Deserializer<I>
+where
+ I: IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ pub fn new(iter: I) -> Deserializer<I> {
+ Deserializer {
+ events: iter.into_iter().peekable(),
+ option_mode: OptionMode::Root,
+ in_plist_value: false,
+ }
+ }
+
+ fn with_option_mode<T, F: FnOnce(&mut Deserializer<I>) -> Result<T, Error>>(
+ &mut self,
+ option_mode: OptionMode,
+ f: F,
+ ) -> Result<T, Error> {
+ let prev_option_mode = mem::replace(&mut self.option_mode, option_mode);
+ let ret = f(&mut *self);
+ self.option_mode = prev_option_mode;
+ ret
+ }
+
+ fn enter_plist_value<T, F: FnOnce(&mut Deserializer<I>) -> Result<T, Error>>(
+ &mut self,
+ f: F,
+ ) -> Result<T, Error> {
+ let prev = mem::replace(&mut self.in_plist_value, true);
+ let ret = f(&mut *self);
+ self.in_plist_value = prev;
+ ret
+ }
+}
+
+impl<'de, 'a, I> de::Deserializer<'de> for &'a mut Deserializer<I>
+where
+ I: IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+ 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.into_owned()),
+ Event::Date(v) if self.in_plist_value => {
+ visitor.visit_enum(MapAccessDeserializer::new(MapDeserializer::new(
+ IntoIter::new([(DATE_NEWTYPE_STRUCT_NAME, v.to_rfc3339())]),
+ )))
+ }
+ 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.into_owned()),
+ Event::Uid(v) if self.in_plist_value => visitor.visit_enum(MapAccessDeserializer::new(
+ MapDeserializer::new(IntoIter::new([(UID_NEWTYPE_STRUCT_NAME, v.get())])),
+ )),
+ Event::Uid(v) => visitor.visit_u64(v.get()),
+ }
+ }
+
+ 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<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ expect!(self.events.next(), EventKind::String);
+ visitor.visit_unit()
+ }
+
+ fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ match self.option_mode {
+ OptionMode::Root => {
+ if self.events.peek().is_none() {
+ visitor.visit_none::<Error>()
+ } 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::<Error>()?
+ }
+ 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<V>(
+ self,
+ name: &'static str,
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ if name == VALUE_NEWTYPE_STRUCT_NAME {
+ self.enter_plist_value(|this| visitor.visit_newtype_struct(this))
+ } else {
+ visitor.visit_newtype_struct(self)
+ }
+ }
+
+ fn deserialize_struct<V>(
+ self,
+ _name: &'static str,
+ _fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ 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<V>(
+ self,
+ name: &'static str,
+ variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ // `plist` since v1.1 serialises unit enum variants as plain strings.
+ if let Some(Ok(Event::String(s))) = self.events.peek() {
+ return s
+ .as_ref()
+ .into_deserializer()
+ .deserialize_enum(name, variants, visitor);
+ }
+
+ 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<I>
+where
+ I: IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ type Error = Error;
+ type Variant = Self;
+
+ fn variant_seed<V>(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<I>
+where
+ I: IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ type Error = Error;
+
+ fn unit_variant(self) -> Result<(), Error> {
+ <() as de::Deserialize>::deserialize(self)
+ }
+
+ fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Error>
+ where
+ T: de::DeserializeSeed<'de>,
+ {
+ seed.deserialize(self)
+ }
+
+ fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ de::Deserializer::deserialize_tuple(self, len, visitor)
+ }
+
+ fn struct_variant<V>(
+ self,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ let name = "";
+ de::Deserializer::deserialize_struct(self, name, fields, visitor)
+ }
+}
+
+struct MapAndSeqAccess<'a, I>
+where
+ I: 'a + IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ de: &'a mut Deserializer<I>,
+ is_struct: bool,
+ remaining: Option<usize>,
+}
+
+impl<'a, I> MapAndSeqAccess<'a, I>
+where
+ I: 'a + IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ fn new(
+ de: &'a mut Deserializer<I>,
+ is_struct: bool,
+ len: Option<usize>,
+ ) -> MapAndSeqAccess<'a, I> {
+ MapAndSeqAccess {
+ de,
+ is_struct,
+ remaining: len,
+ }
+ }
+}
+
+impl<'de, 'a, I> de::SeqAccess<'de> for MapAndSeqAccess<'a, I>
+where
+ I: 'a + IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ type Error = Error;
+
+ fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, 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<usize> {
+ self.remaining
+ }
+}
+
+impl<'de, 'a, I> de::MapAccess<'de> for MapAndSeqAccess<'a, I>
+where
+ I: 'a + IntoIterator<Item = Result<OwnedEvent, Error>>,
+{
+ type Error = Error;
+
+ fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, 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<V>(&mut self, seed: V) -> Result<V::Value, Error>
+ 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<usize> {
+ self.remaining
+ }
+}
+
+/// Deserializes an instance of type `T` from a byte slice.
+pub fn from_bytes<T: de::DeserializeOwned>(bytes: &[u8]) -> Result<T, Error> {
+ 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<P: AsRef<Path>, T: de::DeserializeOwned>(path: P) -> Result<T, Error> {
+ 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<R: Read + Seek, T: de::DeserializeOwned>(reader: R) -> Result<T, Error> {
+ 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<R: Read, T: de::DeserializeOwned>(reader: R) -> Result<T, Error> {
+ let reader = stream::XmlReader::new(reader);
+ let mut de = Deserializer::new(reader);
+ de::Deserialize::deserialize(&mut de)
+}
diff --git a/third_party/rust/plist/src/dictionary.rs b/third_party/rust/plist/src/dictionary.rs
new file mode 100644
index 0000000000..0e2b937c36
--- /dev/null
+++ b/third_party/rust/plist/src/dictionary.rs
@@ -0,0 +1,757 @@
+//! 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 indexmap::{map, IndexMap};
+use std::{
+ fmt::{self, Debug},
+ iter::FromIterator,
+ ops,
+};
+
+use crate::Value;
+
+/// Represents a plist dictionary type.
+pub struct Dictionary {
+ map: IndexMap<String, Value>,
+}
+
+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<Value> {
+ 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<Value> {
+ self.map.remove(key)
+ }
+
+ /// Scan through each key-value pair in the map and keep those where the
+ /// closure `keep` returns `true`.
+ #[inline]
+ pub fn retain<F>(&mut self, keep: F)
+ where
+ F: FnMut(&String, &mut Value) -> bool,
+ {
+ self.map.retain(keep)
+ }
+
+ /// Sort the dictionary keys.
+ ///
+ /// This uses the default ordering defined on [`str`].
+ ///
+ /// This function is useful if you are serializing to XML, and wish to
+ /// ensure a consistent key order.
+ #[inline]
+ pub fn sort_keys(&mut self) {
+ self.map.sort_keys()
+ }
+
+ /// 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<str> or
+ // T: AsRef<str> + Into<String>
+ #[cfg(any(
+ test,
+ feature = "enable_unstable_features_that_may_break_with_minor_version_bumps"
+ ))]
+ pub fn entry<S>(&mut self, key: S) -> Entry
+ where
+ S: Into<String>,
+ {
+ 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<K: Into<String>, V: Into<Value>> FromIterator<(K, V)> for Dictionary {
+ fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
+ Dictionary {
+ map: iter
+ .into_iter()
+ .map(|(k, v)| (k.into(), v.into()))
+ .collect(),
+ }
+ }
+}
+
+impl Extend<(String, Value)> for Dictionary {
+ fn extend<T>(&mut self, iter: T)
+ where
+ T: IntoIterator<Item = (String, Value)>,
+ {
+ 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::Item> {
+ self.iter.next()
+ }
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+ }
+
+ /*impl $($generics)* DoubleEndedIterator for $name $($generics)* {
+ #[inline]
+ fn next_back(&mut self) -> Option<Self::Item> {
+ 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<F>(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<String, Value>,
+}
+
+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);
+
+#[cfg(feature = "serde")]
+pub mod serde_impls {
+ use serde::{de, ser};
+ use std::fmt;
+
+ use crate::Dictionary;
+
+ impl ser::Serialize for Dictionary {
+ #[inline]
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ use serde::ser::SerializeMap;
+ let mut map = serializer.serialize_map(Some(self.len()))?;
+ for (k, v) in self {
+ map.serialize_key(k)?;
+ map.serialize_value(v)?;
+ }
+ map.end()
+ }
+ }
+
+ impl<'de> de::Deserialize<'de> for Dictionary {
+ #[inline]
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ 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<E>(self) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ Ok(Dictionary::new())
+ }
+
+ #[inline]
+ fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mut values = Dictionary::new();
+
+ while let Some((key, value)) = visitor.next_entry()? {
+ values.insert(key, value);
+ }
+
+ Ok(values)
+ }
+ }
+
+ deserializer.deserialize_map(Visitor)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Dictionary;
+ use std::array::IntoIter;
+
+ #[test]
+ fn from_hash_map_to_dict() {
+ let dict: Dictionary = IntoIter::new([
+ ("Doge", "Shiba Inu"),
+ ("Cheems", "Shiba Inu"),
+ ("Walter", "Bull Terrier"),
+ ("Perro", "Golden Retriever"),
+ ])
+ .collect();
+ assert_eq!(
+ dict.get("Doge").and_then(|v| v.as_string()),
+ Some("Shiba Inu")
+ );
+ assert_eq!(
+ dict.get("Cheems").and_then(|v| v.as_string()),
+ Some("Shiba Inu")
+ );
+ assert_eq!(
+ dict.get("Walter").and_then(|v| v.as_string()),
+ Some("Bull Terrier")
+ );
+ assert_eq!(
+ dict.get("Perro").and_then(|v| v.as_string()),
+ Some("Golden Retriever")
+ );
+ }
+}
diff --git a/third_party/rust/plist/src/error.rs b/third_party/rust/plist/src/error.rs
new file mode 100644
index 0000000000..2f60a3e2a4
--- /dev/null
+++ b/third_party/rust/plist/src/error.rs
@@ -0,0 +1,220 @@
+use 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<ErrorImpl>,
+}
+
+#[derive(Debug)]
+pub(crate) struct ErrorImpl {
+ kind: ErrorKind,
+ file_position: Option<FilePosition>,
+}
+
+#[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<io::Error, Self> {
+ 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 {
+ if let Some(position) = &self.inner.file_position {
+ write!(f, "{:?} ({})", &self.inner.kind, position)
+ } else {
+ fmt::Debug::fmt(&self.inner.kind, f)
+ }
+ }
+}
+
+impl fmt::Display for FilePosition {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ FilePosition::LineColumn(line, column) => {
+ write!(f, "line {}, column {}", line, column)
+ }
+ FilePosition::Offset(offset) => {
+ write!(f, "offset {}", offset)
+ }
+ }
+ }
+}
+
+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,
+ }
+ }
+}
+
+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()
+}
diff --git a/third_party/rust/plist/src/integer.rs b/third_party/rust/plist/src/integer.rs
new file mode 100644
index 0000000000..4e69ec1b2b
--- /dev/null
+++ b/third_party/rust/plist/src/integer.rs
@@ -0,0 +1,202 @@
+use 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<i64> {
+ 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<u64> {
+ 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<Self, ParseIntError> {
+ 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::<i64>() {
+ Ok(v) => v.into(),
+ Err(_) => s.parse::<u64>()?.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<i64> for Integer {
+ fn from(value: i64) -> Integer {
+ Integer {
+ value: value.into(),
+ }
+ }
+}
+
+impl From<i32> for Integer {
+ fn from(value: i32) -> Integer {
+ Integer {
+ value: value.into(),
+ }
+ }
+}
+
+impl From<i16> for Integer {
+ fn from(value: i16) -> Integer {
+ Integer {
+ value: value.into(),
+ }
+ }
+}
+
+impl From<i8> for Integer {
+ fn from(value: i8) -> Integer {
+ Integer {
+ value: value.into(),
+ }
+ }
+}
+
+impl From<u64> for Integer {
+ fn from(value: u64) -> Integer {
+ Integer {
+ value: value.into(),
+ }
+ }
+}
+
+impl From<u32> for Integer {
+ fn from(value: u32) -> Integer {
+ Integer {
+ value: value.into(),
+ }
+ }
+}
+
+impl From<u16> for Integer {
+ fn from(value: u16) -> Integer {
+ Integer {
+ value: value.into(),
+ }
+ }
+}
+
+impl From<u8> 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ 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<E>(self, v: i64) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Integer::from(v))
+ }
+
+ fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Integer::from(v))
+ }
+ }
+
+ impl<'de> Deserialize<'de> for Integer {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ 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());
+ }
+}
diff --git a/third_party/rust/plist/src/lib.rs b/third_party/rust/plist/src/lib.rs
new file mode 100644
index 0000000000..0ac048e30d
--- /dev/null
+++ b/third_party/rust/plist/src/lib.rs
@@ -0,0 +1,133 @@
+//! # 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 stream::XmlWriteOptions;
+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, to_writer_xml_with_options,
+ },
+};
+
+#[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<usize> {
+ let len = len_u64 as usize;
+ if len as u64 != len_u64 {
+ return None; // Too long
+ }
+ Some(len)
+}
diff --git a/third_party/rust/plist/src/ser.rs b/third_party/rust/plist/src/ser.rs
new file mode 100644
index 0000000000..039bfcfcc9
--- /dev/null
+++ b/third_party/rust/plist/src/ser.rs
@@ -0,0 +1,804 @@
+use 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, XmlWriteOptions,
+};
+
+#[doc(hidden)]
+impl ser::Error for Error {
+ fn custom<T: Display>(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<W: Writer> {
+ writer: W,
+ option_mode: OptionMode,
+}
+
+impl<W: Writer> Serializer<W> {
+ pub fn new(writer: W) -> Serializer<W> {
+ Serializer {
+ writer,
+ option_mode: OptionMode::Root,
+ }
+ }
+
+ pub fn into_inner(self) -> W {
+ self.writer
+ }
+
+ fn serialize_with_option_mode<T: ?Sized + ser::Serialize>(
+ &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<u64>) -> Result<(), Error> {
+ self.maybe_write_pending_struct_field_name()?;
+ self.writer.write_start_array(len)
+ }
+
+ fn write_start_dictionary(&mut self, len: Option<u64>) -> 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<W> {
+ type Ok = ();
+ type Error = Error;
+
+ type SerializeSeq = Compound<'a, W>;
+ type SerializeTuple = Compound<'a, W>;
+ type SerializeTupleStruct = Compound<'a, W>;
+ type SerializeTupleVariant = Compound<'a, W>;
+ type SerializeMap = Compound<'a, W>;
+ type SerializeStruct = Compound<'a, W>;
+ type SerializeStructVariant = Compound<'a, W>;
+
+ fn serialize_bool(self, v: bool) -> Result<(), Self::Error> {
+ self.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<T: ?Sized + ser::Serialize>(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> {
+ // `plist` since v1.1 serialises unit enum variants as plain strings.
+ self.write_string(variant)
+ }
+
+ fn serialize_newtype_struct<T: ?Sized + ser::Serialize>(
+ 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<T: ?Sized + ser::Serialize>(
+ 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<usize>) -> Result<Self::SerializeSeq, Error> {
+ 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::SerializeTuple, Error> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleStruct, Error> {
+ self.serialize_tuple(len)
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleVariant, Error> {
+ self.write_start_dictionary(Some(1))?;
+ self.write_string(variant)?;
+ self.serialize_tuple(len)
+ }
+
+ fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Error> {
+ 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<Self::SerializeStruct, Error> {
+ // 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::SerializeStructVariant, Error> {
+ 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<W>,
+}
+
+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<T: ?Sized + ser::Serialize>(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<T: ?Sized + ser::Serialize>(
+ self,
+ _: &'static str,
+ _: &T,
+ ) -> Result<(), Error> {
+ Err(self.expecting_date_error())
+ }
+
+ fn serialize_newtype_variant<T: ?Sized + ser::Serialize>(
+ self,
+ _: &'static str,
+ _: u32,
+ _: &'static str,
+ _: &T,
+ ) -> Result<(), Error> {
+ Err(self.expecting_date_error())
+ }
+
+ fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Error> {
+ Err(self.expecting_date_error())
+ }
+
+ fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Error> {
+ Err(self.expecting_date_error())
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _: &'static str,
+ _: usize,
+ ) -> Result<Self::SerializeTupleStruct, Error> {
+ Err(self.expecting_date_error())
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _: &'static str,
+ _: u32,
+ _: &'static str,
+ _: usize,
+ ) -> Result<Self::SerializeTupleVariant, Error> {
+ Err(self.expecting_date_error())
+ }
+
+ fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Error> {
+ Err(self.expecting_date_error())
+ }
+
+ fn serialize_struct(self, _: &'static str, _: usize) -> Result<Self::SerializeStruct, Error> {
+ Err(self.expecting_date_error())
+ }
+
+ fn serialize_struct_variant(
+ self,
+ _: &'static str,
+ _: u32,
+ _: &'static str,
+ _: usize,
+ ) -> Result<Self::SerializeStructVariant, Error> {
+ Err(self.expecting_date_error())
+ }
+}
+
+struct UidSerializer<'a, W: 'a + Writer> {
+ ser: &'a mut Serializer<W>,
+}
+
+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<T: ?Sized + ser::Serialize>(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<T: ?Sized + ser::Serialize>(
+ self,
+ _: &'static str,
+ _: &T,
+ ) -> Result<(), Error> {
+ Err(self.expecting_uid_error())
+ }
+
+ fn serialize_newtype_variant<T: ?Sized + ser::Serialize>(
+ self,
+ _: &'static str,
+ _: u32,
+ _: &'static str,
+ _: &T,
+ ) -> Result<(), Error> {
+ Err(self.expecting_uid_error())
+ }
+
+ fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Error> {
+ Err(self.expecting_uid_error())
+ }
+
+ fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Error> {
+ Err(self.expecting_uid_error())
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _: &'static str,
+ _: usize,
+ ) -> Result<Self::SerializeTupleStruct, Error> {
+ Err(self.expecting_uid_error())
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _: &'static str,
+ _: u32,
+ _: &'static str,
+ _: usize,
+ ) -> Result<Self::SerializeTupleVariant, Error> {
+ Err(self.expecting_uid_error())
+ }
+
+ fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Error> {
+ Err(self.expecting_uid_error())
+ }
+
+ fn serialize_struct(self, _: &'static str, _: usize) -> Result<Self::SerializeStruct, Error> {
+ Err(self.expecting_uid_error())
+ }
+
+ fn serialize_struct_variant(
+ self,
+ _: &'static str,
+ _: u32,
+ _: &'static str,
+ _: usize,
+ ) -> Result<Self::SerializeStructVariant, Error> {
+ Err(self.expecting_uid_error())
+ }
+}
+
+#[doc(hidden)]
+pub struct Compound<'a, W: 'a + Writer> {
+ ser: &'a mut Serializer<W>,
+}
+
+impl<'a, W: Writer> ser::SerializeSeq for Compound<'a, W> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_element<T: ?Sized + ser::Serialize>(&mut self, value: &T) -> Result<(), Error> {
+ self.ser
+ .serialize_with_option_mode(OptionMode::Explicit, value)
+ }
+
+ fn end(self) -> Result<Self::Ok, Self::Error> {
+ self.ser.write_end_collection()
+ }
+}
+
+impl<'a, W: Writer> ser::SerializeTuple for Compound<'a, W> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_element<T: ?Sized + ser::Serialize>(&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<T: ?Sized + ser::Serialize>(&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<T: ?Sized + ser::Serialize>(&mut self, value: &T) -> Result<(), Error> {
+ self.ser
+ .serialize_with_option_mode(OptionMode::Explicit, value)
+ }
+
+ fn end(self) -> Result<Self::Ok, Self::Error> {
+ 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<T: ?Sized + ser::Serialize>(&mut self, key: &T) -> Result<(), Error> {
+ self.ser
+ .serialize_with_option_mode(OptionMode::Explicit, key)
+ }
+
+ fn serialize_value<T: ?Sized + ser::Serialize>(&mut self, value: &T) -> Result<(), Error> {
+ self.ser
+ .serialize_with_option_mode(OptionMode::Explicit, value)
+ }
+
+ fn end(self) -> Result<Self::Ok, Self::Error> {
+ self.ser.write_end_collection()
+ }
+}
+
+impl<'a, W: Writer> ser::SerializeStruct for Compound<'a, W> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized + ser::Serialize>(
+ &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<T: ?Sized + ser::Serialize>(
+ &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<P: AsRef<Path>, 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<P: AsRef<Path>, 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<W: Write, T: ser::Serialize>(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<W: Write, T: ser::Serialize>(writer: W, value: &T) -> Result<(), Error> {
+ to_writer_xml_with_options(writer, value, &XmlWriteOptions::default())
+}
+
+/// Serializes to a byte stream as an XML encoded plist, using custom [`XmlWriteOptions`].
+pub fn to_writer_xml_with_options<W: Write, T: ser::Serialize>(
+ writer: W,
+ value: &T,
+ options: &XmlWriteOptions,
+) -> Result<(), Error> {
+ let writer = stream::XmlWriter::new_with_options(writer, options);
+ let mut ser = Serializer::new(writer);
+ value.serialize(&mut ser)
+}
diff --git a/third_party/rust/plist/src/serde_tests.rs b/third_party/rust/plist/src/serde_tests.rs
new file mode 100644
index 0000000000..3f0717f34c
--- /dev/null
+++ b/third_party/rust/plist/src/serde_tests.rs
@@ -0,0 +1,887 @@
+use serde::{
+ de::{Deserialize, DeserializeOwned},
+ ser::Serialize,
+};
+use std::{collections::BTreeMap, fmt::Debug, fs::File, io::Cursor, path::Path};
+
+use crate::{
+ stream::{private::Sealed, Event, OwnedEvent, Writer},
+ Date, Deserializer, Dictionary, Error, Integer, Serializer, Uid, Value,
+};
+
+struct VecWriter {
+ events: Vec<OwnedEvent>,
+}
+
+impl VecWriter {
+ pub fn new() -> VecWriter {
+ VecWriter { events: Vec::new() }
+ }
+
+ pub fn into_inner(self) -> Vec<OwnedEvent> {
+ self.events
+ }
+}
+
+impl Writer for VecWriter {
+ fn write_start_array(&mut self, len: Option<u64>) -> Result<(), Error> {
+ self.events.push(Event::StartArray(len));
+ Ok(())
+ }
+
+ fn write_start_dictionary(&mut self, len: Option<u64>) -> 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().into()));
+ 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().into()));
+ 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<VecWriter> {
+ Serializer::new(VecWriter::new())
+}
+
+fn new_deserializer(events: Vec<OwnedEvent>) -> Deserializer<Vec<Result<OwnedEvent, Error>>> {
+ let result_events = events.into_iter().map(Ok).collect();
+ Deserializer::new(result_events)
+}
+
+fn assert_roundtrip<T>(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<String, bool>, Option<Vec<f64>>),
+ Cat {
+ age: Integer,
+ name: String,
+ firmware: Option<Vec<u8>>,
+ },
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+struct DogOuter {
+ inner: Vec<DogInner>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+struct DogInner {
+ a: (),
+ b: usize,
+ c: Vec<String>,
+ d: Option<Uid>,
+}
+
+#[test]
+fn cow() {
+ let cow = Animal::Cow;
+
+ let comparison = &[Event::String("Cow".into())];
+
+ 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".into()),
+ Event::StartDictionary(None),
+ Event::String("inner".into()),
+ Event::StartArray(Some(1)),
+ Event::StartDictionary(None),
+ Event::String("a".into()),
+ Event::String("".into()),
+ Event::String("b".into()),
+ Event::Integer(12.into()),
+ Event::String("c".into()),
+ Event::StartArray(Some(2)),
+ Event::String("a".into()),
+ Event::String("b".into()),
+ Event::EndCollection,
+ Event::String("d".into()),
+ 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".into()),
+ Event::StartArray(Some(2)),
+ Event::StartDictionary(Some(1)),
+ Event::String("Ok".into()),
+ Event::String("hello".into()),
+ Event::EndCollection,
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ 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".into()),
+ Event::StartDictionary(None),
+ Event::String("age".into()),
+ Event::Integer(12.into()),
+ Event::String("name".into()),
+ Event::String("Paws".into()),
+ Event::String("firmware".into()),
+ 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".into()),
+ Event::StartDictionary(None),
+ Event::String("age".into()),
+ Event::Integer(Integer::from(-12)),
+ Event::String("name".into()),
+ Event::String("Paws".into()),
+ 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<String>,
+ b: Option<Option<u32>>,
+ c: Option<Box<TypeWithOptions>>,
+}
+
+#[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".into()),
+ Event::String("hello".into()),
+ Event::String("b".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("None".into()),
+ Event::String("".into()),
+ Event::EndCollection,
+ Event::String("c".into()),
+ Event::StartDictionary(None),
+ Event::String("b".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ 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<i32>,
+ b: Option<Date>,
+}
+
+#[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".into()),
+ Event::Integer(28.into()),
+ Event::String("b".into()),
+ 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<u32> = 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".into()),
+ Event::Integer(12.into()),
+ Event::EndCollection,
+ ];
+
+ assert_roundtrip(obj, Some(comparison));
+}
+
+#[test]
+fn option_some_none() {
+ let obj: Option<Option<u32>> = Some(None);
+
+ let comparison = &[
+ Event::StartDictionary(Some(1)),
+ Event::String("None".into()),
+ Event::String("".into()),
+ 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".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("None".into()),
+ Event::String("".into()),
+ Event::EndCollection,
+ Event::String("b".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("None".into()),
+ Event::String("".into()),
+ Event::EndCollection,
+ Event::EndCollection,
+ Event::String("c".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ 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".into()),
+ Event::String("".into()),
+ Event::EndCollection,
+ Event::Integer(1.into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("None".into()),
+ Event::String("".into()),
+ Event::EndCollection,
+ Event::EndCollection,
+ Event::Integer(2.into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ 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".into()),
+ Event::String("".into()),
+ Event::EndCollection,
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("None".into()),
+ Event::String("".into()),
+ Event::EndCollection,
+ Event::EndCollection,
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ Event::StartDictionary(Some(1)),
+ Event::String("Some".into()),
+ Event::Integer(144.into()),
+ Event::EndCollection,
+ Event::EndCollection,
+ Event::EndCollection,
+ ];
+
+ assert_roundtrip(obj, Some(comparison));
+}
+
+#[test]
+fn enum_variant_types() {
+ #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
+ enum Foo {
+ Unit,
+ Newtype(u32),
+ Tuple(u32, String),
+ Struct { v: u32, s: String },
+ }
+
+ let expected = &[Event::String("Unit".into())];
+ assert_roundtrip(Foo::Unit, Some(expected));
+
+ let expected = &[
+ Event::StartDictionary(Some(1)),
+ Event::String("Newtype".into()),
+ Event::Integer(42.into()),
+ Event::EndCollection,
+ ];
+ assert_roundtrip(Foo::Newtype(42), Some(expected));
+
+ let expected = &[
+ Event::StartDictionary(Some(1)),
+ Event::String("Tuple".into()),
+ Event::StartArray(Some(2)),
+ Event::Integer(42.into()),
+ Event::String("bar".into()),
+ Event::EndCollection,
+ Event::EndCollection,
+ ];
+ assert_roundtrip(Foo::Tuple(42, "bar".into()), Some(expected));
+
+ let expected = &[
+ Event::StartDictionary(Some(1)),
+ Event::String("Struct".into()),
+ Event::StartDictionary(None),
+ Event::String("v".into()),
+ Event::Integer(42.into()),
+ Event::String("s".into()),
+ Event::String("bar".into()),
+ Event::EndCollection,
+ Event::EndCollection,
+ ];
+ assert_roundtrip(
+ Foo::Struct {
+ v: 42,
+ s: "bar".into(),
+ },
+ Some(expected),
+ );
+}
+
+#[test]
+fn deserialise_old_enum_unit_variant_encoding() {
+ #[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
+ enum Foo {
+ Bar,
+ Baz,
+ }
+
+ // `plist` before v1.1 serialised unit enum variants as if they were newtype variants
+ // containing an empty string.
+ let events = &[
+ Event::StartDictionary(Some(1)),
+ Event::String("Baz".into()),
+ Event::String("".into()),
+ Event::EndCollection,
+ ];
+
+ let mut de = new_deserializer(events.to_vec());
+ let obj = Foo::deserialize(&mut de).unwrap();
+
+ assert_eq!(obj, Foo::Baz);
+}
+
+#[test]
+fn deserialize_dictionary_xml() {
+ let reader = File::open(&Path::new("./tests/data/xml.plist")).unwrap();
+ let dict: Dictionary = crate::from_reader(reader).unwrap();
+
+ check_common_plist(&dict);
+
+ // xml.plist has this member, but binary.plist does not.
+ assert_eq!(
+ dict.get("HexademicalNumber") // sic
+ .unwrap()
+ .as_unsigned_integer()
+ .unwrap(),
+ 0xDEADBEEF
+ );
+}
+
+#[test]
+fn deserialize_dictionary_binary() {
+ let reader = File::open(&Path::new("./tests/data/binary.plist")).unwrap();
+ let dict: Dictionary = crate::from_reader(reader).unwrap();
+
+ check_common_plist(&dict);
+}
+
+// Shared checks used by the tests deserialize_dictionary_xml() and
+// deserialize_dictionary_binary(), which load files with different formats
+// but the same data elements.
+fn check_common_plist(dict: &Dictionary) {
+ // Array elements
+
+ let lines = dict.get("Lines").unwrap().as_array().unwrap();
+ assert_eq!(lines.len(), 2);
+ assert_eq!(
+ lines[0].as_string().unwrap(),
+ "It is a tale told by an idiot,"
+ );
+ assert_eq!(
+ lines[1].as_string().unwrap(),
+ "Full of sound and fury, signifying nothing."
+ );
+
+ // Dictionary
+ //
+ // There is no embedded dictionary in this plist. See
+ // deserialize_dictionary_binary_nskeyedarchiver() below for an example
+ // of that.
+
+ // Boolean elements
+
+ assert_eq!(dict.get("IsTrue").unwrap().as_boolean().unwrap(), true);
+
+ assert_eq!(dict.get("IsNotFalse").unwrap().as_boolean().unwrap(), false);
+
+ // Data
+
+ let data = dict.get("Data").unwrap().as_data().unwrap();
+ assert_eq!(data.len(), 15);
+ assert_eq!(
+ data,
+ &[0, 0, 0, 0xbe, 0, 0, 0, 0x03, 0, 0, 0, 0x1e, 0, 0, 0]
+ );
+
+ // Date
+
+ assert_eq!(
+ dict.get("Birthdate").unwrap().as_date().unwrap(),
+ Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap()
+ );
+
+ // Real
+
+ assert_eq!(dict.get("Height").unwrap().as_real().unwrap(), 1.6);
+
+ // Integer
+
+ assert_eq!(
+ dict.get("BiggestNumber")
+ .unwrap()
+ .as_unsigned_integer()
+ .unwrap(),
+ 18446744073709551615
+ );
+
+ assert_eq!(
+ dict.get("Death").unwrap().as_unsigned_integer().unwrap(),
+ 1564
+ );
+
+ assert_eq!(
+ dict.get("SmallestNumber")
+ .unwrap()
+ .as_signed_integer()
+ .unwrap(),
+ -9223372036854775808
+ );
+
+ // String
+
+ assert_eq!(
+ dict.get("Author").unwrap().as_string().unwrap(),
+ "William Shakespeare"
+ );
+
+ assert_eq!(dict.get("Blank").unwrap().as_string().unwrap(), "");
+
+ // Uid
+ //
+ // No checks for Uid value type in this test. See
+ // deserialize_dictionary_binary_nskeyedarchiver() below for an example
+ // of that.
+}
+
+#[test]
+fn deserialize_dictionary_binary_nskeyedarchiver() {
+ let reader = File::open(&Path::new("./tests/data/binary_NSKeyedArchiver.plist")).unwrap();
+ let dict: Dictionary = crate::from_reader(reader).unwrap();
+
+ assert_eq!(
+ dict.get("$archiver").unwrap().as_string().unwrap(),
+ "NSKeyedArchiver"
+ );
+
+ let objects = dict.get("$objects").unwrap().as_array().unwrap();
+ assert_eq!(objects.len(), 5);
+
+ assert_eq!(objects[0].as_string().unwrap(), "$null");
+
+ let objects_1 = objects[1].as_dictionary().unwrap();
+ assert_eq!(
+ *objects_1.get("$class").unwrap().as_uid().unwrap(),
+ Uid::new(4)
+ );
+ assert_eq!(
+ objects_1
+ .get("NSRangeCount")
+ .unwrap()
+ .as_unsigned_integer()
+ .unwrap(),
+ 42
+ );
+ assert_eq!(
+ *objects_1.get("NSRangeData").unwrap().as_uid().unwrap(),
+ Uid::new(2)
+ );
+
+ let objects_2 = objects[2].as_dictionary().unwrap();
+ assert_eq!(
+ *objects_2.get("$class").unwrap().as_uid().unwrap(),
+ Uid::new(3)
+ );
+ let objects_2_nsdata = objects_2.get("NS.data").unwrap().as_data().unwrap();
+ assert_eq!(objects_2_nsdata.len(), 103);
+ assert_eq!(objects_2_nsdata[0], 0x03);
+ assert_eq!(objects_2_nsdata[102], 0x01);
+
+ let objects_3 = objects[3].as_dictionary().unwrap();
+ let objects_3_classes = objects_3.get("$classes").unwrap().as_array().unwrap();
+ assert_eq!(objects_3_classes.len(), 3);
+ assert_eq!(objects_3_classes[0].as_string().unwrap(), "NSMutableData");
+ assert_eq!(objects_3_classes[1].as_string().unwrap(), "NSData");
+ assert_eq!(objects_3_classes[2].as_string().unwrap(), "NSObject");
+ assert_eq!(
+ objects_3.get("$classname").unwrap().as_string().unwrap(),
+ "NSMutableData"
+ );
+
+ let objects_4 = objects[4].as_dictionary().unwrap();
+ let objects_4_classes = objects_4.get("$classes").unwrap().as_array().unwrap();
+ assert_eq!(objects_4_classes.len(), 3);
+ assert_eq!(
+ objects_4_classes[0].as_string().unwrap(),
+ "NSMutableIndexSet"
+ );
+ assert_eq!(objects_4_classes[1].as_string().unwrap(), "NSIndexSet");
+ assert_eq!(objects_4_classes[2].as_string().unwrap(), "NSObject");
+ assert_eq!(
+ objects_4.get("$classname").unwrap().as_string().unwrap(),
+ "NSMutableIndexSet"
+ );
+
+ let top = dict.get("$top").unwrap().as_dictionary().unwrap();
+ assert_eq!(
+ *top.get("foundItems").unwrap().as_uid().unwrap(),
+ Uid::new(1)
+ );
+
+ let version = dict.get("$version").unwrap().as_unsigned_integer().unwrap();
+ assert_eq!(version, 100000);
+}
+
+#[test]
+fn dictionary_deserialize_dictionary_in_struct() {
+ // Example from <https://github.com/ebarnard/rust-plist/issues/54>
+ #[derive(Deserialize)]
+ struct LayerinfoData {
+ color: Option<String>,
+ lib: Option<Dictionary>,
+ }
+
+ let lib_dict: LayerinfoData = crate::from_bytes(r#"
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+ <plist version="1.0">
+ <dict>
+ <key>color</key>
+ <string>1,0.75,0,0.7</string>
+ <key>lib</key>
+ <dict>
+ <key>com.typemytype.robofont.segmentType</key>
+ <string>curve</string>
+ </dict>
+ </dict>
+ </plist>
+ "#.as_bytes()).unwrap();
+
+ assert_eq!(lib_dict.color.unwrap(), "1,0.75,0,0.7");
+ assert_eq!(
+ lib_dict
+ .lib
+ .unwrap()
+ .get("com.typemytype.robofont.segmentType")
+ .unwrap()
+ .as_string()
+ .unwrap(),
+ "curve"
+ );
+}
+
+#[test]
+fn dictionary_serialize_xml() {
+ // Dictionary to be embedded in dict, below.
+ let mut inner_dict = Dictionary::new();
+ inner_dict.insert(
+ "FirstKey".to_owned(),
+ Value::String("FirstValue".to_owned()),
+ );
+ inner_dict.insert("SecondKey".to_owned(), Value::Data(vec![10, 20, 30, 40]));
+ inner_dict.insert("ThirdKey".to_owned(), Value::Real(1.234));
+ inner_dict.insert(
+ "FourthKey".to_owned(),
+ Value::Date(Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap()),
+ );
+
+ // Top-level dictionary.
+ let mut dict = Dictionary::new();
+ dict.insert(
+ "AnArray".to_owned(),
+ Value::Array(vec![
+ Value::String("Hello, world!".to_owned()),
+ Value::Integer(Integer::from(345)),
+ ]),
+ );
+ dict.insert("ADictionary".to_owned(), Value::Dictionary(inner_dict));
+ dict.insert("AnInteger".to_owned(), Value::Integer(Integer::from(123)));
+ dict.insert("ATrueBoolean".to_owned(), Value::Boolean(true));
+ dict.insert("AFalseBoolean".to_owned(), Value::Boolean(false));
+
+ // Serialize dictionary as an XML plist.
+ let mut buf = Cursor::new(Vec::new());
+ crate::to_writer_xml(&mut buf, &dict).unwrap();
+ let buf = buf.into_inner();
+ let xml = std::str::from_utf8(&buf).unwrap();
+
+ let comparison = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+\t<key>AnArray</key>
+\t<array>
+\t\t<string>Hello, world!</string>
+\t\t<integer>345</integer>
+\t</array>
+\t<key>ADictionary</key>
+\t<dict>
+\t\t<key>FirstKey</key>
+\t\t<string>FirstValue</string>
+\t\t<key>SecondKey</key>
+\t\t<data>\n\t\tChQeKA==\n\t\t</data>
+\t\t<key>ThirdKey</key>
+\t\t<real>1.234</real>
+\t\t<key>FourthKey</key>
+\t\t<date>1981-05-16T11:32:06Z</date>
+\t</dict>
+\t<key>AnInteger</key>
+\t<integer>123</integer>
+\t<key>ATrueBoolean</key>
+\t<true/>
+\t<key>AFalseBoolean</key>
+\t<false/>
+</dict>
+</plist>";
+
+ assert_eq!(xml, comparison);
+}
+
+#[test]
+fn serde_yaml_to_value() {
+ let value: Value = serde_yaml::from_str("true").unwrap();
+ assert_eq!(value, Value::Boolean(true));
+}
diff --git a/third_party/rust/plist/src/stream/binary_reader.rs b/third_party/rust/plist/src/stream/binary_reader.rs
new file mode 100644
index 0000000000..a5d2d47ede
--- /dev/null
+++ b/third_party/rust/plist/src/stream/binary_reader.rs
@@ -0,0 +1,492 @@
+use std::{
+ io::{self, Read, Seek, SeekFrom},
+ mem::size_of,
+};
+
+use crate::{
+ date::{Date, InfiniteOrNanDate},
+ error::{Error, ErrorKind},
+ stream::{Event, OwnedEvent},
+ u64_to_usize, Uid,
+};
+
+struct StackItem {
+ object_ref: u64,
+ child_object_refs: Vec<u64>,
+ 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<R> {
+ stack: Vec<StackItem>,
+ object_offsets: Vec<u64>,
+ object_on_stack: Vec<bool>,
+ reader: PosReader<R>,
+ ref_size: u8,
+ root_object: u64,
+ trailer_start_offset: u64,
+}
+
+struct PosReader<R> {
+ reader: R,
+ pos: u64,
+}
+
+impl<R: Read + Seek> PosReader<R> {
+ 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<u64, Error> {
+ self.pos = self
+ .reader
+ .seek(pos)
+ .map_err(|err| ErrorKind::Io(err).with_byte_offset(self.pos))?;
+ Ok(self.pos)
+ }
+}
+
+impl<R: Read> Read for PosReader<R> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ 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<R: Read + Seek> BinaryReader<R> {
+ pub fn new(reader: R) -> BinaryReader<R> {
+ 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<T>(&self, len: u64, size: usize) -> Result<Vec<T>, 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<Vec<u64>, 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<Vec<u64>, 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<u64, Error> {
+ 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<Vec<u8>, Error> {
+ let mut data = self.allocate_vec(len, size_of::<u8>())?;
+ data.resize(len as usize, 0);
+ self.reader.read_all(&mut data)?;
+ Ok(data)
+ }
+
+ fn seek_to_object(&mut self, object_ref: u64) -> Result<u64, Error> {
+ 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<Option<OwnedEvent>, 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)?.into()))
+ }
+ (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.into()))
+ }
+ (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::<u16>())?;
+
+ 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.into()))
+ }
+ (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<u8, Error> {
+ let mut buf = [0; 1];
+ self.reader.read_all(&mut buf)?;
+ Ok(buf[0])
+ }
+
+ fn read_be_u16(&mut self) -> Result<u16, Error> {
+ let mut buf = [0; 2];
+ self.reader.read_all(&mut buf)?;
+ Ok(u16::from_be_bytes(buf))
+ }
+
+ fn read_be_u32(&mut self) -> Result<u32, Error> {
+ let mut buf = [0; 4];
+ self.reader.read_all(&mut buf)?;
+ Ok(u32::from_be_bytes(buf))
+ }
+
+ fn read_be_u64(&mut self) -> Result<u64, Error> {
+ let mut buf = [0; 8];
+ self.reader.read_all(&mut buf)?;
+ Ok(u64::from_be_bytes(buf))
+ }
+
+ fn read_be_i64(&mut self) -> Result<i64, Error> {
+ let mut buf = [0; 8];
+ self.reader.read_all(&mut buf)?;
+ Ok(i64::from_be_bytes(buf))
+ }
+
+ fn read_be_i128(&mut self) -> Result<i128, Error> {
+ 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<R: Read + Seek> Iterator for BinaryReader<R> {
+ type Item = Result<OwnedEvent, Error>;
+
+ fn next(&mut self) -> Option<Result<OwnedEvent, Error>> {
+ 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<Event> = 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].into()),
+ 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<Event> = streaming_parser.map(|e| e.unwrap()).collect();
+
+ assert_eq!(events[2], Event::String("\u{2605} or better".into()));
+
+ 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.to_mut().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<Event> = 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)));
+ }
+}
diff --git a/third_party/rust/plist/src/stream/binary_writer.rs b/third_party/rust/plist/src/stream/binary_writer.rs
new file mode 100644
index 0000000000..0e04dbe865
--- /dev/null
+++ b/third_party/rust/plist/src/stream/binary_writer.rs
@@ -0,0 +1,736 @@
+// 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<W: Write> {
+ writer: PosWriter<W>,
+ events: Vec<Event>,
+ dictionary_key_events: Vec<usize>,
+ values: IndexMap<Value<'static>, ValueState>,
+ /// Pointers into `events` for each of the currently unclosed `Collection` events.
+ collection_stack: Vec<usize>,
+ /// The number of `Collection` and unique `Value` events in `events`.
+ num_objects: usize,
+}
+
+struct PosWriter<W: Write> {
+ 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<ObjectRef>,
+}
+
+#[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<W: Write> BinaryWriter<W> {
+ pub fn new(writer: W) -> BinaryWriter<W> {
+ 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<usize>,
+ ) -> 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<usize>,
+ ) -> 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<W: Write> Writer for BinaryWriter<W> {
+ fn write_start_array(&mut self, _len: Option<u64>) -> Result<(), Error> {
+ self.write_start_collection(CollectionType::Array)
+ }
+ fn write_start_dictionary(&mut self, _len: Option<u64>) -> 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<Value<'static>, 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<impl Write>,
+ 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<impl Write>,
+ 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<W: Write> PosWriter<W> {
+ fn write_exact(&mut self, buf: &[u8]) -> Result<(), Error> {
+ self.write_all(buf)
+ .map_err(error::from_io_without_position)?;
+ Ok(())
+ }
+}
+
+impl<W: Write> Write for PosWriter<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ 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<Result<_, _>> = 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"))
+ }
+}
diff --git a/third_party/rust/plist/src/stream/mod.rs b/third_party/rust/plist/src/stream/mod.rs
new file mode 100644
index 0000000000..0fd9ec5f3e
--- /dev/null
+++ b/third_party/rust/plist/src/stream/mod.rs
@@ -0,0 +1,266 @@
+//! 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::{
+ borrow::Cow,
+ 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
+/// ```
+///
+/// ## Lifetimes
+///
+/// This type has a lifetime parameter; during serialization, data is borrowed
+/// from a [`Value`], and the lifetime of the event is the lifetime of the
+/// [`Value`] being serialized.
+///
+/// During deserialization, data is always copied anyway, and this lifetime
+/// is always `'static`.
+#[derive(Clone, Debug, PartialEq)]
+#[non_exhaustive]
+pub enum Event<'a> {
+ // 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<u64>),
+ StartDictionary(Option<u64>),
+ EndCollection,
+
+ Boolean(bool),
+ Data(Cow<'a, [u8]>),
+ Date(Date),
+ Integer(Integer),
+ Real(f64),
+ String(Cow<'a, str>),
+ Uid(Uid),
+}
+
+/// An owned [`Event`].
+///
+/// During deserialization, events are always owned; this type alias helps
+/// keep that code a bit clearer.
+pub type OwnedEvent = Event<'static>;
+
+/// An `Event` stream returned by `Value::into_events`.
+pub struct Events<'a> {
+ stack: Vec<StackItem<'a>>,
+}
+
+enum StackItem<'a> {
+ Root(&'a Value),
+ Array(std::slice::Iter<'a, Value>),
+ Dict(dictionary::Iter<'a>),
+ DictValue(&'a Value),
+}
+
+/// Options for customizing serialization of XML plists.
+#[derive(Clone, Debug)]
+pub struct XmlWriteOptions {
+ indent_str: Cow<'static, str>,
+}
+
+impl XmlWriteOptions {
+ /// Specify the sequence of characters used for indentation.
+ ///
+ /// This may be either an `&'static str` or an owned `String`.
+ ///
+ /// The default is `\t`.
+ pub fn indent_string(mut self, indent_str: impl Into<Cow<'static, str>>) -> Self {
+ self.indent_str = indent_str.into();
+ self
+ }
+}
+
+impl Default for XmlWriteOptions {
+ fn default() -> Self {
+ XmlWriteOptions {
+ indent_str: Cow::Borrowed("\t"),
+ }
+ }
+}
+
+impl<'a> Events<'a> {
+ pub(crate) fn new(value: &'a Value) -> Events<'a> {
+ Events {
+ stack: vec![StackItem::Root(value)],
+ }
+ }
+}
+
+impl<'a> Iterator for Events<'a> {
+ type Item = Event<'a>;
+
+ fn next(&mut self) -> Option<Event<'a>> {
+ fn handle_value<'c, 'b: 'c>(
+ value: &'b Value,
+ stack: &'c mut Vec<StackItem<'b>>,
+ ) -> Event<'b> {
+ match value {
+ Value::Array(array) => {
+ let len = array.len();
+ let iter = array.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(Cow::Borrowed(&value)),
+ Value::Date(value) => Event::Date(*value),
+ Value::Real(value) => Event::Real(*value),
+ Value::Integer(value) => Event::Integer(*value),
+ Value::String(value) => Event::String(Cow::Borrowed(value.as_str())),
+ Value::Uid(value) => Event::Uid(*value),
+ }
+ }
+
+ 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(Cow::Borrowed(key))
+ } else {
+ Event::EndCollection
+ }
+ }
+ StackItem::DictValue(value) => handle_value(value, &mut self.stack),
+ })
+ }
+}
+
+pub struct Reader<R: Read + Seek>(ReaderInner<R>);
+
+enum ReaderInner<R: Read + Seek> {
+ Uninitialized(Option<R>),
+ Xml(XmlReader<R>),
+ Binary(BinaryReader<R>),
+}
+
+impl<R: Read + Seek> Reader<R> {
+ pub fn new(reader: R) -> Reader<R> {
+ Reader(ReaderInner::Uninitialized(Some(reader)))
+ }
+
+ fn is_binary(reader: &mut R) -> Result<bool, Error> {
+ 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<R: Read + Seek> Iterator for Reader<R> {
+ type Item = Result<OwnedEvent, Error>;
+
+ fn next(&mut self) -> Option<Result<OwnedEvent, Error>> {
+ 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(),
+ };
+
+ match Reader::is_binary(&mut reader) {
+ Ok(true) => self.0 = ReaderInner::Binary(BinaryReader::new(reader)),
+ Ok(false) => self.0 = ReaderInner::Xml(XmlReader::new(reader)),
+ Err(err) => {
+ self.0 = ReaderInner::Uninitialized(Some(reader));
+ return Some(Err(err));
+ }
+ }
+
+ 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),
+ }
+ }
+
+ fn write_start_array(&mut self, len: Option<u64>) -> Result<(), Error>;
+ fn write_start_dictionary(&mut self, len: Option<u64>) -> 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<W: Write> Sealed for super::BinaryWriter<W> {}
+ impl<W: Write> Sealed for super::XmlWriter<W> {}
+}
diff --git a/third_party/rust/plist/src/stream/xml_reader.rs b/third_party/rust/plist/src/stream/xml_reader.rs
new file mode 100644
index 0000000000..31c0d39701
--- /dev/null
+++ b/third_party/rust/plist/src/stream/xml_reader.rs
@@ -0,0 +1,275 @@
+use 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, OwnedEvent},
+ Date, Integer,
+};
+
+pub struct XmlReader<R: Read> {
+ xml_reader: EventReader<R>,
+ queued_event: Option<XmlEvent>,
+ element_stack: Vec<String>,
+ finished: bool,
+}
+
+impl<R: Read> XmlReader<R> {
+ pub fn new(reader: R) -> XmlReader<R> {
+ 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<String, Error> {
+ 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<XmlEvent, XmlReaderError> {
+ if let Some(event) = self.queued_event.take() {
+ Ok(event)
+ } else {
+ self.xml_reader.next()
+ }
+ }
+
+ fn read_next(&mut self) -> Result<Option<OwnedEvent>, 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()?.into()))),
+ "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.into())));
+ }
+ "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()?.into()))),
+ _ => 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<R: Read> Iterator for XmlReader<R> {
+ type Item = Result<OwnedEvent, Error>;
+
+ fn next(&mut self) -> Option<Result<OwnedEvent, Error>> {
+ 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<Event> = streaming_parser.map(|e| e.unwrap()).collect();
+
+ let comparison = &[
+ StartDictionary(None),
+ String("Author".into()),
+ String("William Shakespeare".into()),
+ String("Lines".into()),
+ StartArray(None),
+ 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("Height".into()),
+ Real(1.60),
+ String("Data".into()),
+ Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0].into()),
+ String("Birthdate".into()),
+ Date(super::Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap()),
+ String("Blank".into()),
+ String("".into()),
+ String("BiggestNumber".into()),
+ Integer(18446744073709551615u64.into()),
+ String("SmallestNumber".into()),
+ Integer((-9223372036854775808i64).into()),
+ String("HexademicalNumber".into()),
+ 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());
+ }
+}
diff --git a/third_party/rust/plist/src/stream/xml_writer.rs b/third_party/rust/plist/src/stream/xml_writer.rs
new file mode 100644
index 0000000000..703435370d
--- /dev/null
+++ b/third_party/rust/plist/src/stream/xml_writer.rs
@@ -0,0 +1,391 @@
+use 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, XmlWriteOptions},
+ Date, Integer, Uid,
+};
+
+static XML_PROLOGUE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+"#;
+
+#[derive(PartialEq)]
+enum Element {
+ Dictionary,
+ Array,
+}
+
+pub struct XmlWriter<W: Write> {
+ xml_writer: EventWriter<W>,
+ stack: Vec<Element>,
+ expecting_key: bool,
+ written_prologue: bool,
+ // Not very nice
+ empty_namespace: Namespace,
+}
+
+impl<W: Write> XmlWriter<W> {
+ pub fn new(writer: W) -> XmlWriter<W> {
+ let opts = XmlWriteOptions::default();
+ XmlWriter::new_with_options(writer, &opts)
+ }
+
+ pub fn new_with_options(writer: W, opts: &XmlWriteOptions) -> XmlWriter<W> {
+ let config = EmitterConfig::new()
+ .line_separator("\n")
+ .indent_string(opts.indent_str.clone())
+ .perform_indent(true)
+ .write_document_declaration(false)
+ .normalize_empty_elements(true)
+ .cdata_to_characters(true)
+ .keep_element_names_stack(false)
+ .autopad_comments(true)
+ .pad_self_closing(false);
+
+ 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<F: FnOnce(&mut Self) -> 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 </plist> element
+ if self.stack.is_empty() {
+ // We didn't tell the xml_writer about the <plist> tag so we'll skip telling it
+ // about the </plist> tag as well.
+ self.xml_writer
+ .inner_mut()
+ .write_all(b"\n</plist>")
+ .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<F: FnOnce(&mut Self) -> 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<W: Write> Writer for XmlWriter<W> {
+ fn write_start_array(&mut self, _len: Option<u64>) -> 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<u64>) -> 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
+ // <data>
+ // AAAA..AA (68 characters per line)
+ // </data>
+ // 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".into()),
+ Event::String("William Shakespeare".into()),
+ Event::String("Lines".into()),
+ Event::StartArray(None),
+ Event::String("It is a tale told by an idiot,".into()),
+ Event::String("Full of sound and fury, signifying nothing.".into()),
+ Event::Data((0..128).collect::<Vec<_>>().into()),
+ Event::EndCollection,
+ Event::String("Death".into()),
+ Event::Integer(1564.into()),
+ Event::String("Height".into()),
+ Event::Real(1.60),
+ Event::String("Data".into()),
+ Event::Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0].into()),
+ Event::String("Birthdate".into()),
+ Event::Date(super::Date::from_rfc3339("1981-05-16T11:32:06Z").unwrap()),
+ Event::String("Comment".into()),
+ Event::String("2 < 3".into()), // make sure characters are escaped
+ Event::String("BiggestNumber".into()),
+ Event::Integer(18446744073709551615u64.into()),
+ Event::String("SmallestNumber".into()),
+ 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+\t<key>Author</key>
+\t<string>William Shakespeare</string>
+\t<key>Lines</key>
+\t<array>
+\t\t<string>It is a tale told by an idiot,</string>
+\t\t<string>Full of sound and fury, signifying nothing.</string>
+\t\t<data>
+\t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEy
+\t\tMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2Rl
+\t\tZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=
+\t\t</data>
+\t</array>
+\t<key>Death</key>
+\t<integer>1564</integer>
+\t<key>Height</key>
+\t<real>1.6</real>
+\t<key>Data</key>
+\t<data>
+\tAAAAvgAAAAMAAAAeAAAA
+\t</data>
+\t<key>Birthdate</key>
+\t<date>1981-05-16T11:32:06Z</date>
+\t<key>Comment</key>
+\t<string>2 &lt; 3</string>
+\t<key>BiggestNumber</key>
+\t<integer>18446744073709551615</integer>
+\t<key>SmallestNumber</key>
+\t<integer>-9223372036854775808</integer>
+\t<key>IsTrue</key>
+\t<true/>
+\t<key>IsNotFalse</key>
+\t<false/>
+</dict>
+</plist>";
+
+ let s = String::from_utf8(cursor.into_inner()).unwrap();
+
+ assert_eq!(s, comparison);
+ }
+}
diff --git a/third_party/rust/plist/src/uid.rs b/third_party/rust/plist/src/uid.rs
new file mode 100644
index 0000000000..f2e107993a
--- /dev/null
+++ b/third_party/rust/plist/src/uid.rs
@@ -0,0 +1,97 @@
+use 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ 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_u64<E>(self, v: u64) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ UidU64Visitor.visit_u64(v)
+ }
+
+ fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ deserializer.deserialize_u64(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<E>(self, v: u64) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ Ok(Uid::new(v))
+ }
+ }
+
+ impl<'de> Deserialize<'de> for Uid {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ deserializer.deserialize_newtype_struct(UID_NEWTYPE_STRUCT_NAME, UidNewtypeVisitor)
+ }
+ }
+}
diff --git a/third_party/rust/plist/src/value.rs b/third_party/rust/plist/src/value.rs
new file mode 100644
index 0000000000..607479e400
--- /dev/null
+++ b/third_party/rust/plist/src/value.rs
@@ -0,0 +1,816 @@
+use std::{
+ fs::File,
+ io::{BufReader, BufWriter, Read, Seek, Write},
+ path::Path,
+};
+
+use crate::{
+ error::{self, Error, ErrorKind, EventKind},
+ stream::{
+ BinaryWriter, Event, Events, OwnedEvent, Reader, Writer, XmlReader, XmlWriteOptions,
+ XmlWriter,
+ },
+ u64_to_usize, Date, Dictionary, Integer, Uid,
+};
+
+/// Represents any plist value.
+#[derive(Clone, Debug, PartialEq)]
+#[non_exhaustive]
+pub enum Value {
+ Array(Vec<Value>),
+ Dictionary(Dictionary),
+ Boolean(bool),
+ Data(Vec<u8>),
+ Date(Date),
+ Real(f64),
+ Integer(Integer),
+ String(String),
+ Uid(Uid),
+}
+
+impl Value {
+ /// Reads a `Value` from a plist file of any encoding.
+ pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Value, Error> {
+ 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<R: Read + Seek>(reader: R) -> Result<Value, Error> {
+ 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<R: Read>(reader: R) -> Result<Value, Error> {
+ 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<P: AsRef<Path>>(&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<P: AsRef<Path>>(&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<W: Write>(&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<W: Write>(&self, writer: W) -> Result<(), Error> {
+ self.to_writer_xml_with_options(writer, &XmlWriteOptions::default())
+ }
+
+ /// Serializes a `Value` to a stream, using custom [`XmlWriteOptions`].
+ ///
+ /// If you need to serialize to a file, you must acquire an appropriate
+ /// `Write` handle yourself.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::io::{BufWriter, Write};
+ /// use std::fs::File;
+ /// use plist::{Dictionary, Value, XmlWriteOptions};
+ ///
+ /// let value: Value = Dictionary::new().into();
+ /// // .. add some keys & values
+ /// let mut file = File::create("com.example.myPlist.plist").unwrap();
+ /// let options = XmlWriteOptions::default().indent_string(" ");
+ /// value.to_writer_xml_with_options(BufWriter::new(&mut file), &options).unwrap();
+ /// file.sync_all().unwrap();
+ /// ```
+ pub fn to_writer_xml_with_options<W: Write>(
+ &self,
+ writer: W,
+ options: &XmlWriteOptions,
+ ) -> Result<(), Error> {
+ let mut writer = XmlWriter::new_with_options(writer, options);
+ self.to_writer_inner(&mut writer)
+ }
+
+ fn to_writer_inner(&self, writer: &mut dyn Writer) -> Result<(), Error> {
+ let events = self.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<T>(events: T) -> Result<Value, Error>
+ where
+ T: IntoIterator<Item = Result<OwnedEvent, Error>>,
+ {
+ 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<T>(events: T) -> Result<Value, Error>
+ where
+ T: IntoIterator<Item = Result<OwnedEvent, Error>>,
+ {
+ 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")]
+ #[doc(hidden)]
+ #[deprecated(since = "1.2.0", note = "use Value::events instead")]
+ pub fn into_events(&self) -> Events {
+ self.events()
+ }
+
+ /// Creates an `Event` iterator for this `Value`.
+ #[cfg(not(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps"))]
+ pub(crate) fn events(&self) -> Events {
+ Events::new(self)
+ }
+
+ /// Creates an `Event` iterator for this `Value`.
+ #[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")]
+ pub fn events(&self) -> Events {
+ Events::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<Vec<Value>> {
+ 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<Value>> {
+ 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<Value>> {
+ 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<Dictionary> {
+ 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<bool> {
+ 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<Vec<u8>> {
+ 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<Date> {
+ 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<f64> {
+ 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<i64> {
+ 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<u64> {
+ 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<String> {
+ 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,
+ }
+ }
+
+ /// If the `Value` is a Uid, returns the underlying `Uid`.
+ ///
+ /// Returns `None` otherwise.
+ ///
+ /// This method consumes the `Value`. If this is not desired, please use
+ /// `as_uid` method.
+ pub fn into_uid(self) -> Option<Uid> {
+ match self {
+ Value::Uid(u) => Some(u),
+ _ => None,
+ }
+ }
+
+ /// If the `Value` is a Uid, returns the associated `Uid`.
+ ///
+ /// Returns `None` otherwise.
+ pub fn as_uid(&self) -> Option<&Uid> {
+ match *self {
+ Value::Uid(ref u) => Some(u),
+ _ => None,
+ }
+ }
+}
+
+#[cfg(feature = "serde")]
+pub mod serde_impls {
+ use serde::{
+ de,
+ de::{EnumAccess, MapAccess, SeqAccess, VariantAccess, Visitor},
+ ser,
+ };
+
+ use crate::{
+ date::serde_impls::DATE_NEWTYPE_STRUCT_NAME, uid::serde_impls::UID_NEWTYPE_STRUCT_NAME,
+ Dictionary, Value,
+ };
+
+ pub const VALUE_NEWTYPE_STRUCT_NAME: &str = "PLIST-VALUE";
+
+ impl ser::Serialize for Value {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ match *self {
+ Value::Array(ref v) => v.serialize(serializer),
+ Value::Dictionary(ref m) => m.serialize(serializer),
+ Value::Boolean(b) => serializer.serialize_bool(b),
+ Value::Data(ref v) => serializer.serialize_bytes(v),
+ Value::Date(d) => d.serialize(serializer),
+ Value::Real(n) => serializer.serialize_f64(n),
+ Value::Integer(n) => n.serialize(serializer),
+ Value::String(ref s) => serializer.serialize_str(s),
+ Value::Uid(ref u) => u.serialize(serializer),
+ }
+ }
+ }
+
+ impl<'de> de::Deserialize<'de> for Value {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct ValueVisitor;
+
+ impl<'de> Visitor<'de> for ValueVisitor {
+ type Value = Value;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ formatter.write_str("any supported plist value")
+ }
+
+ fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
+ Ok(Value::Boolean(value))
+ }
+
+ fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Value, E> {
+ Ok(Value::Data(v))
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Value, E> {
+ Ok(Value::Data(v.to_vec()))
+ }
+
+ fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
+ Ok(Value::Integer(value.into()))
+ }
+
+ fn visit_u64<E>(self, value: u64) -> Result<Value, E> {
+ Ok(Value::Integer(value.into()))
+ }
+
+ fn visit_f64<E>(self, value: f64) -> Result<Value, E> {
+ Ok(Value::Real(value))
+ }
+
+ fn visit_map<V>(self, mut map: V) -> Result<Value, V::Error>
+ where
+ V: MapAccess<'de>,
+ {
+ let mut values = Dictionary::new();
+ while let Some((k, v)) = map.next_entry()? {
+ values.insert(k, v);
+ }
+ Ok(Value::Dictionary(values))
+ }
+
+ fn visit_str<E>(self, value: &str) -> Result<Value, E> {
+ Ok(Value::String(value.to_owned()))
+ }
+
+ fn visit_string<E>(self, value: String) -> Result<Value, E> {
+ Ok(Value::String(value))
+ }
+
+ fn visit_newtype_struct<T>(self, deserializer: T) -> Result<Value, T::Error>
+ where
+ T: de::Deserializer<'de>,
+ {
+ deserializer.deserialize_any(self)
+ }
+
+ fn visit_seq<A>(self, mut seq: A) -> Result<Value, A::Error>
+ where
+ A: SeqAccess<'de>,
+ {
+ let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or(0));
+ while let Some(elem) = seq.next_element()? {
+ vec.push(elem);
+ }
+ Ok(Value::Array(vec))
+ }
+
+ fn visit_enum<A>(self, data: A) -> Result<Value, A::Error>
+ where
+ A: EnumAccess<'de>,
+ {
+ let (name, variant) = data.variant::<String>()?;
+ match &*name {
+ DATE_NEWTYPE_STRUCT_NAME => Ok(Value::Date(variant.newtype_variant()?)),
+ UID_NEWTYPE_STRUCT_NAME => Ok(Value::Uid(variant.newtype_variant()?)),
+ _ => Err(de::Error::unknown_variant(
+ &name,
+ &[DATE_NEWTYPE_STRUCT_NAME, UID_NEWTYPE_STRUCT_NAME],
+ )),
+ }
+ }
+ }
+
+ // Serde serialisers are encouraged to treat newtype structs as insignificant
+ // wrappers around the data they contain. That means not parsing anything other
+ // than the contained value. Therefore, this should not prevent using `Value`
+ // with other `Serializer`s.
+ deserializer.deserialize_newtype_struct(VALUE_NEWTYPE_STRUCT_NAME, ValueVisitor)
+ }
+ }
+}
+
+impl From<Vec<Value>> for Value {
+ fn from(from: Vec<Value>) -> Value {
+ Value::Array(from)
+ }
+}
+
+impl From<Dictionary> for Value {
+ fn from(from: Dictionary) -> Value {
+ Value::Dictionary(from)
+ }
+}
+
+impl From<bool> 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<Date> 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<f64> for Value {
+ fn from(from: f64) -> Value {
+ Value::Real(from)
+ }
+}
+
+impl From<f32> for Value {
+ fn from(from: f32) -> Value {
+ Value::Real(from.into())
+ }
+}
+
+impl From<i64> for Value {
+ fn from(from: i64) -> Value {
+ Value::Integer(Integer::from(from))
+ }
+}
+
+impl From<i32> for Value {
+ fn from(from: i32) -> Value {
+ Value::Integer(Integer::from(from))
+ }
+}
+
+impl From<i16> for Value {
+ fn from(from: i16) -> Value {
+ Value::Integer(Integer::from(from))
+ }
+}
+
+impl From<i8> for Value {
+ fn from(from: i8) -> Value {
+ Value::Integer(Integer::from(from))
+ }
+}
+
+impl From<u64> for Value {
+ fn from(from: u64) -> Value {
+ Value::Integer(Integer::from(from))
+ }
+}
+
+impl From<u32> for Value {
+ fn from(from: u32) -> Value {
+ Value::Integer(Integer::from(from))
+ }
+}
+
+impl From<u16> for Value {
+ fn from(from: u16) -> Value {
+ Value::Integer(Integer::from(from))
+ }
+}
+
+impl From<u8> 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<String> 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<T> {
+ stream: T,
+ token: Option<OwnedEvent>,
+}
+
+impl<T: Iterator<Item = Result<OwnedEvent, Error>>> Builder<T> {
+ fn new(stream: T) -> Builder<T> {
+ Builder {
+ stream,
+ token: None,
+ }
+ }
+
+ fn build(mut self) -> Result<Value, Error> {
+ 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<Value, Error> {
+ 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.into_owned())),
+ 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.into_owned())),
+ Some(Event::Uid(u)) => Ok(Value::Uid(u)),
+
+ Some(event @ Event::EndCollection) => Err(error::unexpected_event_type(
+ EventKind::ValueOrStartCollection,
+ &event,
+ )),
+
+ None => Err(ErrorKind::UnexpectedEndOfEventStream.without_position()),
+ }
+ }
+
+ fn build_array(&mut self, len: Option<u64>) -> Result<Vec<Value>, 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<u64>) -> Result<Dictionary, Error> {
+ 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.into_owned(), 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".into()),
+ String("William Shakespeare".into()),
+ String("Lines".into()),
+ StartArray(None),
+ String("It is a tale told by an idiot,".into()),
+ String("Full of sound and fury, signifying nothing.".into()),
+ EndCollection,
+ String("Birthdate".into()),
+ Integer(1564.into()),
+ String("Height".into()),
+ 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));
+ }
+}
diff --git a/third_party/rust/plist/tests/data/binary.plist b/third_party/rust/plist/tests/data/binary.plist
new file mode 100644
index 0000000000..c9c1d5861f
--- /dev/null
+++ b/third_party/rust/plist/tests/data/binary.plist
Binary files differ
diff --git a/third_party/rust/plist/tests/data/binary_NSKeyedArchiver.plist b/third_party/rust/plist/tests/data/binary_NSKeyedArchiver.plist
new file mode 100644
index 0000000000..3f86b812de
--- /dev/null
+++ b/third_party/rust/plist/tests/data/binary_NSKeyedArchiver.plist
Binary files differ
diff --git a/third_party/rust/plist/tests/data/binary_circular_array.plist b/third_party/rust/plist/tests/data/binary_circular_array.plist
new file mode 100644
index 0000000000..57d4575e5c
--- /dev/null
+++ b/third_party/rust/plist/tests/data/binary_circular_array.plist
Binary files differ
diff --git a/third_party/rust/plist/tests/data/binary_zero_offset_size.plist b/third_party/rust/plist/tests/data/binary_zero_offset_size.plist
new file mode 100644
index 0000000000..83ede66d50
--- /dev/null
+++ b/third_party/rust/plist/tests/data/binary_zero_offset_size.plist
Binary files differ
diff --git a/third_party/rust/plist/tests/data/book.plist b/third_party/rust/plist/tests/data/book.plist
new file mode 100644
index 0000000000..048ca6bb3c
--- /dev/null
+++ b/third_party/rust/plist/tests/data/book.plist
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Title</key>
+ <string>Great Expectations</string>
+ <key>Author</key>
+ <string>Charles Dickens</string>
+ <key>Excerpt</key>
+ <string>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.</string>
+ <key>CopiesSold</key>
+ <integer>123456789</integer>
+</dict>
+</plist>
diff --git a/third_party/rust/plist/tests/data/utf16_bplist.plist b/third_party/rust/plist/tests/data/utf16_bplist.plist
new file mode 100644
index 0000000000..a50477c4d0
--- /dev/null
+++ b/third_party/rust/plist/tests/data/utf16_bplist.plist
Binary files differ
diff --git a/third_party/rust/plist/tests/data/xml.plist b/third_party/rust/plist/tests/data/xml.plist
new file mode 100644
index 0000000000..a4016a245a
--- /dev/null
+++ b/third_party/rust/plist/tests/data/xml.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Author</key>
+ <string>William Shakespeare</string>
+ <key>Lines</key>
+ <array>
+ <string>It is a tale told by an idiot,</string>
+ <string>Full of sound and fury, signifying nothing.</string>
+ </array>
+ <key>Death</key>
+ <integer>1564</integer>
+ <key>Height</key>
+ <real>1.6</real>
+ <key>Data</key>
+ <data>
+ AAAAvgAAAA
+ MAAAAeAAAA
+ </data>
+ <key>Birthdate</key>
+ <date>1981-05-16T11:32:06Z</date>
+ <key>Blank</key>
+ <string></string>
+ <key>BiggestNumber</key>
+ <integer>18446744073709551615</integer>
+ <key>SmallestNumber</key>
+ <integer>-9223372036854775808</integer>
+ <key>HexademicalNumber</key>
+ <integer>0xDEADBEEF</integer>
+ <key>IsTrue</key>
+ <true/>
+ <key>IsNotFalse</key>
+ <false/>
+</dict>
+</plist>
diff --git a/third_party/rust/plist/tests/data/xml_error.plist b/third_party/rust/plist/tests/data/xml_error.plist
new file mode 100644
index 0000000000..8fe6a96ae4
--- /dev/null
+++ b/third_party/rust/plist/tests/data/xml_error.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Author</key>
+ <string>William Shakespeare</string>
+ <key>Lines</key>
+ <array>
+ <string>It is a tale told by an idiot,</string>
+ <string>Full of sound and fury, signifying nothing.</string>
+ </array>
+ <key>Death</key>
+ <integer>1564</integer>
+ <key>Height</key>
+ <real>1.6</real>
+ <key>Data</ke
+ BADNESS \ No newline at end of file
diff --git a/third_party/rust/plist/tests/fuzzer.rs b/third_party/rust/plist/tests/fuzzer.rs
new file mode 100644
index 0000000000..df5d81af05
--- /dev/null
+++ b/third_party/rust/plist/tests/fuzzer.rs
@@ -0,0 +1,87 @@
+extern crate plist;
+
+use plist::{Error, Value};
+use std::io::Cursor;
+
+#[test]
+fn too_large_allocation() {
+ let data = b"bplist00\"&L^^^^^^^^-^^^^^^^^^^^";
+ test_fuzzer_data_err(data);
+}
+
+#[test]
+fn too_large_allocation_2() {
+ let data = b"bplist00;<)\x9fX\x0a<h\x0a:hhhhG:hh\x0amhhhhhhx#hhT)\x0a*";
+ test_fuzzer_data_err(data);
+}
+
+#[test]
+fn empty_offset_table() {
+ let data = b"bplist00;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<)\x9fXTX(";
+ test_fuzzer_data_err(data);
+}
+
+#[test]
+fn binary_circular_reference() {
+ let data = b"bplist00\xd6\x01\x02\x03\x04\x05\x06\x07\x0a\x0b\x0c\x0d\x0eULinesUDeathVHeightYBirthdateVAutbplist00\xd6\x01\x02\x03\x04\x05\x06\x07\x0a\x0b\x0c\x0d\x0eULinesUDeathVHeightYBirthdateVAuthorTData\xa2\x08\x09_\x10\x1eIt is nifying nothing.\x11\x06\x1c#?\xf9\x99\x99\x99\x99\x99\x9a3\xc1\xc2v\x00e\x00\x00\x00_\x10\x13William ShakespeareO\x10\x0f\x00\x00\x00\xbe\x00\x00\x00\x03\x00\x00\x00\x1e\x00\x00\x00\x08\x15\x1b!(58>Ab\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);
+}
+
+#[test]
+fn overflow_instant_add() {
+ let data = b"bplist00\x10\x01\x00\x00\x00\x00\x00\x003~\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00";
+
+ test_fuzzer_data_err(data);
+}
+
+#[test]
+fn overflow_instant_sub() {
+ let data = b"bplist00\x10\x01\x00\x00\x00\x00\x00\x003\xfe\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00";
+
+ test_fuzzer_data_err(data);
+}
+
+fn test_fuzzer_data(data: &[u8]) -> Result<Value, Error> {
+ 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());
+}