diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/plist | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/plist')
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 < 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 Binary files differnew file mode 100644 index 0000000000..c9c1d5861f --- /dev/null +++ b/third_party/rust/plist/tests/data/binary.plist diff --git a/third_party/rust/plist/tests/data/binary_NSKeyedArchiver.plist b/third_party/rust/plist/tests/data/binary_NSKeyedArchiver.plist Binary files differnew file mode 100644 index 0000000000..3f86b812de --- /dev/null +++ b/third_party/rust/plist/tests/data/binary_NSKeyedArchiver.plist diff --git a/third_party/rust/plist/tests/data/binary_circular_array.plist b/third_party/rust/plist/tests/data/binary_circular_array.plist Binary files differnew file mode 100644 index 0000000000..57d4575e5c --- /dev/null +++ b/third_party/rust/plist/tests/data/binary_circular_array.plist 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 Binary files differnew file mode 100644 index 0000000000..83ede66d50 --- /dev/null +++ b/third_party/rust/plist/tests/data/binary_zero_offset_size.plist 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 Binary files differnew file mode 100644 index 0000000000..a50477c4d0 --- /dev/null +++ b/third_party/rust/plist/tests/data/utf16_bplist.plist 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()); +} |