plist-1.6.0/.cargo_vcs_info.json0000644000000001360000000000100121730ustar { "git": { "sha1": "d8abd76d9c2e2b5cbbdede6736b3f5b8c1d51f35" }, "path_in_vcs": "" }plist-1.6.0/.editorconfig000064400000000000000000000004311046102023000134360ustar 00000000000000# EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 plist-1.6.0/.github/workflows/ci.yml000064400000000000000000000072771046102023000155130ustar 00000000000000on: push: branches: - master pull_request: branches: - master name: CI env: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse jobs: build_and_test: name: Build and Test runs-on: ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-22.04 target: x86_64-unknown-linux-gnu channel: 1.68.0 - os: ubuntu-22.04 target: x86_64-unknown-linux-gnu channel: stable - os: ubuntu-22.04 target: x86_64-unknown-linux-gnu channel: beta - os: ubuntu-22.04 target: x86_64-unknown-linux-gnu channel: nightly steps: - name: Checkout uses: actions/checkout@v3 - name: Install Rust Toolchain run: | rustup default "$TOOLCHAIN" rustup update "$TOOLCHAIN" env: TOOLCHAIN: ${{ matrix.channel }}-${{ matrix.target }} - name: Build and Test (Debug, No Default Features) run: cargo test --no-default-features - name: Build and Test (Debug, enable_unstable_features_that_may_break_with_minor_version_bumps) run: cargo test --no-default-features --features enable_unstable_features_that_may_break_with_minor_version_bumps - name: Build and Test (Debug, serde) run: cargo test --no-default-features --features serde - name: Build and Test (Release, All Features) run: cargo test --release --all-features - name: Build and Test (Minimal Versions, All Features) if: matrix.channel == 'nightly' run: cargo test --all-features -Z minimal-versions lint: name: Lint runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v3 - name: Check for broken intra-doc links run: cargo doc --all-features --document-private-items --no-deps ci_succeeded: name: Build and Test Succeeded if: always() needs: [build_and_test, lint] runs-on: ubuntu-22.04 outputs: release: ${{ steps.release_check.outputs.release }} tag_name: ${{ steps.release_check.outputs.tag_name }} steps: - name: Fail if Any Previous Job Failed if: contains(needs.*.result, 'failure') run: exit 1 - name: Checkout uses: actions/checkout@v3 - name: Check if Release Needed id: release_check run: | set -euo pipefail TAG_NAME="v$(cargo metadata --no-deps --format-version=1 | jq -er '.packages[] | select(.name=="plist") | .version')" echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" if git ls-remote --exit-code origin "refs/tags/$TAG_NAME" then echo "release=false" >> "$GITHUB_OUTPUT" else echo "release=true" >> "$GITHUB_OUTPUT" fi release: name: Release needs: ci_succeeded if: github.ref == 'refs/heads/master' && needs.ci_succeeded.outputs.release == 'true' runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v3 - name: Install cargo-semver-checks run: cargo install cargo-semver-checks --locked - name: Run cargo-semver-checks run: cargo semver-checks check-release - name: Publish Crate run: | cargo publish --token "$CRATES_IO_TOKEN" env: CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} - name: Create GitHub Release uses: actions/create-release@latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ needs.ci_succeeded.outputs.tag_name }} release_name: ${{ needs.ci_succeeded.outputs.tag_name }} draft: false prerelease: false plist-1.6.0/.gitignore000064400000000000000000000000301046102023000127440ustar 00000000000000target Cargo.lock .idea plist-1.6.0/Cargo.toml0000644000000026630000000000100102000ustar # 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 = "2021" rust-version = "1.68" name = "plist" version = "1.6.0" authors = ["Ed Barnard "] description = "A rusty plist parser. Supports Serde serialization." documentation = "https://docs.rs/plist/" readme = "README.md" keywords = [ "plist", "parser", ] categories = [ "config", "encoding", "parser-implementations", ] license = "MIT" repository = "https://github.com/ebarnard/rust-plist/" [dependencies.base64] version = "0.21.0" [dependencies.indexmap] version = "2.1.0" [dependencies.line-wrap] version = "0.1.1" [dependencies.quick_xml] version = "0.31.0" package = "quick-xml" [dependencies.serde] version = "1.0.2" optional = true [dependencies.time] version = "0.3.30" features = [ "parsing", "formatting", ] [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 = [] plist-1.6.0/Cargo.toml.orig000064400000000000000000000014751046102023000136610ustar 00000000000000[package] name = "plist" version = "1.6.0" authors = ["Ed Barnard "] description = "A rusty plist parser. Supports Serde serialization." license = "MIT" repository = "https://github.com/ebarnard/rust-plist/" documentation = "https://docs.rs/plist/" keywords = ["plist", "parser"] categories = ["config", "encoding", "parser-implementations"] edition = "2021" rust-version = "1.68" [features] default = ["serde"] enable_unstable_features_that_may_break_with_minor_version_bumps = [] [dependencies] base64 = "0.21.0" time = { version = "0.3.30", features = ["parsing", "formatting"] } indexmap = "2.1.0" line-wrap = "0.1.1" quick_xml = { package = "quick-xml", version = "0.31.0" } serde = { version = "1.0.2", optional = true } [dev-dependencies] serde_derive = { version = "1.0.2" } serde_yaml = "0.8.21" plist-1.6.0/LICENCE000064400000000000000000000020411046102023000117450ustar 00000000000000Copyright (c) 2015 Edward Barnard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.plist-1.6.0/README.md000064400000000000000000000007211046102023000122420ustar 00000000000000# 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/) plist-1.6.0/rustfmt.toml000064400000000000000000000000341046102023000133610ustar 00000000000000imports_granularity="Crate" plist-1.6.0/src/data.rs000064400000000000000000000067201046102023000130360ustar 00000000000000use std::fmt; use base64::{engine::general_purpose::STANDARD as base64_standard, Engine}; /// A byte buffer used for serialization to and from the plist data type. /// /// You use it in types with derived `Serialize`/`Deserialize` traits. /// /// ## Examples /// /// ```rust /// extern crate plist; /// #[macro_use] /// extern crate serde_derive; /// /// # fn main() { /// #[derive(Deserialize, Serialize)] /// struct Info { /// blob: plist::Data, /// } /// /// let actual = Info { blob: plist::Data::new(vec![1, 2, 3, 4]) }; /// /// let mut xml_byte_buffer: Vec = vec![]; /// plist::to_writer_xml(&mut xml_byte_buffer, &actual) /// .expect("serialize into xml"); /// /// let expected: Info = plist::from_reader_xml(xml_byte_buffer.as_slice()) /// .expect("deserialize from xml"); /// /// assert_eq!(actual.blob, expected.blob); /// # } /// ``` #[derive(Clone, PartialEq, Eq)] pub struct Data { inner: Vec, } /// An error indicating a string was not valid XML data. #[derive(Debug)] pub struct InvalidXmlData(base64::DecodeError); impl Data { /// Creates a new `Data` from vec of bytes. pub fn new(bytes: Vec) -> Self { Data { inner: bytes } } /// Create a `Data` object from an XML plist (Base-64) encoded string. pub fn from_xml_format(b64_str: &str) -> Result { base64_standard .decode(b64_str) .map_err(InvalidXmlData) .map(Data::new) } /// Converts the `Data` to an XML plist (Base-64) string. pub fn to_xml_format(&self) -> String { crate::stream::base64_encode_plist(&self.inner, 0) } } impl From> for Data { fn from(from: Vec) -> Self { Data { inner: from } } } impl From for Vec { fn from(from: Data) -> Self { from.inner } } impl AsRef<[u8]> for Data { fn as_ref(&self) -> &[u8] { self.inner.as_ref() } } impl AsMut<[u8]> for Data { fn as_mut(&mut self) -> &mut [u8] { self.inner.as_mut() } } impl fmt::Debug for Data { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } impl fmt::Display for InvalidXmlData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Invalid XML data: '{}'", self.0) } } impl std::error::Error for InvalidXmlData {} pub mod serde_impls { use serde::{de, ser}; use std::fmt; use crate::Data; impl ser::Serialize for Data { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { serializer.serialize_bytes(self.as_ref()) } } struct DataVisitor; impl<'de> de::Visitor<'de> for DataVisitor { type Value = Data; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a byte array") } fn visit_bytes(self, v: &[u8]) -> Result where E: de::Error, { self.visit_byte_buf(v.to_owned()) } fn visit_byte_buf(self, v: Vec) -> Result where E: de::Error, { Ok(v.into()) } } impl<'de> de::Deserialize<'de> for Data { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { deserializer.deserialize_byte_buf(DataVisitor) } } } plist-1.6.0/src/date.rs000064400000000000000000000133731046102023000130440ustar 00000000000000use 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, } /// An error indicating that a string was not a valid XML plist date. #[derive(Debug)] #[non_exhaustive] pub struct InvalidXmlDate; 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); /// Converts an XML plist date string to a `Date`. pub fn from_xml_format(date: &str) -> Result { let offset: OffsetDateTime = OffsetDateTime::parse(date, &Rfc3339) .map_err(|_| InvalidXmlDate)? .to_offset(UtcOffset::UTC); Ok(Date { inner: offset.into(), }) } /// Converts the `Date` to an XML plist date string. pub fn to_xml_format(&self) -> String { let datetime: OffsetDateTime = self.inner.into(); datetime.format(&Rfc3339).unwrap() } pub(crate) fn from_seconds_since_plist_epoch( timestamp: f64, ) -> Result { // `timestamp` is the number of seconds since the plist epoch of 1/1/2001 00:00:00. let plist_epoch = UNIX_EPOCH + Date::PLIST_EPOCH_UNIX_TIMESTAMP; if !timestamp.is_finite() { return Err(InfiniteOrNanDate); } let is_negative = timestamp < 0.0; let timestamp = timestamp.abs(); let seconds = timestamp.floor() as u64; let subsec_nanos = (timestamp.fract() * 1e9) as u32; let dur_since_plist_epoch = Duration::new(seconds, subsec_nanos); let inner = if is_negative { plist_epoch.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 as_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_xml_format()) } } impl From for Date { fn from(date: SystemTime) -> Self { Date { inner: date } } } impl From for SystemTime { fn from(val: Date) -> Self { val.inner } } impl fmt::Display for InvalidXmlDate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("String was not a valid XML plist date") } } impl std::error::Error for InvalidXmlDate {} #[cfg(feature = "serde")] pub mod serde_impls { use serde::{ de::{Deserialize, Deserializer, Error, Unexpected, Visitor}, ser::{Serialize, Serializer}, }; use std::fmt; use crate::Date; pub const DATE_NEWTYPE_STRUCT_NAME: &str = "PLIST-DATE"; impl Serialize for Date { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let date_str = self.to_xml_format(); 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(self, v: &str) -> Result where E: Error, { DateStrVisitor.visit_str(v) } fn visit_newtype_struct(self, deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(DateStrVisitor) } } struct DateStrVisitor; impl<'de> Visitor<'de> for DateStrVisitor { type Value = Date; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a plist date string") } fn visit_str(self, v: &str) -> Result where E: Error, { Date::from_xml_format(v).map_err(|_| E::invalid_value(Unexpected::Str(v), &self)) } } impl<'de> Deserialize<'de> for Date { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_newtype_struct(DATE_NEWTYPE_STRUCT_NAME, DateNewtypeVisitor) } } } #[cfg(test)] mod testing { use super::*; #[test] fn date_roundtrip() { let date_str = "1981-05-16T11:32:06Z"; let date = Date::from_xml_format(date_str).expect("should parse"); let generated_str = date.to_xml_format(); assert_eq!(date_str, generated_str); } #[test] fn far_past_date() { let date_str = "1920-01-01T00:00:00Z"; Date::from_xml_format(date_str).expect("should parse"); } } plist-1.6.0/src/de.rs000064400000000000000000000326421046102023000125170ustar 00000000000000use serde::de::{ self, value::{MapAccessDeserializer, MapDeserializer}, IntoDeserializer, }; use std::{ borrow::Cow, 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}, u64_to_usize, uid::serde_impls::UID_NEWTYPE_STRUCT_NAME, value::serde_impls::VALUE_NEWTYPE_STRUCT_NAME, Value, }; macro_rules! expect { ($next:expr, $kind:expr) => { match $next { Some(Ok(ref event)) if EventKind::of_event(event) != $kind => { return Err(error::unexpected_event_type($kind, event))?; } Some(Ok(event)) => event, Some(Err(err)) => return Err(err), None => return Err(ErrorKind::UnexpectedEndOfEventStream.without_position()), } }; } macro_rules! try_next { ($next:expr) => { match $next { Some(Ok(event)) => event, Some(Err(err)) => return Err(err)?, None => return Err(ErrorKind::UnexpectedEndOfEventStream.without_position())?, } }; } #[doc(hidden)] impl de::Error for Error { fn custom(msg: T) -> Self { ErrorKind::Serde(msg.to_string()).without_position() } } enum OptionMode { Root, StructField, Explicit, } /// A structure that deserializes plist event streams into Rust values. pub struct Deserializer<'event, I> where I: IntoIterator, Error>>, { events: Peekable<::IntoIter>, option_mode: OptionMode, in_plist_value: bool, } impl<'event, I> Deserializer<'event, I> where I: IntoIterator, Error>>, { pub fn new(iter: I) -> Deserializer<'event, I> { Deserializer { events: iter.into_iter().peekable(), option_mode: OptionMode::Root, in_plist_value: false, } } fn with_option_mode) -> Result>( &mut self, option_mode: OptionMode, f: F, ) -> Result { let prev_option_mode = mem::replace(&mut self.option_mode, option_mode); let ret = f(&mut *self); self.option_mode = prev_option_mode; ret } fn enter_plist_value) -> Result>( &mut self, f: F, ) -> Result { let prev = mem::replace(&mut self.in_plist_value, true); let ret = f(&mut *self); self.in_plist_value = prev; ret } } impl<'de, 'a, 'event, I> de::Deserializer<'de> for &'a mut Deserializer<'event, I> where I: IntoIterator, Error>>, { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: de::Visitor<'de>, { match try_next!(self.events.next()) { Event::StartArray(len) => { let len = len.and_then(u64_to_usize); let ret = visitor.visit_seq(MapAndSeqAccess::new(self, false, len))?; expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } Event::StartDictionary(len) => { let len = len.and_then(u64_to_usize); let ret = visitor.visit_map(MapAndSeqAccess::new(self, false, len))?; expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } event @ Event::EndCollection => Err(error::unexpected_event_type( EventKind::ValueOrStartCollection, &event, )), Event::Boolean(v) => visitor.visit_bool(v), Event::Data(Cow::Borrowed(v)) => visitor.visit_bytes(v), Event::Data(Cow::Owned(v)) => visitor.visit_byte_buf(v), Event::Date(v) if self.in_plist_value => { visitor.visit_enum(MapAccessDeserializer::new(MapDeserializer::new( [(DATE_NEWTYPE_STRUCT_NAME, v.to_xml_format())].into_iter(), ))) } Event::Date(v) => visitor.visit_string(v.to_xml_format()), 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(Cow::Borrowed(v)) => visitor.visit_str(v), Event::String(Cow::Owned(v)) => visitor.visit_string(v), Event::Uid(v) if self.in_plist_value => visitor.visit_enum(MapAccessDeserializer::new( MapDeserializer::new([(UID_NEWTYPE_STRUCT_NAME, v.get())].into_iter()), )), 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(self, visitor: V) -> Result where V: de::Visitor<'de>, { expect!(self.events.next(), EventKind::String); visitor.visit_unit() } fn deserialize_option(self, visitor: V) -> Result where V: de::Visitor<'de>, { match self.option_mode { OptionMode::Root => { if self.events.peek().is_none() { visitor.visit_none::() } else { self.with_option_mode(OptionMode::Explicit, |this| visitor.visit_some(this)) } } OptionMode::StructField => { // None struct values are ignored so if we're here the value must be Some. self.with_option_mode(OptionMode::Explicit, |this| visitor.visit_some(this)) } OptionMode::Explicit => { expect!(self.events.next(), EventKind::StartDictionary); let ret = match try_next!(self.events.next()) { Event::String(ref s) if &s[..] == "None" => { expect!(self.events.next(), EventKind::String); visitor.visit_none::()? } Event::String(ref s) if &s[..] == "Some" => visitor.visit_some(&mut *self)?, event => return Err(error::unexpected_event_type(EventKind::String, &event))?, }; expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } } } fn deserialize_newtype_struct( self, name: &'static str, visitor: V, ) -> Result where V: de::Visitor<'de>, { 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( self, _name: &'static str, _fields: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { expect!(self.events.next(), EventKind::StartDictionary); let ret = visitor.visit_map(MapAndSeqAccess::new(self, true, None))?; expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } fn deserialize_enum( self, name: &'static str, variants: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { let event = self.events.next(); // `plist` since v1.1 serialises unit enum variants as plain strings. if let Some(Ok(Event::String(s))) = event { return match s { Cow::Borrowed(s) => s .into_deserializer() .deserialize_enum(name, variants, visitor), Cow::Owned(s) => s .into_deserializer() .deserialize_enum(name, variants, visitor), }; } expect!(event, EventKind::StartDictionary); let ret = visitor.visit_enum(&mut *self)?; expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } } impl<'de, 'a, 'event, I> de::EnumAccess<'de> for &'a mut Deserializer<'event, I> where I: IntoIterator, Error>>, { type Error = Error; type Variant = Self; fn variant_seed(self, seed: V) -> Result<(V::Value, Self), Error> where V: de::DeserializeSeed<'de>, { Ok((seed.deserialize(&mut *self)?, self)) } } impl<'de, 'a, 'event, I> de::VariantAccess<'de> for &'a mut Deserializer<'event, I> where I: IntoIterator, Error>>, { type Error = Error; fn unit_variant(self) -> Result<(), Error> { <() as de::Deserialize>::deserialize(self) } fn newtype_variant_seed(self, seed: T) -> Result where T: de::DeserializeSeed<'de>, { seed.deserialize(self) } fn tuple_variant(self, len: usize, visitor: V) -> Result where V: de::Visitor<'de>, { de::Deserializer::deserialize_tuple(self, len, visitor) } fn struct_variant( self, fields: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { let name = ""; de::Deserializer::deserialize_struct(self, name, fields, visitor) } } struct MapAndSeqAccess<'a, 'event, I> where I: 'a + IntoIterator, Error>>, { de: &'a mut Deserializer<'event, I>, is_struct: bool, remaining: Option, } impl<'a, 'event, I> MapAndSeqAccess<'a, 'event, I> where I: 'a + IntoIterator, Error>>, { fn new( de: &'a mut Deserializer<'event, I>, is_struct: bool, len: Option, ) -> MapAndSeqAccess<'a, 'event, I> { MapAndSeqAccess { de, is_struct, remaining: len, } } } impl<'de, 'a, 'event, I> de::SeqAccess<'de> for MapAndSeqAccess<'a, 'event, I> where I: 'a + IntoIterator, Error>>, { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result, Error> where T: de::DeserializeSeed<'de>, { if let Some(&Ok(Event::EndCollection)) = self.de.events.peek() { return Ok(None); } self.remaining = self.remaining.map(|r| r.saturating_sub(1)); self.de .with_option_mode(OptionMode::Explicit, |this| seed.deserialize(this)) .map(Some) } fn size_hint(&self) -> Option { self.remaining } } impl<'de, 'a, 'event, I> de::MapAccess<'de> for MapAndSeqAccess<'a, 'event, I> where I: 'a + IntoIterator, Error>>, { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result, Error> where K: de::DeserializeSeed<'de>, { if let Some(&Ok(Event::EndCollection)) = self.de.events.peek() { return Ok(None); } self.remaining = self.remaining.map(|r| r.saturating_sub(1)); self.de .with_option_mode(OptionMode::Explicit, |this| seed.deserialize(this)) .map(Some) } fn next_value_seed(&mut self, seed: V) -> Result where V: de::DeserializeSeed<'de>, { let option_mode = if self.is_struct { OptionMode::StructField } else { OptionMode::Explicit }; self.de .with_option_mode(option_mode, |this| seed.deserialize(this)) } fn size_hint(&self) -> Option { self.remaining } } /// Deserializes an instance of type `T` from a byte slice. pub fn from_bytes(bytes: &[u8]) -> Result { let cursor = Cursor::new(bytes); from_reader(cursor) } /// Deserializes an instance of type `T` from a plist file of any encoding. pub fn from_file, T: de::DeserializeOwned>(path: P) -> Result { let file = File::open(path).map_err(error::from_io_without_position)?; from_reader(BufReader::new(file)) } /// Deserializes an instance of type `T` from a seekable byte stream containing a plist of any encoding. pub fn from_reader(reader: R) -> Result { let reader = stream::Reader::new(reader); let mut de = Deserializer::new(reader); de::Deserialize::deserialize(&mut de) } /// Deserializes an instance of type `T` from a byte stream containing an XML encoded plist. pub fn from_reader_xml(reader: R) -> Result { let reader = stream::XmlReader::new(reader); let mut de = Deserializer::new(reader); de::Deserialize::deserialize(&mut de) } /// Interprets a [`Value`] as an instance of type `T`. pub fn from_value(value: &Value) -> Result { let events = value.events().map(Ok); let mut de = Deserializer::new(events); de::Deserialize::deserialize(&mut de) } plist-1.6.0/src/dictionary.rs000064400000000000000000000471511046102023000142750ustar 00000000000000//! 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. #[derive(Clone, Default, PartialEq)] pub struct Dictionary { map: IndexMap, } impl Dictionary { /// Makes a new empty `Dictionary`. #[inline] pub fn new() -> Self { Dictionary { map: IndexMap::new(), } } /// Clears the dictionary, removing all values. #[inline] pub fn clear(&mut self) { self.map.clear() } /// Returns a reference to the value corresponding to the key. #[inline] pub fn get(&self, key: &str) -> Option<&Value> { self.map.get(key) } /// Returns true if the dictionary contains a value for the specified key. #[inline] pub fn contains_key(&self, key: &str) -> bool { self.map.contains_key(key) } /// Returns a mutable reference to the value corresponding to the key. #[inline] pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> { self.map.get_mut(key) } /// Inserts a key-value pair into the dictionary. /// /// If the dictionary did not have this key present, `None` is returned. /// /// If the dictionary did have this key present, the value is updated, and the old value is /// returned. #[inline] pub fn insert(&mut self, k: String, v: Value) -> Option { self.map.insert(k, v) } /// Removes a key from the dictionary, returning the value at the key if the key was previously /// in the dictionary. #[inline] pub fn remove(&mut self, key: &str) -> Option { self.map.remove(key) } /// Scan through each key-value pair in the map and keep those where the /// closure `keep` returns `true`. #[inline] pub fn retain(&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 or // T: AsRef + Into #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] pub fn entry(&mut self, key: S) -> Entry where S: Into, { match self.map.entry(key.into()) { map::Entry::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant }), map::Entry::Occupied(occupied) => Entry::Occupied(OccupiedEntry { occupied }), } } /// Returns the number of elements in the dictionary. #[inline] pub fn len(&self) -> usize { self.map.len() } /// Returns true if the dictionary contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.map.is_empty() } /// Gets an iterator over the entries of the dictionary. #[inline] pub fn iter(&self) -> Iter { Iter { iter: self.map.iter(), } } /// Gets a mutable iterator over the entries of the dictionary. #[inline] pub fn iter_mut(&mut self) -> IterMut { IterMut { iter: self.map.iter_mut(), } } /// Gets an iterator over the keys of the dictionary. #[inline] pub fn keys(&self) -> Keys { Keys { iter: self.map.keys(), } } /// Gets an iterator over the values of the dictionary. #[inline] pub fn values(&self) -> Values { Values { iter: self.map.values(), } } /// Gets an iterator over mutable values of the dictionary. #[inline] pub fn values_mut(&mut self) -> ValuesMut { ValuesMut { iter: self.map.values_mut(), } } } /// 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, V: Into> FromIterator<(K, V)> for Dictionary { fn from_iter>(iter: I) -> Self { Dictionary { map: iter .into_iter() .map(|(k, v)| (k.into(), v.into())) .collect(), } } } impl Extend<(String, Value)> for Dictionary { fn extend(&mut self, iter: T) where T: IntoIterator, { self.map.extend(iter); } } macro_rules! delegate_iterator { (($name:ident $($generics:tt)*) => $item:ty) => { impl $($generics)* Iterator for $name $($generics)* { type Item = $item; #[inline] fn next(&mut self) -> Option { self.iter.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } /*impl $($generics)* DoubleEndedIterator for $name $($generics)* { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back() } }*/ impl $($generics)* ExactSizeIterator for $name $($generics)* { #[inline] fn len(&self) -> usize { self.iter.len() } } } } ////////////////////////////////////////////////////////////////////////////// /// A view into a single entry in a dictionary, which may either be vacant or occupied. /// This enum is constructed from the [`entry`] method on [`Dictionary`]. /// /// [`entry`]: struct.Dictionary.html#method.entry /// [`Dictionary`]: struct.Dictionary.html #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] pub enum Entry<'a> { /// A vacant Entry. Vacant(VacantEntry<'a>), /// An occupied Entry. Occupied(OccupiedEntry<'a>), } /// A vacant Entry. It is part of the [`Entry`] enum. /// /// [`Entry`]: enum.Entry.html #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] pub struct VacantEntry<'a> { vacant: map::VacantEntry<'a, String, Value>, } /// An occupied Entry. It is part of the [`Entry`] enum. /// /// [`Entry`]: enum.Entry.html #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] pub struct OccupiedEntry<'a> { occupied: map::OccupiedEntry<'a, String, Value>, } #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] impl<'a> Entry<'a> { /// Returns a reference to this entry's key. /// /// # Examples /// /// ``` /// let mut dict = plist::Dictionary::new(); /// assert_eq!(dict.entry("serde").key(), &"serde"); /// ``` pub fn key(&self) -> &String { match *self { Entry::Vacant(ref e) => e.key(), Entry::Occupied(ref e) => e.key(), } } /// Ensures a value is in the entry by inserting the default if empty, and returns a mutable /// reference to the value in the entry. /// /// # Examples /// /// ``` /// let mut dict = plist::Dictionary::new(); /// dict.entry("serde").or_insert(12.into()); /// /// assert_eq!(dict["serde"], 12.into()); /// ``` pub fn or_insert(self, default: Value) -> &'a mut Value { match self { Entry::Vacant(entry) => entry.insert(default), Entry::Occupied(entry) => entry.into_mut(), } } /// Ensures a value is in the entry by inserting the result of the default function if empty, /// and returns a mutable reference to the value in the entry. /// /// # Examples /// /// ``` /// let mut dict = plist::Dictionary::new(); /// dict.entry("serde").or_insert_with(|| "hoho".into()); /// /// assert_eq!(dict["serde"], "hoho".into()); /// ``` pub fn or_insert_with(self, default: F) -> &'a mut Value where F: FnOnce() -> Value, { match self { Entry::Vacant(entry) => entry.insert(default()), Entry::Occupied(entry) => entry.into_mut(), } } } #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] impl<'a> VacantEntry<'a> { /// Gets a reference to the key that would be used when inserting a value through the /// VacantEntry. /// /// # Examples /// /// ``` /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// /// match dict.entry("serde") { /// Entry::Vacant(vacant) => assert_eq!(vacant.key(), &"serde"), /// Entry::Occupied(_) => unimplemented!(), /// } /// ``` #[inline] pub fn key(&self) -> &String { self.vacant.key() } /// Sets the value of the entry with the VacantEntry's key, and returns a mutable reference /// to it. /// /// # Examples /// /// ``` /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// /// match dict.entry("serde") { /// Entry::Vacant(vacant) => vacant.insert("hoho".into()), /// Entry::Occupied(_) => unimplemented!(), /// }; /// ``` #[inline] pub fn insert(self, value: Value) -> &'a mut Value { self.vacant.insert(value) } } #[cfg(any( test, feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" ))] impl<'a> OccupiedEntry<'a> { /// Gets a reference to the key in the entry. /// /// # Examples /// /// ``` /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), 12.into()); /// /// match dict.entry("serde") { /// Entry::Occupied(occupied) => assert_eq!(occupied.key(), &"serde"), /// Entry::Vacant(_) => unimplemented!(), /// } /// ``` #[inline] pub fn key(&self) -> &String { self.occupied.key() } /// Gets a reference to the value in the entry. /// /// # Examples /// /// ``` /// use plist::Value; /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), 12.into()); /// /// match dict.entry("serde") { /// Entry::Occupied(occupied) => assert_eq!(occupied.get(), &Value::from(12)), /// Entry::Vacant(_) => unimplemented!(), /// } /// ``` #[inline] pub fn get(&self) -> &Value { self.occupied.get() } /// Gets a mutable reference to the value in the entry. /// /// # Examples /// /// ``` /// use plist::Value; /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), Value::Array(vec![1.into(), 2.into(), 3.into()])); /// /// match dict.entry("serde") { /// Entry::Occupied(mut occupied) => { /// occupied.get_mut().as_array_mut().unwrap().push(4.into()); /// } /// Entry::Vacant(_) => unimplemented!(), /// } /// /// assert_eq!(dict["serde"].as_array().unwrap().len(), 4); /// ``` #[inline] pub fn get_mut(&mut self) -> &mut Value { self.occupied.get_mut() } /// Converts the entry into a mutable reference to its value. /// /// # Examples /// /// ``` /// use plist::Value; /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), Value::Array(vec![1.into(), 2.into(), 3.into()])); /// /// match dict.entry("serde") { /// Entry::Occupied(mut occupied) => { /// occupied.into_mut().as_array_mut().unwrap().push(4.into()); /// } /// Entry::Vacant(_) => unimplemented!(), /// } /// /// assert_eq!(dict["serde"].as_array().unwrap().len(), 4); /// ``` #[inline] pub fn into_mut(self) -> &'a mut Value { self.occupied.into_mut() } /// Sets the value of the entry with the `OccupiedEntry`'s key, and returns /// the entry's old value. /// /// # Examples /// /// ``` /// use plist::Value; /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), 12.into()); /// /// match dict.entry("serde") { /// Entry::Occupied(mut occupied) => { /// assert_eq!(occupied.insert(13.into()), 12.into()); /// assert_eq!(occupied.get(), &Value::from(13)); /// } /// Entry::Vacant(_) => unimplemented!(), /// } /// ``` #[inline] pub fn insert(&mut self, value: Value) -> Value { self.occupied.insert(value) } /// Takes the value of the entry out of the dictionary, and returns it. /// /// # Examples /// /// ``` /// use plist::dictionary::Entry; /// /// let mut dict = plist::Dictionary::new(); /// dict.insert("serde".to_owned(), 12.into()); /// /// match dict.entry("serde") { /// Entry::Occupied(occupied) => assert_eq!(occupied.remove(), 12.into()), /// Entry::Vacant(_) => unimplemented!(), /// } /// ``` #[inline] pub fn remove(self) -> Value { self.occupied.remove() } } ////////////////////////////////////////////////////////////////////////////// impl<'a> IntoIterator for &'a Dictionary { type Item = (&'a String, &'a Value); type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { Iter { iter: self.map.iter(), } } } /// An iterator over a plist::Dictionary's entries. pub struct Iter<'a> { iter: IterImpl<'a>, } type IterImpl<'a> = map::Iter<'a, String, Value>; delegate_iterator!((Iter<'a>) => (&'a String, &'a Value)); ////////////////////////////////////////////////////////////////////////////// impl<'a> IntoIterator for &'a mut Dictionary { type Item = (&'a String, &'a mut Value); type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { IterMut { iter: self.map.iter_mut(), } } } /// A mutable iterator over a plist::Dictionary's entries. pub struct IterMut<'a> { iter: map::IterMut<'a, String, Value>, } delegate_iterator!((IterMut<'a>) => (&'a String, &'a mut Value)); ////////////////////////////////////////////////////////////////////////////// impl IntoIterator for Dictionary { type Item = (String, Value); type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { iter: self.map.into_iter(), } } } /// An owning iterator over a plist::Dictionary's entries. pub struct IntoIter { iter: map::IntoIter, } delegate_iterator!((IntoIter) => (String, Value)); ////////////////////////////////////////////////////////////////////////////// /// An iterator over a plist::Dictionary's keys. pub struct Keys<'a> { iter: map::Keys<'a, String, Value>, } delegate_iterator!((Keys<'a>) => &'a String); ////////////////////////////////////////////////////////////////////////////// /// An iterator over a plist::Dictionary's values. pub struct Values<'a> { iter: map::Values<'a, String, Value>, } delegate_iterator!((Values<'a>) => &'a Value); ////////////////////////////////////////////////////////////////////////////// /// A mutable iterator over a plist::Dictionary's values. pub struct ValuesMut<'a> { iter: map::ValuesMut<'a, String, Value>, } delegate_iterator!((ValuesMut<'a>) => &'a mut Value); #[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(&self, serializer: S) -> Result 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(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = Dictionary; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map") } #[inline] fn visit_unit(self) -> Result where E: de::Error, { Ok(Dictionary::new()) } #[inline] fn visit_map(self, mut visitor: V) -> Result where V: de::MapAccess<'de>, { let mut values = Dictionary::new(); while let Some((key, value)) = visitor.next_entry()? { values.insert(key, value); } Ok(values) } } deserializer.deserialize_map(Visitor) } } } #[cfg(test)] mod tests { use super::Dictionary; #[test] fn from_hash_map_to_dict() { let dict: Dictionary = [ ("Doge", "Shiba Inu"), ("Cheems", "Shiba Inu"), ("Walter", "Bull Terrier"), ("Perro", "Golden Retriever"), ] .into_iter() .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") ); } } plist-1.6.0/src/error.rs000064400000000000000000000153741046102023000132630ustar 00000000000000use std::{error, fmt, io}; #[cfg(feature = "serde")] use crate::stream::Event; use crate::{InvalidXmlDate, Value}; /// This type represents all possible errors that can occur when working with plist data. #[derive(Debug)] pub struct Error { inner: Box, } #[derive(Debug)] pub(crate) struct ErrorImpl { kind: ErrorKind, file_position: Option, } #[derive(Debug)] pub(crate) enum ErrorKind { UnexpectedEof, UnexpectedEndOfEventStream, UnexpectedEventType { // Used by the `Debug` implementation. #[allow(dead_code)] expected: EventKind, #[allow(dead_code)] found: EventKind, }, ExpectedEndOfEventStream { // Used by the `Debug` implementation. #[allow(dead_code)] found: EventKind, }, // Xml format-specific errors UnclosedXmlElement, 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), #[cfg(feature = "serde")] Serde(String), } #[derive(Debug, Clone, Copy)] pub(crate) struct FilePosition(pub(crate) 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 { matches!(self.inner.kind, ErrorKind::UnexpectedEof) } /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO /// stream. pub fn as_io(&self) -> Option<&io::Error> { if let ErrorKind::Io(err) = &self.inner.kind { Some(err) } else { None } } /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO /// stream or `self` if it was not. pub fn into_io(self) -> Result { if let ErrorKind::Io(err) = self.inner.kind { Ok(err) } else { Err(self) } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self.inner.kind { ErrorKind::Io(err) => Some(err), _ => None, } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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 { write!(f, "offset {}", self.0) } } impl From for Error { fn from(error: InvalidXmlDate) -> Self { ErrorKind::from(error).without_position() } } impl ErrorKind { pub fn with_byte_offset(self, offset: u64) -> Error { self.with_position(FilePosition(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 From for ErrorKind { fn from(_: InvalidXmlDate) -> Self { ErrorKind::InvalidDateString } } impl EventKind { #[cfg(feature = "serde")] 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, } } pub fn of_value(event: &Value) -> EventKind { match event { Value::Array(_) => EventKind::StartArray, Value::Dictionary(_) => EventKind::StartDictionary, 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, } } } 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() } #[cfg(feature = "serde")] pub(crate) fn unexpected_event_type(expected: EventKind, found: &Event) -> Error { let found = EventKind::of_event(found); ErrorKind::UnexpectedEventType { expected, found }.without_position() } plist-1.6.0/src/integer.rs000064400000000000000000000116321046102023000135600ustar 00000000000000use std::{fmt, num::ParseIntError}; /// An integer that can be represented by either an `i64` or a `u64`. #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Integer { value: i128, } impl Integer { /// Returns the value as an `i64` if it can be represented by that type. pub fn as_signed(self) -> Option { if self.value >= i128::from(i64::min_value()) && self.value <= i128::from(i64::max_value()) { Some(self.value as i64) } else { None } } /// Returns the value as a `u64` if it can be represented by that type. pub fn as_unsigned(self) -> Option { if self.value >= 0 && self.value <= i128::from(u64::max_value()) { Some(self.value as u64) } else { None } } pub(crate) fn from_str(s: &str) -> Result { if s.starts_with("0x") { // NetBSD dialect adds the `0x` numeric objects, // which are always unsigned. // See the `PROP_NUMBER(3)` man page let s = s.trim_start_matches("0x"); u64::from_str_radix(s, 16).map(Into::into) } else { // Match Apple's implementation in CFPropertyList.h - always try to parse as an i64 first. // TODO: Use IntErrorKind once stable and retry parsing on overflow only. Ok(match s.parse::() { Ok(v) => v.into(), Err(_) => s.parse::()?.into(), }) } } } impl fmt::Debug for Integer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } impl fmt::Display for Integer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } impl From for Integer { fn from(value: i64) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: i32) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: i16) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: i8) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: u64) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: u32) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: u16) -> Integer { Integer { value: value.into(), } } } impl From for Integer { fn from(value: u8) -> Integer { Integer { value: value.into(), } } } #[cfg(feature = "serde")] pub mod serde_impls { use serde::{ de::{Deserialize, Deserializer, Error, Visitor}, ser::{Serialize, Serializer}, }; use std::fmt; use crate::Integer; impl Serialize for Integer { fn serialize(&self, serializer: S) -> Result where S: Serializer, { if let Some(v) = self.as_unsigned() { serializer.serialize_u64(v) } else if let Some(v) = self.as_signed() { serializer.serialize_i64(v) } else { unreachable!(); } } } struct IntegerVisitor; impl<'de> Visitor<'de> for IntegerVisitor { type Value = Integer; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a plist integer") } fn visit_i64(self, v: i64) -> Result where E: Error, { Ok(Integer::from(v)) } fn visit_u64(self, v: u64) -> Result where E: Error, { Ok(Integer::from(v)) } } impl<'de> Deserialize<'de> for Integer { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(IntegerVisitor) } } } #[cfg(test)] mod tests { use super::Integer; #[test] fn from_str_limits() { assert_eq!(Integer::from_str("-1"), Ok((-1).into())); assert_eq!(Integer::from_str("0"), Ok(0.into())); assert_eq!(Integer::from_str("1"), Ok(1.into())); assert_eq!( Integer::from_str("-9223372036854775808"), Ok((-9223372036854775808i64).into()) ); assert!(Integer::from_str("-9223372036854775809").is_err()); assert_eq!( Integer::from_str("18446744073709551615"), Ok(18446744073709551615u64.into()) ); assert!(Integer::from_str("18446744073709551616").is_err()); } } plist-1.6.0/src/lib.rs000064400000000000000000000063171046102023000126750ustar 00000000000000//! # 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. #![deny(warnings)] // Treat all warnings as errors #![deny(rustdoc::broken_intra_doc_links)] 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; #[cfg(feature = "serde")] mod data; mod date; mod error; mod integer; mod uid; mod value; #[cfg(feature = "serde")] pub use data::Data; pub use date::{Date, InvalidXmlDate}; 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, from_value}, ser::{ to_file_binary, to_file_xml, to_value, 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 { let len = len_u64 as usize; if len as u64 != len_u64 { return None; // Too long } Some(len) } plist-1.6.0/src/ser.rs000064400000000000000000000566431046102023000127270ustar 00000000000000use serde::ser; use std::{ borrow::Cow, 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, Value, XmlWriteOptions, }; #[doc(hidden)] impl ser::Error for Error { fn custom(msg: T) -> Self { ErrorKind::Serde(msg.to_string()).without_position() } } enum OptionMode { Root, StructField(&'static str), StructFieldNameWritten, Explicit, } /// A structure that serializes Rust values plist event streams. pub struct Serializer { writer: W, option_mode: OptionMode, } impl Serializer { pub fn new(writer: W) -> Serializer { Serializer { writer, option_mode: OptionMode::Root, } } pub fn into_inner(self) -> W { self.writer } fn serialize_with_option_mode( &mut self, option_mode: OptionMode, value: &T, ) -> Result<(), Error> { let prev_option_mode = mem::replace(&mut self.option_mode, option_mode); let result = value.serialize(&mut *self); self.option_mode = prev_option_mode; result } fn maybe_write_pending_struct_field_name(&mut self) -> Result<(), Error> { if let OptionMode::StructField(field_name) = self.option_mode { self.option_mode = OptionMode::StructFieldNameWritten; self.writer.write_string(Cow::Borrowed(field_name))?; } Ok(()) } fn write_start_array(&mut self, len: Option) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_start_array(len) } fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_start_dictionary(len) } fn write_end_collection(&mut self) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_end_collection() } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_boolean(value) } fn write_data(&mut self, value: Cow<[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<'a, T: Into>>(&mut self, value: T) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_string(value.into()) } fn write_uid(&mut self, value: Uid) -> Result<(), Error> { self.maybe_write_pending_struct_field_name()?; self.writer.write_uid(value) } } impl<'a, W: Writer> ser::Serializer for &'a mut Serializer { type Ok = (); type Error = Error; type SerializeSeq = Compound<'a, W>; type SerializeTuple = Compound<'a, W>; type SerializeTupleStruct = Compound<'a, W>; type SerializeTupleVariant = Compound<'a, W>; type SerializeMap = Compound<'a, W>; type SerializeStruct = Compound<'a, W>; type SerializeStructVariant = Compound<'a, W>; fn serialize_bool(self, v: bool) -> Result<(), Self::Error> { self.write_boolean(v) } fn serialize_i8(self, v: i8) -> Result<(), Error> { self.serialize_i64(v.into()) } fn serialize_i16(self, v: i16) -> Result<(), Error> { self.serialize_i64(v.into()) } fn serialize_i32(self, v: i32) -> Result<(), Error> { self.serialize_i64(v.into()) } fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { self.write_integer(v.into()) } fn serialize_u8(self, v: u8) -> Result<(), Error> { self.serialize_u64(v.into()) } fn serialize_u16(self, v: u16) -> Result<(), Error> { self.serialize_u64(v.into()) } fn serialize_u32(self, v: u32) -> Result<(), Error> { self.serialize_u64(v.into()) } fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { self.write_integer(v.into()) } fn serialize_f32(self, v: f32) -> Result<(), Error> { self.serialize_f64(v.into()) } fn serialize_f64(self, v: f64) -> Result<(), Error> { self.write_real(v) } fn serialize_char(self, v: char) -> Result<(), Self::Error> { let mut buf = [0; 4]; let v = v.encode_utf8(&mut buf); self.write_string(&*v) } fn serialize_str(self, v: &str) -> Result<(), Error> { self.write_string(v) } fn serialize_bytes(self, v: &[u8]) -> Result<(), Error> { self.write_data(Cow::Borrowed(v)) } fn serialize_none(self) -> Result<(), Error> { match self.option_mode { OptionMode::Root | OptionMode::StructField(_) => (), OptionMode::StructFieldNameWritten => unreachable!(), OptionMode::Explicit => { self.write_start_dictionary(Some(1))?; self.write_string("None")?; self.serialize_unit()?; self.write_end_collection()?; } } Ok(()) } fn serialize_some(self, value: &T) -> Result<(), Error> { match self.option_mode { OptionMode::Root => self.serialize_with_option_mode(OptionMode::Explicit, value)?, OptionMode::StructField(field_name) => { self.option_mode = OptionMode::StructFieldNameWritten; self.write_string(field_name)?; self.serialize_with_option_mode(OptionMode::Explicit, value)?; } OptionMode::StructFieldNameWritten => unreachable!(), OptionMode::Explicit => { self.write_start_dictionary(Some(1))?; self.write_string("Some")?; value.serialize(&mut *self)?; self.write_end_collection()?; } } Ok(()) } fn serialize_unit(self) -> Result<(), Error> { self.write_string("") } fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> { self.serialize_unit() } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result<(), Error> { // `plist` since v1.1 serialises unit enum variants as plain strings. self.write_string(variant) } fn serialize_newtype_struct( self, name: &'static str, value: &T, ) -> Result<(), Error> { match name { DATE_NEWTYPE_STRUCT_NAME => value.serialize(DateSerializer { ser: &mut *self }), UID_NEWTYPE_STRUCT_NAME => value.serialize(UidSerializer { ser: &mut *self }), _ => value.serialize(self), } } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result<(), Error> { self.write_start_dictionary(Some(1))?; self.write_string(variant)?; value.serialize(&mut *self)?; self.write_end_collection() } fn serialize_seq(self, len: Option) -> Result { let len = len.map(|len| len as u64); self.write_start_array(len)?; Ok(Compound { ser: self }) } fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } fn serialize_tuple_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_tuple(len) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.write_start_dictionary(Some(1))?; self.write_string(variant)?; self.serialize_tuple(len) } fn serialize_map(self, len: Option) -> Result { let len = len.map(|len| len as u64); self.write_start_dictionary(len)?; Ok(Compound { ser: self }) } fn serialize_struct( self, _name: &'static str, _len: usize, ) -> Result { // The number of struct fields is not known as fields with None values are ignored. self.serialize_map(None) } fn serialize_struct_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.write_start_dictionary(Some(1))?; self.write_string(variant)?; self.serialize_struct(name, len) } } struct DateSerializer<'a, W: 'a + Writer> { ser: &'a mut Serializer, } impl<'a, W: Writer> DateSerializer<'a, W> { fn expecting_date_error(&self) -> Error { ser::Error::custom("plist date string expected") } } impl<'a, W: Writer> ser::Serializer for DateSerializer<'a, W> { type Ok = (); type Error = Error; type SerializeSeq = ser::Impossible<(), Error>; type SerializeTuple = ser::Impossible<(), Error>; type SerializeTupleStruct = ser::Impossible<(), Error>; type SerializeTupleVariant = ser::Impossible<(), Error>; type SerializeMap = ser::Impossible<(), Error>; type SerializeStruct = ser::Impossible<(), Error>; type SerializeStructVariant = ser::Impossible<(), Error>; fn serialize_bool(self, _: bool) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_i8(self, _: i8) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_i16(self, _: i16) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_i32(self, _: i32) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_i64(self, _: i64) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_u8(self, _: u8) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_u16(self, _: u16) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_u32(self, _: u32) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_u64(self, _: u64) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_f32(self, _: f32) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_f64(self, _: f64) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_char(self, _: char) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_str(self, v: &str) -> Result<(), Error> { let date = Date::from_xml_format(v).map_err(|_| self.expecting_date_error())?; self.ser.write_date(date) } fn serialize_bytes(self, _: &[u8]) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_none(self) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_some(self, _: &T) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_unit(self) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_unit_struct(self, _: &'static str) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_newtype_struct( self, _: &'static str, _: &T, ) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_newtype_variant( self, _: &'static str, _: u32, _: &'static str, _: &T, ) -> Result<(), Error> { Err(self.expecting_date_error()) } fn serialize_seq(self, _: Option) -> Result { Err(self.expecting_date_error()) } fn serialize_tuple(self, _: usize) -> Result { Err(self.expecting_date_error()) } fn serialize_tuple_struct( self, _: &'static str, _: usize, ) -> Result { Err(self.expecting_date_error()) } fn serialize_tuple_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(self.expecting_date_error()) } fn serialize_map(self, _: Option) -> Result { Err(self.expecting_date_error()) } fn serialize_struct(self, _: &'static str, _: usize) -> Result { Err(self.expecting_date_error()) } fn serialize_struct_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(self.expecting_date_error()) } } struct UidSerializer<'a, W: 'a + Writer> { ser: &'a mut Serializer, } impl<'a, W: Writer> UidSerializer<'a, W> { fn expecting_uid_error(&self) -> Error { ser::Error::custom("plist uid expected") } } impl<'a, W: Writer> ser::Serializer for UidSerializer<'a, W> { type Ok = (); type Error = Error; type SerializeSeq = ser::Impossible<(), Error>; type SerializeTuple = ser::Impossible<(), Error>; type SerializeTupleStruct = ser::Impossible<(), Error>; type SerializeTupleVariant = ser::Impossible<(), Error>; type SerializeMap = ser::Impossible<(), Error>; type SerializeStruct = ser::Impossible<(), Error>; type SerializeStructVariant = ser::Impossible<(), Error>; fn serialize_bool(self, _: bool) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_i8(self, _: i8) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_i16(self, _: i16) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_i32(self, _: i32) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_i64(self, _: i64) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_u8(self, _: u8) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_u16(self, _: u16) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_u32(self, _: u32) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_u64(self, v: u64) -> Result<(), Error> { self.ser.write_uid(Uid::new(v)) } fn serialize_f32(self, _: f32) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_f64(self, _: f64) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_char(self, _: char) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_str(self, _: &str) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_bytes(self, _: &[u8]) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_none(self) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_some(self, _: &T) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_unit(self) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_unit_struct(self, _: &'static str) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_newtype_struct( self, _: &'static str, _: &T, ) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_newtype_variant( self, _: &'static str, _: u32, _: &'static str, _: &T, ) -> Result<(), Error> { Err(self.expecting_uid_error()) } fn serialize_seq(self, _: Option) -> Result { Err(self.expecting_uid_error()) } fn serialize_tuple(self, _: usize) -> Result { Err(self.expecting_uid_error()) } fn serialize_tuple_struct( self, _: &'static str, _: usize, ) -> Result { Err(self.expecting_uid_error()) } fn serialize_tuple_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(self.expecting_uid_error()) } fn serialize_map(self, _: Option) -> Result { Err(self.expecting_uid_error()) } fn serialize_struct(self, _: &'static str, _: usize) -> Result { Err(self.expecting_uid_error()) } fn serialize_struct_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Err(self.expecting_uid_error()) } } #[doc(hidden)] pub struct Compound<'a, W: 'a + Writer> { ser: &'a mut Serializer, } impl<'a, W: Writer> ser::SerializeSeq for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeTuple for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result<(), Error> { self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeTupleStruct for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result<(), Error> { self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeTupleVariant for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { self.ser.write_end_collection()?; self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeMap for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_key(&mut self, key: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, key) } fn serialize_value(&mut self, value: &T) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeStruct for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field( &mut self, key: &'static str, value: &T, ) -> Result<(), Error> { // We don't want to serialize None if the Option is a struct field as this is how null // fields are represented in plists. self.ser .serialize_with_option_mode(OptionMode::StructField(key), value) } fn end(self) -> Result<(), Error> { self.ser.write_end_collection() } } impl<'a, W: Writer> ser::SerializeStructVariant for Compound<'a, W> { type Ok = (); type Error = Error; fn serialize_field( &mut self, key: &'static str, value: &T, ) -> Result<(), Error> { self.ser .serialize_with_option_mode(OptionMode::StructField(key), value) } fn end(self) -> Result<(), Error> { self.ser.write_end_collection()?; self.ser.write_end_collection() } } /// Serializes the given data structure to a file as a binary encoded plist. pub fn to_file_binary, T: ser::Serialize>(path: P, value: &T) -> Result<(), Error> { let mut file = File::create(path).map_err(error::from_io_without_position)?; to_writer_binary(BufWriter::new(&mut file), value)?; file.sync_all().map_err(error::from_io_without_position)?; Ok(()) } /// Serializes the given data structure to a file as an XML encoded plist. pub fn to_file_xml, T: ser::Serialize>(path: P, value: &T) -> Result<(), Error> { let mut file = File::create(path).map_err(error::from_io_without_position)?; to_writer_xml(BufWriter::new(&mut file), value)?; file.sync_all().map_err(error::from_io_without_position)?; Ok(()) } /// Serializes the given data structure to a byte stream as a binary encoded plist. pub fn to_writer_binary(writer: W, value: &T) -> Result<(), Error> { let writer = stream::BinaryWriter::new(writer); let mut ser = Serializer::new(writer); value.serialize(&mut ser) } /// Serializes the given data structure to a byte stream as an XML encoded plist. pub fn to_writer_xml(writer: W, value: &T) -> Result<(), Error> { 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( 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) } /// Converts a `T` into a [`Value`] which can represent any valid plist. pub fn to_value(value: &T) -> Result { let writer = crate::value::Builder::default(); let mut ser = Serializer::new(writer); value.serialize(&mut ser)?; ser.into_inner().finish() } plist-1.6.0/src/serde_tests.rs000064400000000000000000000635031046102023000144530ustar 00000000000000use serde::{ de::{Deserialize, DeserializeOwned}, ser::Serialize, }; use std::{borrow::Cow, collections::BTreeMap, fmt::Debug, fs::File, io::Cursor}; use crate::{ from_value, stream::{private::Sealed, Event, OwnedEvent, Writer}, to_value, Data, Date, Deserializer, Dictionary, Error, Integer, Serializer, Uid, Value, }; struct VecWriter { events: Vec, } impl VecWriter { pub fn new() -> VecWriter { VecWriter { events: Vec::new() } } pub fn into_inner(self) -> Vec { self.events } } impl Writer for VecWriter { fn write_start_array(&mut self, len: Option) -> Result<(), Error> { self.events.push(Event::StartArray(len)); Ok(()) } fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error> { self.events.push(Event::StartDictionary(len)); Ok(()) } fn write_end_collection(&mut self) -> Result<(), Error> { self.events.push(Event::EndCollection); Ok(()) } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.events.push(Event::Boolean(value)); Ok(()) } fn write_data(&mut self, value: Cow<[u8]>) -> Result<(), Error> { self.events .push(Event::Data(Cow::Owned(value.into_owned()))); Ok(()) } fn write_date(&mut self, value: Date) -> Result<(), Error> { self.events.push(Event::Date(value)); Ok(()) } fn write_integer(&mut self, value: Integer) -> Result<(), Error> { self.events.push(Event::Integer(value)); Ok(()) } fn write_real(&mut self, value: f64) -> Result<(), Error> { self.events.push(Event::Real(value)); Ok(()) } fn write_string(&mut self, value: Cow) -> Result<(), Error> { self.events .push(Event::String(Cow::Owned(value.into_owned()))); Ok(()) } fn write_uid(&mut self, value: Uid) -> Result<(), Error> { self.events.push(Event::Uid(value)); Ok(()) } } impl Sealed for VecWriter {} fn new_serializer() -> Serializer { Serializer::new(VecWriter::new()) } fn new_deserializer<'event>( events: Vec>, ) -> Deserializer<'event, Vec, Error>>> { let result_events = events.into_iter().map(Ok).collect(); Deserializer::new(result_events) } fn assert_roundtrip(obj: T, expected_events: &[Event], roundtrip_value: bool) where T: Debug + DeserializeOwned + PartialEq + Serialize, { let mut se = new_serializer(); obj.serialize(&mut se).unwrap(); let events = se.into_inner().into_inner(); let value = if roundtrip_value { to_value(&obj).expect("failed to convert object into value") } else { Value::Boolean(false) }; assert_eq!(&events[..], &expected_events[..]); if roundtrip_value { let expected_value = Value::from_events(expected_events.iter().cloned().map(Ok)) .expect("failed to convert expected events into value"); assert_eq!(value, expected_value); } let mut de = new_deserializer(events); let obj_events_roundtrip = T::deserialize(&mut de).unwrap(); assert_eq!(obj_events_roundtrip, obj); if roundtrip_value { let obj_value_roundtrip: T = from_value(&value).unwrap(); assert_eq!(obj_value_roundtrip, obj); } } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] enum Animal { Cow, Dog(DogOuter), Frog(Result, Option>), Cat { age: Integer, name: String, firmware: Option>, }, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct DogOuter { inner: Vec, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct DogInner { a: (), b: usize, c: Vec, d: Option, e: Data, } #[test] fn cow() { let cow = Animal::Cow; let comparison = &[Event::String("Cow".into())]; assert_roundtrip(cow, comparison, true); } #[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)), e: Data::new(vec![20, 22]), }], }); 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::String("e".into()), Event::Data(vec![20, 22].into()), Event::EndCollection, Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(dog, comparison, true); } #[test] fn frog() { let frog = Animal::Frog( Ok("hello".to_owned()), Some(vec![1.0, 2.0, std::f64::consts::PI, 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(std::f64::consts::PI), Event::Real(0.000000001), Event::Real(1.27e31), Event::EndCollection, Event::EndCollection, Event::EndCollection, Event::EndCollection, ]; assert_roundtrip(frog, comparison, true); } #[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, comparison, true); } #[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, comparison, true); } #[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, comparison, true); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct TypeWithOptions { a: Option, b: Option>, c: Option>, } #[test] fn type_with_options() { let inner = TypeWithOptions { a: None, b: Some(Some(12)), c: None, }; let obj = TypeWithOptions { a: Some("hello".to_owned()), b: Some(None), c: Some(Box::new(inner)), }; let comparison = &[ Event::StartDictionary(None), Event::String("a".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, comparison, true); } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct TypeWithDate { a: Option, b: Option, } #[test] fn type_with_date() { let date = Date::from_xml_format("1920-01-01T00:10:00Z").unwrap(); let obj = TypeWithDate { a: Some(28), b: Some(date), }; 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, comparison, true); } #[test] fn option_some() { let obj = Some(12); let comparison = &[Event::Integer(12.into())]; assert_roundtrip(obj, comparison, true); } #[test] fn option_none() { let obj: Option = None; let comparison = &[]; // Written as nothing so can't be represented by a `Value`. assert_roundtrip(obj, comparison, false); } #[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, comparison, true); } #[test] fn option_some_none() { let obj: Option> = Some(None); let comparison = &[ Event::StartDictionary(Some(1)), Event::String("None".into()), Event::String("".into()), Event::EndCollection, ]; assert_roundtrip(obj, comparison, true); } #[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, comparison, true); } #[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, ]; // This example uses non-string dictionary keys which can only be represented by binary plists // and not by a `Value`. assert_roundtrip(obj, comparison, false); } #[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, comparison, true); } #[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, expected, true); let expected = &[ Event::StartDictionary(Some(1)), Event::String("Newtype".into()), Event::Integer(42.into()), Event::EndCollection, ]; assert_roundtrip(Foo::Newtype(42), expected, true); 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()), expected, true); 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(), }, expected, true, ); } #[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("./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("./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!(dict.get("IsTrue").unwrap().as_boolean().unwrap()); assert!(!dict.get("IsNotFalse").unwrap().as_boolean().unwrap()); // 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_xml_format("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("./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 #[derive(Deserialize)] struct LayerinfoData { color: Option, lib: Option, } let lib_dict: LayerinfoData = crate::from_bytes(r#" color 1,0.75,0,0.7 lib com.typemytype.robofont.segmentType curve "#.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_xml_format("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 = " \tAnArray \t \t\tHello, world! \t\t345 \t \tADictionary \t \t\tFirstKey \t\tFirstValue \t\tSecondKey \t\t \t\tChQeKA== \t\t \t\tThirdKey \t\t1.234 \t\tFourthKey \t\t1981-05-16T11:32:06Z \t \tAnInteger \t123 \tATrueBoolean \t \tAFalseBoolean \t "; assert_eq!(xml, comparison); } #[test] fn empty_array_and_dictionary_serialize_to_xml() { #[derive(Serialize, Default)] struct Empty { vec: Vec, map: BTreeMap, } // Serialize dictionary as an XML plist. let mut buf = Cursor::new(Vec::new()); crate::to_writer_xml(&mut buf, &Empty::default()).unwrap(); let buf = buf.into_inner(); let xml = std::str::from_utf8(&buf).unwrap(); let comparison = " \tvec \t \tmap \t "; 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)); } #[test] fn serialize_to_from_value() { let dog = Animal::Dog(DogOuter { inner: vec![DogInner { a: (), b: 12, c: vec!["a".to_string(), "b".to_string()], d: Some(Uid::new(42)), e: Data::new(vec![1, 2, 3]), }], }); let dog_value = to_value(&dog).unwrap(); assert_eq!( dog_value .as_dictionary() .unwrap() .get("Dog") .unwrap() .as_dictionary() .unwrap() .get("inner") .unwrap() .as_array() .unwrap()[0] .as_dictionary() .unwrap()["b"] .as_unsigned_integer() .unwrap(), 12 ); let dog_roundtrip: Animal = from_value(&dog_value).unwrap(); assert_eq!(dog_roundtrip, dog); } plist-1.6.0/src/stream/binary_reader.rs000064400000000000000000000422251046102023000162260ustar 00000000000000use 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, ty: StackType, } enum StackType { Array, Dict, } // https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c // https://hg.python.org/cpython/file/3.4/Lib/plistlib.py pub struct BinaryReader { stack: Vec, object_offsets: Vec, object_on_stack: Vec, reader: PosReader, ref_size: u8, root_object: u64, trailer_start_offset: u64, } struct PosReader { reader: R, pos: u64, } impl PosReader { fn read_all(&mut self, buf: &mut [u8]) -> Result<(), Error> { self.read_exact(buf) .map_err(|err| ErrorKind::Io(err).with_byte_offset(self.pos))?; Ok(()) } fn seek(&mut self, pos: SeekFrom) -> Result { self.pos = self .reader .seek(pos) .map_err(|err| ErrorKind::Io(err).with_byte_offset(self.pos))?; Ok(self.pos) } } impl Read for PosReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { let count = self.reader.read(buf)?; self.pos .checked_add(count as u64) .expect("file cannot be larger than `u64::max_value()` bytes"); Ok(count) } } impl BinaryReader { pub fn new(reader: R) -> BinaryReader { BinaryReader { stack: Vec::new(), object_offsets: Vec::new(), object_on_stack: Vec::new(), reader: PosReader { reader, pos: 0 }, ref_size: 0, root_object: 0, trailer_start_offset: 0, } } fn allocate_vec(&self, len: u64, size: usize) -> Result, Error> { // Check we are not reading past the start of the plist trailer let inner = |len: u64, size: usize| { let byte_len = len.checked_mul(size as u64)?; let end_offset = self.reader.pos.checked_add(byte_len)?; if end_offset <= self.trailer_start_offset { Some(()) } else { None } }; inner(len, size).ok_or_else(|| self.with_pos(ErrorKind::ObjectOffsetTooLarge))?; Ok(Vec::with_capacity(len as usize)) } fn read_trailer(&mut self) -> Result<(), Error> { self.reader.seek(SeekFrom::Start(0))?; let mut magic = [0; 8]; self.reader.read_all(&mut magic)?; if &magic != b"bplist00" { return Err(self.with_pos(ErrorKind::InvalidMagic)); } self.trailer_start_offset = self.reader.seek(SeekFrom::End(-32))?; // Trailer starts with 6 bytes of padding let mut zeros = [0; 6]; self.reader.read_all(&mut zeros)?; let offset_size = self.read_u8()?; match offset_size { 1 | 2 | 4 | 8 => (), _ => return Err(self.with_pos(ErrorKind::InvalidTrailerObjectOffsetSize)), } self.ref_size = self.read_u8()?; match self.ref_size { 1 | 2 | 4 | 8 => (), _ => return Err(self.with_pos(ErrorKind::InvalidTrailerObjectReferenceSize)), } let num_objects = self.read_be_u64()?; self.root_object = self.read_be_u64()?; let offset_table_offset = self.read_be_u64()?; // Read offset table self.reader.seek(SeekFrom::Start(offset_table_offset))?; self.object_offsets = self.read_ints(num_objects, offset_size)?; self.object_on_stack = vec![false; self.object_offsets.len()]; Ok(()) } /// Reads a list of `len` big-endian integers of `size` bytes from the reader. fn read_ints(&mut self, len: u64, size: u8) -> Result, Error> { let mut ints = self.allocate_vec(len, size as usize)?; for _ in 0..len { match size { 1 => ints.push(self.read_u8()?.into()), 2 => ints.push(self.read_be_u16()?.into()), 4 => ints.push(self.read_be_u32()?.into()), 8 => ints.push(self.read_be_u64()?), _ => unreachable!("size is either self.ref_size or offset_size both of which are already validated") } } Ok(ints) } /// Reads a list of `len` offsets into the object table from the reader. fn read_refs(&mut self, len: u64) -> Result, Error> { let ref_size = self.ref_size; self.read_ints(len, ref_size) } /// Reads a compressed value length from the reader. `len` must contain the low 4 bits of the /// object token. fn read_object_len(&mut self, len: u8) -> Result { if (len & 0x0f) == 0x0f { let len_power_of_two = self.read_u8()? & 0x03; Ok(match len_power_of_two { 0 => self.read_u8()?.into(), 1 => self.read_be_u16()?.into(), 2 => self.read_be_u32()?.into(), 3 => self.read_be_u64()?, _ => return Err(self.with_pos(ErrorKind::InvalidObjectLength)), }) } else { Ok(len.into()) } } /// Reads `len` bytes from the reader. fn read_data(&mut self, len: u64) -> Result, Error> { let mut data = self.allocate_vec(len, size_of::())?; data.resize(len as usize, 0); self.reader.read_all(&mut data)?; Ok(data) } fn seek_to_object(&mut self, object_ref: u64) -> Result { let object_ref = u64_to_usize(object_ref) .ok_or_else(|| self.with_pos(ErrorKind::ObjectReferenceTooLarge))?; let offset = *self .object_offsets .get(object_ref) .ok_or_else(|| self.with_pos(ErrorKind::ObjectReferenceTooLarge))?; if offset >= self.trailer_start_offset { return Err(self.with_pos(ErrorKind::ObjectOffsetTooLarge)); } self.reader.seek(SeekFrom::Start(offset)) } fn push_stack_item_and_check_for_recursion(&mut self, item: StackItem) -> Result<(), Error> { let object_ref = u64_to_usize(item.object_ref).expect("internal consistency error"); let is_on_stack = &mut self.object_on_stack[object_ref]; if *is_on_stack { return Err(self.with_pos(ErrorKind::RecursiveObject)); } *is_on_stack = true; self.stack.push(item); Ok(()) } fn pop_stack_item(&mut self) -> StackItem { let item = self.stack.pop().expect("internal consistency error"); let object_ref = u64_to_usize(item.object_ref).expect("internal consistency error"); self.object_on_stack[object_ref] = false; item } fn read_next(&mut self) -> Result, Error> { let object_ref = if self.ref_size == 0 { // Initialise here rather than in new self.read_trailer()?; self.root_object } else { let maybe_object_ref = if let Some(stack_item) = self.stack.last_mut() { stack_item.child_object_refs.pop() } else { // Finished reading the plist return Ok(None); }; if let Some(object_ref) = maybe_object_ref { object_ref } else { // We're at the end of an array or dict. Pop the top stack item and return. let stack_item = self.pop_stack_item(); match stack_item.ty { StackType::Array | StackType::Dict => return Ok(Some(Event::EndCollection)), } } }; self.seek_to_object(object_ref)?; let token = self.read_u8()?; let ty = (token & 0xf0) >> 4; let size = token & 0x0f; let result = match (ty, size) { (0x0, 0x00) => return Err(self.with_pos(ErrorKind::NullObjectUnimplemented)), (0x0, 0x08) => Some(Event::Boolean(false)), (0x0, 0x09) => Some(Event::Boolean(true)), (0x0, 0x0f) => return Err(self.with_pos(ErrorKind::FillObjectUnimplemented)), (0x1, 0) => Some(Event::Integer(self.read_u8()?.into())), (0x1, 1) => Some(Event::Integer(self.read_be_u16()?.into())), (0x1, 2) => Some(Event::Integer(self.read_be_u32()?.into())), (0x1, 3) => Some(Event::Integer(self.read_be_i64()?.into())), (0x1, 4) => { let value = self.read_be_i128()?; if value < 0 || value > u64::max_value().into() { return Err(self.with_pos(ErrorKind::IntegerOutOfRange)); } Some(Event::Integer((value as u64).into())) } (0x1, _) => return Err(self.with_pos(ErrorKind::UnknownObjectType(token))), // variable length int (0x2, 2) => Some(Event::Real(f32::from_bits(self.read_be_u32()?).into())), (0x2, 3) => Some(Event::Real(f64::from_bits(self.read_be_u64()?))), (0x2, _) => return Err(self.with_pos(ErrorKind::UnknownObjectType(token))), // odd length float (0x3, 3) => { // Date. Seconds since 1/1/2001 00:00:00. let secs = f64::from_bits(self.read_be_u64()?); let date = Date::from_seconds_since_plist_epoch(secs) .map_err(|InfiniteOrNanDate| self.with_pos(ErrorKind::InfiniteOrNanDate))?; Some(Event::Date(date)) } (0x4, n) => { // Data let len = self.read_object_len(n)?; Some(Event::Data(self.read_data(len)?.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::())?; 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 { let mut buf = [0; 1]; self.reader.read_all(&mut buf)?; Ok(buf[0]) } fn read_be_u16(&mut self) -> Result { let mut buf = [0; 2]; self.reader.read_all(&mut buf)?; Ok(u16::from_be_bytes(buf)) } fn read_be_u32(&mut self) -> Result { let mut buf = [0; 4]; self.reader.read_all(&mut buf)?; Ok(u32::from_be_bytes(buf)) } fn read_be_u64(&mut self) -> Result { let mut buf = [0; 8]; self.reader.read_all(&mut buf)?; Ok(u64::from_be_bytes(buf)) } fn read_be_i64(&mut self) -> Result { let mut buf = [0; 8]; self.reader.read_all(&mut buf)?; Ok(i64::from_be_bytes(buf)) } fn read_be_i128(&mut self) -> Result { let mut buf = [0; 16]; self.reader.read_all(&mut buf)?; Ok(i128::from_be_bytes(buf)) } fn with_pos(&self, kind: ErrorKind) -> Error { kind.with_byte_offset(self.reader.pos) } } impl Iterator for BinaryReader { type Item = Result; fn next(&mut self) -> Option> { match self.read_next() { Ok(Some(event)) => Some(Ok(event)), Err(err) => { // Mark the plist as finished self.stack.clear(); Some(Err(err)) } Ok(None) => None, } } } #[cfg(test)] mod tests { use std::fs::File; use super::*; use crate::{stream::Event, Uid}; #[test] fn streaming_parser() { use crate::stream::Event::*; let reader = File::open("./tests/data/binary.plist").unwrap(); let streaming_parser = BinaryReader::new(reader); let events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); let comparison = &[ StartDictionary(Some(13)), String("Author".into()), String("William Shakespeare".into()), String("Birthdate".into()), Date(super::Date::from_xml_format("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("./tests/data/utf16_bplist.plist").unwrap(); let streaming_parser = BinaryReader::new(reader); let mut events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); assert_eq!(events[2], Event::String("\u{2605} or better".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("./tests/data/binary_NSKeyedArchiver.plist").unwrap(); let streaming_parser = BinaryReader::new(reader); let events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); assert_eq!(events[10], Event::Uid(Uid::new(4))); assert_eq!(events[12], Event::Uid(Uid::new(2))); assert_eq!(events[18], Event::Uid(Uid::new(3))); assert_eq!(events[46], Event::Uid(Uid::new(1))); } } plist-1.6.0/src/stream/binary_writer.rs000064400000000000000000000651741046102023000163100ustar 00000000000000// 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}, num::NonZeroUsize, }; use crate::{ error::{self, Error, ErrorKind, EventKind}, stream::Writer, Date, Integer, Uid, }; pub struct BinaryWriter { writer: PosWriter, events: Vec, dictionary_key_events: Vec, values: IndexMap, ValueState>, /// Pointers into `events` for each of the currently unclosed `Collection` events. collection_stack: Vec, /// The number of `Collection` and unique `Value` events in `events`. num_objects: usize, } struct PosWriter { writer: W, pos: usize, } #[derive(Clone)] struct ObjectRef(NonZeroUsize); /// An array of `len` elements is stored as a `Collection` event followed by `skip_len` events /// containing the contents of the array. e.g. /// /// Collection(ty: Array, len: 2, skip_len: 2) /// Value /// Value /// /// If the array contains another array or dictionary `len` and `skip_len` will differ. e.g. /// /// Collection(ty: Array, len: 2, skip_len: 3) /// Value /// Collection(ty: Array, len: 1, skip_len: 1) /// Value /// /// A dictionary of `len` (key, value) pairs is stored as a `Collection` event followed by /// `skip_len` events containing the contents of the dictionary. The dictionary values are stored /// first. These are followed by a `DictionaryKeys` event and then the keys themselves. e.g. /// /// Collection(ty: Dictionary, len: 2, skip_len: 6) /// Value /// Collection(ty: Array, len: 1, skip_len: 1) /// Value /// DictionaryKeys(2) /// Value (Key) /// Value (Key) /// /// This arrangement simplifies writing dictionaries as they must be written in the order /// (key, key, value, value) instead of (key, value, key, value) as they are passed to the writer. /// Unclosed dictionaries have their keys stored in `dictionary_key_events` and these are only /// moved to the end of the `BinaryWriter::events` array once the dictionary is closed in /// `write_end_collection`. enum Event { Collection(Collection), /// Index of the value in the `values` map. Value(usize), /// The number of dictionary keys following this event. DictionaryKeys(usize), } struct Collection { ty: CollectionType, /// The number of elements in an array or (key, value) pairs in a dictionary. /// Unclosed dictionaries have a `len` equal to the number of keys plus the number of values /// written so far. This is fixed up in `write_end_collection`. len: usize, /// The number of events to skip to get to the next element after the collection. skip: usize, object_ref: Option, } #[derive(Eq, PartialEq)] enum CollectionType { Array, Dictionary, } #[derive(Eq, Hash, PartialEq)] enum Value<'a> { Boolean(bool), Data(Cow<'a, [u8]>), Date(Date), Integer(Integer), /// Floats are deduplicated based on their bitwise value. Real(u64), String(Cow<'a, str>), Uid(Uid), } enum ValueState { /// The value has not been assigned an object reference. Unassigned, /// The value has been assigned an object reference but has not yet been written. Unwritten(ObjectRef), /// The value has been written with the given object reference. Written(ObjectRef), } impl BinaryWriter { pub fn new(writer: W) -> BinaryWriter { BinaryWriter { writer: PosWriter { writer, pos: 0 }, events: Vec::new(), dictionary_key_events: Vec::new(), values: IndexMap::new(), collection_stack: Vec::new(), num_objects: 0, } } fn write_start_collection(&mut self, ty: CollectionType) -> Result<(), Error> { if self.expecting_dictionary_key() { let ty_event_kind = match ty { CollectionType::Array => EventKind::StartArray, CollectionType::Dictionary => EventKind::StartDictionary, }; return Err(ErrorKind::UnexpectedEventType { expected: EventKind::DictionaryKeyOrEndCollection, found: ty_event_kind, } .without_position()); } self.increment_current_collection_len(); self.collection_stack.push(self.events.len()); self.events.push(Event::Collection(Collection { ty, len: 0, skip: 0, object_ref: None, })); self.num_objects += 1; Ok(()) } fn write_end_collection(&mut self) -> Result<(), Error> { let collection_event_index = self.collection_stack.pop().ok_or_else(|| { ErrorKind::UnexpectedEventType { expected: EventKind::ValueOrStartCollection, found: EventKind::EndCollection, } .without_position() })?; let current_event_index = self.events.len() - 1; let c = if let Event::Collection(c) = &mut self.events[collection_event_index] { c } else { unreachable!("items in `collection_stack` always point to a collection event"); }; c.skip = current_event_index - collection_event_index; if let CollectionType::Dictionary = c.ty { // Ensure that every dictionary key is paired with a value. if !is_even(c.len) { return Err(ErrorKind::UnexpectedEventType { expected: EventKind::DictionaryKeyOrEndCollection, found: EventKind::EndCollection, } .without_position()); } // Fix up the dictionary length. It should contain the number of key-value pairs, // not the number of keys and values. c.len /= 2; // To skip past a dictionary we also need to skip the `DictionaryKeys` event and the // keys that follow it. c.skip += 1 + c.len; let len = c.len; self.events.push(Event::DictionaryKeys(len)); // Move the cached dictionary keys to the end of the events array. let keys_start_index = self.dictionary_key_events.len() - len; self.events.extend( self.dictionary_key_events .drain(keys_start_index..) .map(Event::Value), ); } if self.collection_stack.is_empty() { self.write_plist()?; } Ok(()) } fn write_value(&mut self, value: Value) -> Result<(), Error> { let expecting_dictionary_key = self.expecting_dictionary_key(); // Ensure that all dictionary keys are strings. match (&value, expecting_dictionary_key) { (Value::String(_), true) | (_, false) => (), (_, true) => { return Err(ErrorKind::UnexpectedEventType { expected: EventKind::DictionaryKeyOrEndCollection, found: value.event_kind(), } .without_position()) } } // Deduplicate `value`. There is one entry in `values` for each unqiue `Value` in the // plist. let value_index = if let Some((value_index, _, _)) = self.values.get_full(&value) { value_index } else { self.num_objects += 1; let value = value.into_owned(); let (value_index, _) = self.values.insert_full(value, ValueState::Unassigned); value_index }; // Dictionary keys are buffered in `dictionary_key_events` until the dictionary is closed // in `write_end_collection` when they are moved to the end of the `events` array. if expecting_dictionary_key { self.dictionary_key_events.push(value_index); } else { self.events.push(Event::Value(value_index)); } self.increment_current_collection_len(); if self.collection_stack.is_empty() { self.write_plist()?; } Ok(()) } fn expecting_dictionary_key(&self) -> bool { if let Some(&event_index) = self.collection_stack.last() { if let Event::Collection(c) = &self.events[event_index] { c.ty == CollectionType::Dictionary && is_even(c.len) } else { unreachable!("items in `collection_stack` always point to a collection event"); } } else { false } } fn increment_current_collection_len(&mut self) { if let Some(&event_index) = self.collection_stack.last() { if let Event::Collection(c) = &mut self.events[event_index] { c.len += 1; } else { unreachable!("items in `collection_stack` always point to a collection event"); } } } fn write_plist(&mut self) -> Result<(), Error> { assert!(self.collection_stack.is_empty()); // Write header self.writer.write_exact(b"bplist00")?; // Write objects let mut events_vec = std::mem::take(&mut self.events); 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 [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 [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.as_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]; // we want to be explicit about the type here #[allow(clippy::unnecessary_cast)] buf[1..].copy_from_slice(&(v as u64).to_be_bytes()); self.writer.write_exact(&buf)?; } } } Ok(()) } } impl Writer for BinaryWriter { fn write_start_array(&mut self, _len: Option) -> Result<(), Error> { self.write_start_collection(CollectionType::Array) } fn write_start_dictionary(&mut self, _len: Option) -> Result<(), Error> { self.write_start_collection(CollectionType::Dictionary) } fn write_end_collection(&mut self) -> Result<(), Error> { self.write_end_collection() } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.write_value(Value::Boolean(value)) } fn write_data(&mut self, value: Cow<[u8]>) -> Result<(), Error> { self.write_value(Value::Data(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: Cow) -> Result<(), Error> { self.write_value(Value::String(value)) } fn write_uid(&mut self, value: Uid) -> Result<(), Error> { self.write_value(Value::Uid(value)) } } fn is_even(value: usize) -> bool { value & 1 == 0 } fn value_mut<'a>( values: &'a mut IndexMap, ValueState>, value_index: usize, ) -> (&'a Value<'static>, &'a mut ValueState) { values .get_index_mut(value_index) .expect("internal consistency error") } fn write_plist_value_ty_and_size( writer: &mut PosWriter, token: u8, size: usize, ) -> Result<(), Error> { if size < 0x0f { writer.write_exact(&[token | (size as u8)])?; } else if size <= u8::max_value() as usize { writer.write_exact(&[token | 0x0f, 0x10, size as u8])?; } else if size <= u16::max_value() as usize { let mut buf: [_; 4] = [token | 0x0f, 0x11, 0, 0]; buf[2..].copy_from_slice(&(size as u16).to_be_bytes()); writer.write_exact(&buf)?; } else if size <= u32::max_value() as usize { let mut buf: [_; 6] = [token | 0x0f, 0x12, 0, 0, 0, 0]; buf[2..].copy_from_slice(&(size as u32).to_be_bytes()); writer.write_exact(&buf)?; } else { let mut buf: [_; 10] = [token | 0x0f, 0x13, 0, 0, 0, 0, 0, 0, 0, 0]; buf[2..].copy_from_slice(&(size as u64).to_be_bytes()); writer.write_exact(&buf)?; } Ok(()) } fn plist_ref_size(max_value: usize) -> u8 { let significant_bits = 64 - (max_value as u64).leading_zeros() as u8; // Convert to number of bytes let significant_bytes = (significant_bits + 7) / 8; // Round up to the next integer byte size which must be power of two. significant_bytes.next_power_of_two() } fn write_plist_ref( writer: &mut PosWriter, ref_size: u8, value: usize, ) -> Result<(), Error> { match ref_size { 1 => writer.write_exact(&[value as u8]), 2 => writer.write_exact(&(value as u16).to_be_bytes()), 4 => writer.write_exact(&(value as u32).to_be_bytes()), 8 => writer.write_exact(&(value as u64).to_be_bytes()), _ => unreachable!("`ref_size` is a power of two less than or equal to 8"), } } impl PosWriter { fn write_exact(&mut self, buf: &[u8]) -> Result<(), Error> { self.write_all(buf) .map_err(error::from_io_without_position)?; Ok(()) } } impl Write for PosWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let count = self.writer.write(buf)?; self.pos = self .pos .checked_add(count) .expect("binary plist cannot be larger than `usize::max_value()` bytes"); Ok(count) } fn flush(&mut self) -> io::Result<()> { self.writer.flush() } } impl ObjectRef { fn zero() -> ObjectRef { ObjectRef(NonZeroUsize::new(1).unwrap()) } fn clone_and_increment_self(&mut self) -> ObjectRef { let current = self.0; self.0 = NonZeroUsize::new(current.get() + 1).unwrap(); ObjectRef(current) } fn value(&self) -> usize { self.0.get() - 1 } } impl<'a> Value<'a> { fn into_owned(self) -> Value<'static> { match self { Value::Boolean(v) => Value::Boolean(v), Value::Data(v) => Value::Data(Cow::Owned(v.into_owned())), Value::Date(v) => Value::Date(v), Value::Integer(v) => Value::Integer(v), Value::Real(v) => Value::Real(v), Value::String(v) => Value::String(Cow::Owned(v.into_owned())), Value::Uid(v) => Value::Uid(v), } } fn event_kind(&self) -> EventKind { match self { Value::Boolean(_) => EventKind::Boolean, Value::Data(_) => EventKind::Data, Value::Date(_) => EventKind::Date, Value::Integer(_) => EventKind::Integer, Value::Real(_) => EventKind::Real, Value::String(_) => EventKind::String, Value::Uid(_) => EventKind::Uid, } } } #[cfg(test)] mod tests { use std::{fs::File, io::Cursor, path::Path}; use crate::{stream::BinaryReader, Value}; fn test_roundtrip>(path: P) { let reader = File::open(path).unwrap(); let streaming_parser = BinaryReader::new(reader); let value_to_encode = Value::from_events(streaming_parser).unwrap(); let mut buf = Cursor::new(Vec::new()); value_to_encode.to_writer_binary(&mut buf).unwrap(); let buf_inner = buf.into_inner(); let streaming_parser = BinaryReader::new(Cursor::new(buf_inner)); let events: Vec> = streaming_parser.collect(); let value_decoded_from_encode = Value::from_events(events.into_iter()).unwrap(); assert_eq!(value_to_encode, value_decoded_from_encode); } #[test] fn bplist_roundtrip() { test_roundtrip("./tests/data/binary.plist") } #[test] fn utf16_roundtrip() { test_roundtrip("./tests/data/utf16_bplist.plist") } #[test] fn nskeyedarchiver_roundtrip() { test_roundtrip("./tests/data/binary_NSKeyedArchiver.plist") } } plist-1.6.0/src/stream/mod.rs000064400000000000000000000244121046102023000141750ustar 00000000000000//! 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; #[cfg(feature = "serde")] pub(crate) use xml_writer::base64_encode_plist; 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), StartDictionary(Option), 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>, } 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 { root_element: bool, indent_char: u8, indent_amount: usize, } 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`. /// /// Since replacing `xml-rs` with `quick-xml`, the indent string has to consist of a single /// repeating ascii character. This is a backwards compatibility function, prefer using /// [`XmlWriteOptions::indent`]. #[deprecated(since = "1.4.0", note = "please use `indent` instead")] pub fn indent_string(self, indent_str: impl Into>) -> Self { let indent_str = indent_str.into(); let indent_str = indent_str.as_ref(); if indent_str.is_empty() { return self.indent(0, 0); } assert!( indent_str.chars().all(|chr| chr.is_ascii()), "indent str must be ascii" ); let indent_str = indent_str.as_bytes(); assert!( indent_str.iter().all(|chr| chr == &indent_str[0]), "indent str must consist of a single repeating character" ); self.indent(indent_str[0], indent_str.len()) } /// Specifies the character and amount used for indentation. /// /// The default is indenting with a single tab. pub fn indent(mut self, indent_char: u8, indent_amount: usize) -> Self { self.indent_char = indent_char; self.indent_amount = indent_amount; self } /// Selects whether to write the XML prologue, plist document type and root element. /// /// In other words the following: /// ```xml /// /// /// /// ... /// /// ``` /// /// The default is `true`. pub fn root_element(mut self, write_root: bool) -> Self { self.root_element = write_root; self } } impl Default for XmlWriteOptions { fn default() -> Self { XmlWriteOptions { indent_char: b'\t', indent_amount: 1, root_element: true, } } } 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> { fn handle_value<'c, 'b: 'c>( value: &'b Value, stack: &'c mut Vec>, ) -> 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(ReaderInner); enum ReaderInner { Uninitialized(Option), Xml(XmlReader), Binary(BinaryReader), } impl Reader { pub fn new(reader: R) -> Reader { Reader(ReaderInner::Uninitialized(Some(reader))) } fn is_binary(reader: &mut R) -> Result { fn from_io_offset_0(err: io::Error) -> Error { ErrorKind::Io(err).with_byte_offset(0) } reader.seek(SeekFrom::Start(0)).map_err(from_io_offset_0)?; let mut magic = [0; 8]; reader.read_exact(&mut magic).map_err(from_io_offset_0)?; reader.seek(SeekFrom::Start(0)).map_err(from_io_offset_0)?; Ok(&magic == b"bplist00") } } impl Iterator for Reader { type Item = Result; fn next(&mut self) -> Option> { let mut reader = match self.0 { ReaderInner::Xml(ref mut parser) => return parser.next(), ReaderInner::Binary(ref mut parser) => return parser.next(), ReaderInner::Uninitialized(ref mut reader) => reader.take().unwrap(), }; 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) -> Result<(), Error>; fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error>; fn write_end_collection(&mut self) -> Result<(), Error>; fn write_boolean(&mut self, value: bool) -> Result<(), Error>; fn write_data(&mut self, value: Cow<[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: Cow) -> Result<(), Error>; fn write_uid(&mut self, value: Uid) -> Result<(), Error>; } pub(crate) mod private { use std::io::Write; pub trait Sealed {} impl Sealed for super::BinaryWriter {} impl Sealed for super::XmlWriter {} } plist-1.6.0/src/stream/xml_reader.rs000064400000000000000000000231121046102023000155340ustar 00000000000000use base64::{engine::general_purpose::STANDARD as base64_standard, Engine}; use quick_xml::{events::Event as XmlEvent, Error as XmlReaderError, Reader as EventReader}; use std::io::{self, BufReader, Read}; use crate::{ error::{Error, ErrorKind, FilePosition}, stream::{Event, OwnedEvent}, Date, Integer, }; #[derive(Clone, PartialEq, Eq)] struct ElmName(Box<[u8]>); impl From<&[u8]> for ElmName { fn from(bytes: &[u8]) -> Self { ElmName(Box::from(bytes)) } } impl AsRef<[u8]> for ElmName { fn as_ref(&self) -> &[u8] { &self.0 } } pub struct XmlReader { buffer: Vec, finished: bool, state: ReaderState, } struct ReaderState(EventReader>); impl XmlReader { pub fn new(reader: R) -> XmlReader { let mut xml_reader = EventReader::from_reader(BufReader::new(reader)); xml_reader.trim_text(false); xml_reader.check_end_names(true); xml_reader.expand_empty_elements(true); XmlReader { buffer: Vec::new(), finished: false, state: ReaderState(xml_reader), } } } impl From for ErrorKind { fn from(err: XmlReaderError) -> Self { match err { XmlReaderError::Io(err) if err.kind() == io::ErrorKind::UnexpectedEof => { ErrorKind::UnexpectedEof } XmlReaderError::Io(err) => match std::sync::Arc::try_unwrap(err) { Ok(err) => ErrorKind::Io(err), Err(err) => ErrorKind::Io(std::io::Error::from(err.kind())), }, XmlReaderError::UnexpectedEof(_) => ErrorKind::UnexpectedEof, XmlReaderError::NonDecodable(_) => ErrorKind::InvalidXmlUtf8, _ => ErrorKind::InvalidXmlSyntax, } } } impl Iterator for XmlReader { type Item = Result; fn next(&mut self) -> Option> { if self.finished { return None; } match self.state.read_next(&mut self.buffer) { Ok(Some(event)) => Some(Ok(event)), Ok(None) => { self.finished = true; None } Err(err) => { self.finished = true; Some(Err(err)) } } } } impl ReaderState { fn xml_reader_pos(&self) -> FilePosition { let pos = self.0.buffer_position(); FilePosition(pos as u64) } fn with_pos(&self, kind: ErrorKind) -> Error { kind.with_position(self.xml_reader_pos()) } fn read_xml_event<'buf>(&mut self, buffer: &'buf mut Vec) -> Result, Error> { let event = self.0.read_event_into(buffer); let pos = self.xml_reader_pos(); event.map_err(|err| ErrorKind::from(err).with_position(pos)) } fn read_content(&mut self, buffer: &mut Vec) -> Result { loop { match self.read_xml_event(buffer)? { XmlEvent::Text(text) => { let unescaped = text .unescape() .map_err(|err| self.with_pos(ErrorKind::from(err)))?; return String::from_utf8(unescaped.as_ref().into()) .map_err(|_| self.with_pos(ErrorKind::InvalidUtf8String)); } XmlEvent::End(_) => { return Ok("".to_owned()); } XmlEvent::Eof => return Err(self.with_pos(ErrorKind::UnclosedXmlElement)), XmlEvent::Start(_) => return Err(self.with_pos(ErrorKind::UnexpectedXmlOpeningTag)), XmlEvent::PI(_) | XmlEvent::Empty(_) | XmlEvent::Comment(_) | XmlEvent::CData(_) | XmlEvent::Decl(_) | XmlEvent::DocType(_) => { // skip } } } } fn read_next(&mut self, buffer: &mut Vec) -> Result, Error> { loop { match self.read_xml_event(buffer)? { XmlEvent::Start(name) => { match name.local_name().as_ref() { b"plist" => {} b"array" => return Ok(Some(Event::StartArray(None))), b"dict" => return Ok(Some(Event::StartDictionary(None))), b"key" => { return Ok(Some(Event::String(self.read_content(buffer)?.into()))) } b"data" => { let mut encoded = self.read_content(buffer)?; // Strip whitespace and line endings from input string encoded.retain(|c| !c.is_ascii_whitespace()); let data = base64_standard .decode(&encoded) .map_err(|_| self.with_pos(ErrorKind::InvalidDataString))?; return Ok(Some(Event::Data(data.into()))); } b"date" => { let s = self.read_content(buffer)?; let date = Date::from_xml_format(&s) .map_err(|_| self.with_pos(ErrorKind::InvalidDateString))?; return Ok(Some(Event::Date(date))); } b"integer" => { let s = self.read_content(buffer)?; match Integer::from_str(&s) { Ok(i) => return Ok(Some(Event::Integer(i))), Err(_) => { return Err(self.with_pos(ErrorKind::InvalidIntegerString)) } } } b"real" => { let s = self.read_content(buffer)?; match s.parse() { Ok(f) => return Ok(Some(Event::Real(f))), Err(_) => return Err(self.with_pos(ErrorKind::InvalidRealString)), } } b"string" => { return Ok(Some(Event::String(self.read_content(buffer)?.into()))) } b"true" => return Ok(Some(Event::Boolean(true))), b"false" => return Ok(Some(Event::Boolean(false))), _ => return Err(self.with_pos(ErrorKind::UnknownXmlElement)), } } XmlEvent::End(name) => match name.local_name().as_ref() { b"array" | b"dict" => return Ok(Some(Event::EndCollection)), _ => (), }, XmlEvent::Eof => return Ok(None), XmlEvent::Text(text) => { let unescaped = text .unescape() .map_err(|err| self.with_pos(ErrorKind::from(err)))?; if !unescaped.chars().all(char::is_whitespace) { return Err( self.with_pos(ErrorKind::UnexpectedXmlCharactersExpectedElement) ); } } XmlEvent::PI(_) | XmlEvent::Decl(_) | XmlEvent::DocType(_) | XmlEvent::CData(_) | XmlEvent::Comment(_) | XmlEvent::Empty(_) => { // skip } } } } } #[cfg(test)] mod tests { use std::fs::File; use super::*; use crate::stream::Event::{self, *}; #[test] fn streaming_parser() { let reader = File::open("./tests/data/xml.plist").unwrap(); let streaming_parser = XmlReader::new(reader); let events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); let comparison = &[ StartDictionary(None), String("Author".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_xml_format("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("./tests/data/xml_error.plist").unwrap(); let streaming_parser = XmlReader::new(reader); let events: Vec<_> = streaming_parser.collect(); assert!(events.last().unwrap().is_err()); } } plist-1.6.0/src/stream/xml_writer.rs000064400000000000000000000373531046102023000156220ustar 00000000000000use base64::{engine::general_purpose::STANDARD as base64_standard, Engine}; use quick_xml::{ events::{BytesEnd, BytesStart, BytesText, Event as XmlEvent}, Error as XmlWriterError, Writer as EventWriter, }; use std::{borrow::Cow, io::Write}; use crate::{ error::{self, Error, ErrorKind, EventKind}, stream::{Writer, XmlWriteOptions}, Date, Integer, Uid, }; static XML_PROLOGUE: &[u8] = br#" "#; #[derive(PartialEq)] enum Element { Dictionary, Array, } pub struct XmlWriter { xml_writer: EventWriter, write_root_element: bool, started_plist: bool, stack: Vec, expecting_key: bool, pending_collection: Option, } enum PendingCollection { Array, Dictionary, } impl XmlWriter { #[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")] pub fn new(writer: W) -> XmlWriter { let opts = XmlWriteOptions::default(); XmlWriter::new_with_options(writer, &opts) } pub fn new_with_options(writer: W, opts: &XmlWriteOptions) -> XmlWriter { let xml_writer = if opts.indent_amount == 0 { EventWriter::new(writer) } else { EventWriter::new_with_indent(writer, opts.indent_char, opts.indent_amount) }; XmlWriter { xml_writer, write_root_element: opts.root_element, started_plist: false, stack: Vec::new(), expecting_key: false, pending_collection: None, } } #[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")] pub fn into_inner(self) -> W { self.xml_writer.into_inner() } 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_event(XmlEvent::Start(BytesStart::new(name)))?; Ok(()) } fn end_element(&mut self, name: &str) -> Result<(), Error> { self.xml_writer .write_event(XmlEvent::End(BytesEnd::new(name)))?; Ok(()) } fn write_value(&mut self, value: &str) -> Result<(), Error> { self.xml_writer .write_event(XmlEvent::Text(BytesText::new(value)))?; Ok(()) } fn write_event Result<(), Error>>( &mut self, f: F, ) -> Result<(), Error> { if !self.started_plist { if self.write_root_element { self.xml_writer .get_mut() .write_all(XML_PROLOGUE) .map_err(error::from_io_without_position)?; } self.started_plist = true; } f(self)?; // If there are no more open tags then write the element if self.stack.is_empty() { if self.write_root_element { // We didn't tell the xml_writer about the tag so we'll skip telling it // about the tag as well. self.xml_writer .get_mut() .write_all(b"\n") .map_err(error::from_io_without_position)?; } self.xml_writer .get_mut() .flush() .map_err(error::from_io_without_position)?; } Ok(()) } fn write_value_event Result<(), Error>>( &mut self, event_kind: EventKind, f: F, ) -> Result<(), Error> { self.handle_pending_collection()?; 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(()) }) } fn handle_pending_collection(&mut self) -> Result<(), Error> { if let Some(PendingCollection::Array) = self.pending_collection { self.pending_collection = None; self.write_value_event(EventKind::StartArray, |this| { this.start_element("array")?; this.stack.push(Element::Array); Ok(()) }) } else if let Some(PendingCollection::Dictionary) = self.pending_collection { self.pending_collection = None; self.write_value_event(EventKind::StartDictionary, |this| { this.start_element("dict")?; this.stack.push(Element::Dictionary); this.expecting_key = true; Ok(()) }) } else { Ok(()) } } } impl Writer for XmlWriter { fn write_start_array(&mut self, _len: Option) -> Result<(), Error> { self.handle_pending_collection()?; self.pending_collection = Some(PendingCollection::Array); Ok(()) } fn write_start_dictionary(&mut self, _len: Option) -> Result<(), Error> { self.handle_pending_collection()?; self.pending_collection = Some(PendingCollection::Dictionary); Ok(()) } fn write_end_collection(&mut self) -> Result<(), Error> { self.write_event(|this| { match this.pending_collection.take() { Some(PendingCollection::Array) => { this.xml_writer .write_event(XmlEvent::Empty(BytesStart::new("array")))?; this.expecting_key = this.stack.last() == Some(&Element::Dictionary); return Ok(()); } Some(PendingCollection::Dictionary) => { this.xml_writer .write_event(XmlEvent::Empty(BytesStart::new("dict")))?; this.expecting_key = this.stack.last() == Some(&Element::Dictionary); return Ok(()); } _ => {} }; 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 = if value { "true" } else { "false" }; Ok(this .xml_writer .write_event(XmlEvent::Empty(BytesStart::new(value)))?) }) } fn write_data(&mut self, value: Cow<[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_xml_format()) }) } 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: Cow) -> Result<(), Error> { self.handle_pending_collection()?; 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()) } } impl From for Error { fn from(err: XmlWriterError) -> Self { match err { XmlWriterError::Io(err) => match std::sync::Arc::try_unwrap(err) { Ok(err) => ErrorKind::Io(err), Err(err) => ErrorKind::Io(std::io::Error::from(err.kind())), } .without_position(), _ => unreachable!(), } } } pub(crate) fn base64_encode_plist(data: &[u8], indent: usize) -> String { // XML plist data elements are always formatted by apple tools as // // AAAA..AA (68 characters per line) // // Allocate space for base 64 string and line endings up front const LINE_LEN: usize = 68; let mut line_ending = Vec::with_capacity(1 + indent); line_ending.push(b'\n'); (0..indent).for_each(|_| line_ending.push(b'\t')); // Find the max length of `data` encoded as a base 64 string with padding let base64_max_string_len = data.len() * 4 / 3 + 4; // Find the max length of the formatted base 64 string as: max length of the base 64 string // + line endings and indents at the start of the string and after every line let base64_max_string_len_with_formatting = base64_max_string_len + (2 + base64_max_string_len / LINE_LEN) * line_ending.len(); let mut output = vec![0; base64_max_string_len_with_formatting]; // Start output with a line ending and indent output[..line_ending.len()].copy_from_slice(&line_ending); // Encode `data` as a base 64 string let base64_string_len = base64_standard .encode_slice(data, &mut output[line_ending.len()..]) .expect("encoding slice fits base64 buffer"); // 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::>().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_xml_format("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 expected = " \tAuthor \tWilliam Shakespeare \tLines \t \t\tIt is a tale told by an idiot, \t\tFull of sound and fury, signifying nothing. \t\t \t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEy \t\tMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2Rl \t\tZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8= \t\t \t \tDeath \t1564 \tHeight \t1.6 \tData \t \tAAAAvgAAAAMAAAAeAAAA \t \tBirthdate \t1981-05-16T11:32:06Z \tComment \t2 < 3 \tBiggestNumber \t18446744073709551615 \tSmallestNumber \t-9223372036854775808 \tIsTrue \t \tIsNotFalse \t "; let actual = events_to_xml(plist, XmlWriteOptions::default()); assert_eq!(actual, expected); } #[test] fn custom_indent_string() { let plist = [ 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::EndCollection, ]; let expected = " ...It is a tale told by an idiot, ...Full of sound and fury, signifying nothing. "; let actual = events_to_xml(plist, XmlWriteOptions::default().indent(b'.', 3)); assert_eq!(actual, expected); } #[test] fn no_root() { let plist = [ 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::EndCollection, ]; let expected = " \tIt is a tale told by an idiot, \tFull of sound and fury, signifying nothing. "; let actual = events_to_xml(plist, XmlWriteOptions::default().root_element(false)); assert_eq!(actual, expected); } fn events_to_xml<'event>( events: impl IntoIterator>, options: XmlWriteOptions, ) -> String { let mut cursor = Cursor::new(Vec::new()); let mut writer = XmlWriter::new_with_options(&mut cursor, &options); for event in events { writer.write(event).unwrap(); } String::from_utf8(cursor.into_inner()).unwrap() } } plist-1.6.0/src/uid.rs000064400000000000000000000045011046102023000127010ustar 00000000000000use std::fmt; /// A plist `uid` value. These are found exclusively in plists created by `NSKeyedArchiver`. #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct Uid { value: u64, } impl Uid { /// Creates a new `Uid` containing the given value. pub fn new(value: u64) -> Uid { Uid { value } } /// Returns the value as a `u64`. pub fn get(self) -> u64 { self.value } } impl fmt::Debug for Uid { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.value.fmt(f) } } #[cfg(feature = "serde")] pub mod serde_impls { use serde::{ de::{Deserialize, Deserializer, Error, Visitor}, ser::{Serialize, Serializer}, }; use std::fmt; use crate::Uid; pub const UID_NEWTYPE_STRUCT_NAME: &str = "PLIST-UID"; impl Serialize for Uid { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_newtype_struct(UID_NEWTYPE_STRUCT_NAME, &self.get()) } } struct UidNewtypeVisitor; impl<'de> Visitor<'de> for UidNewtypeVisitor { type Value = Uid; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a plist uid") } fn visit_u64(self, v: u64) -> Result where E: Error, { UidU64Visitor.visit_u64(v) } fn visit_newtype_struct(self, deserializer: D) -> Result 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(self, v: u64) -> Result where E: Error, { Ok(Uid::new(v)) } } impl<'de> Deserialize<'de> for Uid { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_newtype_struct(UID_NEWTYPE_STRUCT_NAME, UidNewtypeVisitor) } } } plist-1.6.0/src/value.rs000064400000000000000000000626601046102023000132460ustar 00000000000000use std::{ borrow::Cow, fs::File, io::{BufReader, BufWriter, Read, Seek, Write}, path::Path, }; use crate::{ error::{self, Error, ErrorKind, EventKind}, stream::{ private, BinaryWriter, Event, Events, 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), Dictionary(Dictionary), Boolean(bool), Data(Vec), 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>(path: P) -> Result { let file = File::open(path).map_err(error::from_io_without_position)?; Value::from_reader(BufReader::new(file)) } /// Reads a `Value` from a seekable byte stream containing a plist of any encoding. pub fn from_reader(reader: R) -> Result { let reader = Reader::new(reader); Value::from_events(reader) } /// Reads a `Value` from a seekable byte stream containing an XML encoded plist. pub fn from_reader_xml(reader: R) -> Result { let reader = XmlReader::new(reader); Value::from_events(reader) } /// Serializes a `Value` to a file as a binary encoded plist. pub fn to_file_binary>(&self, path: P) -> Result<(), Error> { let mut file = File::create(path).map_err(error::from_io_without_position)?; self.to_writer_binary(BufWriter::new(&mut file))?; file.sync_all().map_err(error::from_io_without_position)?; Ok(()) } /// Serializes a `Value` to a file as an XML encoded plist. pub fn to_file_xml>(&self, path: P) -> Result<(), Error> { let mut file = File::create(path).map_err(error::from_io_without_position)?; self.to_writer_xml(BufWriter::new(&mut file))?; file.sync_all().map_err(error::from_io_without_position)?; Ok(()) } /// Serializes a `Value` to a byte stream as a binary encoded plist. pub fn to_writer_binary(&self, writer: W) -> Result<(), Error> { let mut writer = BinaryWriter::new(writer); self.to_writer_inner(&mut writer) } /// Serializes a `Value` to a byte stream as an XML encoded plist. pub fn to_writer_xml(&self, writer: W) -> Result<(), Error> { 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( &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<'event, T>(events: T) -> Result where T: IntoIterator, Error>>, { Builder::build(events.into_iter()) } /// 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<'event, T>(events: T) -> Result where T: IntoIterator, Error>>, { Builder::build(events.into_iter()) } /// 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> { match self { Value::Array(dict) => Some(dict), _ => None, } } /// If the `Value` is an Array, returns the associated `Vec`. /// /// Returns `None` otherwise. pub fn as_array(&self) -> Option<&Vec> { match *self { Value::Array(ref array) => Some(array), _ => None, } } /// If the `Value` is an Array, returns the associated mutable `Vec`. /// /// Returns `None` otherwise. pub fn as_array_mut(&mut self) -> Option<&mut Vec> { match *self { Value::Array(ref mut array) => Some(array), _ => None, } } /// If the `Value` is a Dictionary, returns the associated `BTreeMap`. /// /// Returns `None` otherwise. /// /// This method consumes the `Value`. To get a reference instead, use /// `as_dictionary`. pub fn into_dictionary(self) -> Option { match self { Value::Dictionary(dict) => Some(dict), _ => None, } } /// If the `Value` is a Dictionary, returns the associated `BTreeMap`. /// /// Returns `None` otherwise. pub fn as_dictionary(&self) -> Option<&Dictionary> { match *self { Value::Dictionary(ref dict) => Some(dict), _ => None, } } /// If the `Value` is a Dictionary, returns the associated mutable `BTreeMap`. /// /// Returns `None` otherwise. pub fn as_dictionary_mut(&mut self) -> Option<&mut Dictionary> { match *self { Value::Dictionary(ref mut dict) => Some(dict), _ => None, } } /// If the `Value` is a Boolean, returns the associated `bool`. /// /// Returns `None` otherwise. pub fn as_boolean(&self) -> Option { match *self { Value::Boolean(v) => Some(v), _ => None, } } /// If the `Value` is a Data, returns the underlying `Vec`. /// /// Returns `None` otherwise. /// /// This method consumes the `Value`. If this is not desired, please use /// `as_data` method. pub fn into_data(self) -> Option> { match self { Value::Data(data) => Some(data), _ => None, } } /// If the `Value` is a Data, returns the associated `Vec`. /// /// Returns `None` otherwise. pub fn as_data(&self) -> Option<&[u8]> { match *self { Value::Data(ref data) => Some(data), _ => None, } } /// If the `Value` is a Date, returns the associated `Date`. /// /// Returns `None` otherwise. pub fn as_date(&self) -> Option { match *self { Value::Date(date) => Some(date), _ => None, } } /// If the `Value` is a Real, returns the associated `f64`. /// /// Returns `None` otherwise. pub fn as_real(&self) -> Option { match *self { Value::Real(v) => Some(v), _ => None, } } /// If the `Value` is a signed Integer, returns the associated `i64`. /// /// Returns `None` otherwise. pub fn as_signed_integer(&self) -> Option { match *self { Value::Integer(v) => v.as_signed(), _ => None, } } /// If the `Value` is an unsigned Integer, returns the associated `u64`. /// /// Returns `None` otherwise. pub fn as_unsigned_integer(&self) -> Option { match *self { Value::Integer(v) => v.as_unsigned(), _ => None, } } /// If the `Value` is a String, returns the underlying `String`. /// /// Returns `None` otherwise. /// /// This method consumes the `Value`. If this is not desired, please use /// `as_string` method. pub fn into_string(self) -> Option { match self { Value::String(v) => Some(v), _ => None, } } /// If the `Value` is a String, returns the associated `str`. /// /// Returns `None` otherwise. pub fn as_string(&self) -> Option<&str> { match *self { Value::String(ref v) => Some(v), _ => None, } } /// 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 { 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(&self, serializer: S) -> Result 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(deserializer: D) -> Result 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(self, value: bool) -> Result { Ok(Value::Boolean(value)) } fn visit_byte_buf(self, v: Vec) -> Result { Ok(Value::Data(v)) } fn visit_bytes(self, v: &[u8]) -> Result { Ok(Value::Data(v.to_vec())) } fn visit_i64(self, value: i64) -> Result { Ok(Value::Integer(value.into())) } fn visit_u64(self, value: u64) -> Result { Ok(Value::Integer(value.into())) } fn visit_f64(self, value: f64) -> Result { Ok(Value::Real(value)) } fn visit_map(self, mut map: V) -> Result 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(self, value: &str) -> Result { Ok(Value::String(value.to_owned())) } fn visit_string(self, value: String) -> Result { Ok(Value::String(value)) } fn visit_newtype_struct(self, deserializer: T) -> Result where T: de::Deserializer<'de>, { deserializer.deserialize_any(self) } fn visit_seq(self, mut seq: A) -> Result 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(self, data: A) -> Result where A: EnumAccess<'de>, { let (name, variant) = data.variant::()?; 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> for Value { fn from(from: Vec) -> Value { Value::Array(from) } } impl From for Value { fn from(from: Dictionary) -> Value { Value::Dictionary(from) } } impl From for Value { fn from(from: bool) -> Value { Value::Boolean(from) } } impl<'a> From<&'a bool> for Value { fn from(from: &'a bool) -> Value { Value::Boolean(*from) } } impl From for Value { fn from(from: Date) -> Value { Value::Date(from) } } impl<'a> From<&'a Date> for Value { fn from(from: &'a Date) -> Value { Value::Date(*from) } } impl From for Value { fn from(from: f64) -> Value { Value::Real(from) } } impl From for Value { fn from(from: f32) -> Value { Value::Real(from.into()) } } impl From for Value { fn from(from: i64) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: i32) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: i16) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: i8) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u64) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u32) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u16) -> Value { Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u8) -> Value { Value::Integer(Integer::from(from)) } } impl<'a> From<&'a f64> for Value { fn from(from: &'a f64) -> Value { Value::Real(*from) } } impl<'a> From<&'a f32> for Value { fn from(from: &'a f32) -> Value { Value::Real((*from).into()) } } impl<'a> From<&'a i64> for Value { fn from(from: &'a i64) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a i32> for Value { fn from(from: &'a i32) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a i16> for Value { fn from(from: &'a i16) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a i8> for Value { fn from(from: &'a i8) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a u64> for Value { fn from(from: &'a u64) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a u32> for Value { fn from(from: &'a u32) -> Value { Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a u16> for Value { fn from(from: &'a u16) -> Value { Value::Integer((*from).into()) } } impl<'a> From<&'a u8> for Value { fn from(from: &'a u8) -> Value { Value::Integer((*from).into()) } } impl From for Value { fn from(from: String) -> Value { Value::String(from) } } impl<'a> From<&'a str> for Value { fn from(from: &'a str) -> Value { Value::String(from.into()) } } enum StackItem { Root(Value), Array(Vec), Dict(Dictionary), DictAndKey(Dictionary, String), } #[derive(Default)] pub struct Builder { stack: Vec, } impl Builder { fn build<'event, T>(stream: T) -> Result where T: Iterator, Error>>, { let mut builder = Self::default(); for event in stream { builder.write(event?)?; } builder.finish() } fn write_value(&mut self, value: Value) -> Result<(), Error> { match (self.stack.pop(), value) { (None, value) => self.stack.push(StackItem::Root(value)), (Some(StackItem::Root(_)), value) => { return Err(ErrorKind::ExpectedEndOfEventStream { found: EventKind::of_value(&value), } .without_position()) } (Some(StackItem::Array(mut array)), value) => { array.push(value); self.stack.push(StackItem::Array(array)); } (Some(StackItem::Dict(dict)), Value::String(key)) => { self.stack.push(StackItem::DictAndKey(dict, key)) } (Some(StackItem::Dict(_)), value) => { return Err(ErrorKind::UnexpectedEventType { expected: EventKind::DictionaryKeyOrEndCollection, found: EventKind::of_value(&value), } .without_position()) } (Some(StackItem::DictAndKey(mut dict, key)), value) => { dict.insert(key, value); self.stack.push(StackItem::Dict(dict)); } } Ok(()) } pub fn finish(&mut self) -> Result { match self.stack.pop() { Some(StackItem::Root(value)) => Ok(value), _ => Err(ErrorKind::UnexpectedEndOfEventStream.without_position()), } } } impl Writer for Builder { fn write_start_array(&mut self, len: Option) -> Result<(), Error> { let len = len.and_then(u64_to_usize).unwrap_or(0); self.stack.push(StackItem::Array(Vec::with_capacity(len))); Ok(()) } fn write_start_dictionary(&mut self, _: Option) -> Result<(), Error> { self.stack.push(StackItem::Dict(Dictionary::new())); Ok(()) } fn write_end_collection(&mut self) -> Result<(), Error> { let value = match self.stack.pop() { Some(StackItem::Root(_)) => { return Err(ErrorKind::ExpectedEndOfEventStream { found: EventKind::EndCollection, } .without_position()) } Some(StackItem::Array(array)) => Value::Array(array), Some(StackItem::Dict(dict)) => Value::Dictionary(dict), Some(StackItem::DictAndKey(_, _)) | None => { return Err(ErrorKind::UnexpectedEventType { expected: EventKind::ValueOrStartCollection, found: EventKind::EndCollection, } .without_position()) } }; self.write_value(value) } fn write_boolean(&mut self, value: bool) -> Result<(), Error> { self.write_value(Value::Boolean(value)) } fn write_data(&mut self, value: Cow<[u8]>) -> Result<(), Error> { self.write_value(Value::Data(value.into_owned())) } 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)) } fn write_string(&mut self, value: Cow) -> Result<(), Error> { self.write_value(Value::String(value.into_owned())) } fn write_uid(&mut self, value: Uid) -> Result<(), Error> { self.write_value(Value::Uid(value)) } } impl private::Sealed for Builder {} #[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 value = Builder::build(events.into_iter().map(|e| Ok(e))); // Expected output let lines = vec![ Value::String("It is a tale told by an idiot,".to_owned()), 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!(value.unwrap(), Value::Dictionary(dict)); } } plist-1.6.0/tests/data/binary.plist000064400000000000000000000006531046102023000154030ustar 00000000000000bplist00 VAuthorYBirthdateZEmptyArrayZIsNotFalse^SmallestNumber_EmptyDictionaryVHeightULinesUDeathUBlank]BiggestNumberVIsTrueTData_William Shakespeare3ve#?_#It is a tale told by an idiot, _+Full of sound and fury, signifying nothing.P O#*4?JYkrx~),->?Qplist-1.6.0/tests/data/binary_NSKeyedArchiver.plist000064400000000000000000000007501046102023000204470ustar 00000000000000bplist00 X$versionX$objectsY$archiverT$topU$null \NSRangeCountV$class[NSRangeData* WNS.dataOg ,035<@GLSV[dox|@Z$classnameX$classes]NSMutableDataVNSDataXNSObject_NSMutableIndexSet_NSMutableIndexSetZNSIndexSet_NSKeyedArchiver!"ZfoundItems#-27=CJW^jlnpu}$)=AU`ru#plist-1.6.0/tests/data/binary_circular_array.plist000064400000000000000000000007031046102023000204610ustar 00000000000000bplist00 LinesUDeathVHeightYBirthdateVAuthorTData _It is a tale Tolda ateVAuthorTData _>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>It is a tale Tolda y bn idiot,_+Full of sound and fury, signif sound and fury, signifying nthng.#?6v!eareOm ShakespeareO!(28>Abplist-1.6.0/tests/data/binary_zero_offset_size.plist000064400000000000000000000004241046102023000210360ustar 00000000000000bplist00 ULinesUDeathVHeightYBirthdate signifying nthing.#?#?3ve_William ShakespeareO!(29>Abte signifying nthing.#?#?3ve_William ShakespeareO!(plist-1.6.0/tests/data/book.plist000064400000000000000000000012211046102023000150410ustar 00000000000000 Title Great Expectations Author Charles Dickens Excerpt Whether I should have made out this object so soon, if there had been no fine lady sitting at it, I cannot say. In an armchair, with an elbow resting on the table and her head leaning on that hand, sat the strangest lady I have ever seen, or shall ever see. CopiesSold 123456789 plist-1.6.0/tests/data/utf16_bplist.plist000064400000000000000000000025421046102023000164400ustar 00000000000000bplist00TnameXlongTextk& or betteroThe sun was shining on the sea, Shining with all his might: He did his very best to make The billows smooth and bright -- And this was odd, because it was The middle of the night. The moon was shining sulkily, Because she thought the sun Had got no business to be there After the day was done -- "It's very rude of him," she said, "To come and spoil the fun!" The sea was wet as wet could be, The sands were dry as dry. You could not see a cloud, because No cloud was in the sky: No birds were flying overhead -- There were no birds to fly. In a Wonderland they lie Dreaming as the days go by, Dreaming as the summer die. & 28plist-1.6.0/tests/data/xml.plist000064400000000000000000000016311046102023000147140ustar 00000000000000 Author William Shakespeare Lines It is a tale told by an idiot, Full of sound and fury, signifying nothing. Death 1564 Height 1.6 Data AAAAvgAAAA MAAAAeAAAA Birthdate 1981-05-16T11:32:06Z Blank BiggestNumber 18446744073709551615 SmallestNumber -9223372036854775808 HexademicalNumber 0xDEADBEEF IsTrue IsNotFalse plist-1.6.0/tests/data/xml_error.plist000064400000000000000000000007411046102023000161260ustar 00000000000000 Author William Shakespeare Lines It is a tale told by an idiot, Full of sound and fury, signifying nothing. Death 1564 Height 1.6 DataAb\x90\x93\x9c\xa5\xbb\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcd"; test_fuzzer_data_err(data); } #[test] fn binary_zero_offset_size() { let data = include_bytes!("data/binary_zero_offset_size.plist"); test_fuzzer_data_err(data); } #[test] fn binary_nan_date() { let data = b"bplist00\xd6\x01\x02\x01\x04\x05\x06\x07\x0a\x0b\x0c\x0d\x0eULinesUDeathVHeightYBthridateVAuthorTData\xa2\x08\x09_\x10\x1eIt is a tale told by an idiot,_\x10+Full of sound and fury, signifying nothing.\x11\x06\x1c#?\xf9\x99\x99\x99\x99\x99\x9a3\xff\xff\xff\xffe\x00\x00\x00_\x13\x10William ShakespeareO\x10\xe5\x00\x00\x00\xbe\x00\x00\x00\x03\x00\x00\x00\x1e\x00\x00\x00\x08\x15\x1b!(14>Ab\x90\x93\x9c\xa5\xbb\xd4\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcd"; test_fuzzer_data_err(data); } #[test] fn binary_circular_array() { let data = include_bytes!("data/binary_circular_array.plist"); test_fuzzer_data_err(data); } // Issue 20 - not found by fuzzing but this is a convenient place to put the test. #[test] fn issue_20_binary_with_data_in_trailer() { let data = b"bplist00\xd0\x08\0\0\0\0\0\0\x01\x01\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t"; test_fuzzer_data_ok(data); } #[test] fn issue_22_binary_with_byte_ref_size() { let data = b"bplist00\xd1\x01\x02TTestQ1\x08\x0b\x10\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12"; test_fuzzer_data_ok(data); } #[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 { 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()); }