diff -Nru rust-plist-0.4.2/Cargo.toml rust-plist-0.5.4/Cargo.toml --- rust-plist-0.4.2/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/Cargo.toml 2020-04-06 15:45:43.000000000 +0000 @@ -3,7 +3,7 @@ # 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 +# to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're @@ -11,11 +11,12 @@ # will likely look very different (and much more reasonable) [package] +edition = "2018" name = "plist" -version = "0.4.2" +version = "0.5.4" authors = ["Ed Barnard "] description = "A rusty plist parser. Supports Serde serialization." -documentation = "https://docs.rs/plist/0.4.2/plist/" +documentation = "https://docs.rs/plist/0.5.4/plist/" keywords = ["plist", "parser"] categories = ["config", "encoding", "parser-implementations"] license = "MIT" @@ -23,11 +24,11 @@ [dependencies.base64] version = "0.10.1" -[dependencies.byteorder] -version = "1.1.0" - [dependencies.humantime] -version = "1.1.1" +version = "2.0.0" + +[dependencies.indexmap] +version = "1.0.2" [dependencies.line-wrap] version = "0.1.1" @@ -36,10 +37,12 @@ version = "1.0.2" optional = true -[dependencies.xml-rs] +[dependencies.xml_rs] version = "0.8.0" +package = "xml-rs" [dev-dependencies.serde_derive] version = "1.0.2" [features] default = ["serde"] +enable_unstable_features_that_may_break_with_minor_version_bumps = [] diff -Nru rust-plist-0.4.2/Cargo.toml.orig rust-plist-0.5.4/Cargo.toml.orig --- rust-plist-0.4.2/Cargo.toml.orig 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/Cargo.toml.orig 2020-04-06 15:45:07.000000000 +0000 @@ -1,45 +1,26 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - [package] name = "plist" -version = "0.4.2" +version = "0.5.4" authors = ["Ed Barnard "] description = "A rusty plist parser. Supports Serde serialization." -documentation = "https://docs.rs/plist/0.4.2/plist/" -keywords = ["plist", "parser"] -categories = ["config", "encoding", "parser-implementations"] license = "MIT" repository = "https://github.com/ebarnard/rust-plist/" -[dependencies.base64] -version = "0.10.1" - -[dependencies.byteorder] -version = "1.1.0" - -[dependencies.humantime] -version = "1.1.1" - -[dependencies.line-wrap] -version = "0.1.1" - -[dependencies.serde] -version = "1.0.2" -optional = true - -[dependencies.xml-rs] -version = "0.8.0" -[dev-dependencies.serde_derive] -version = "1.0.2" +documentation = "https://docs.rs/plist/0.5.4/plist/" +keywords = ["plist", "parser"] +categories = ["config", "encoding", "parser-implementations"] +edition = "2018" [features] default = ["serde"] +enable_unstable_features_that_may_break_with_minor_version_bumps = [] + +[dependencies] +base64 = "0.10.1" +humantime = "2.0.0" +indexmap = "1.0.2" +line-wrap = "0.1.1" +xml_rs = { package = "xml-rs", version = "0.8.0" } +serde = { version = "1.0.2", optional = true } + +[dev-dependencies] +serde_derive = { version = "1.0.2" } diff -Nru rust-plist-0.4.2/.cargo_vcs_info.json rust-plist-0.5.4/.cargo_vcs_info.json --- rust-plist-0.4.2/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/.cargo_vcs_info.json 2020-04-06 15:45:43.000000000 +0000 @@ -1,5 +1,5 @@ { "git": { - "sha1": "214b2438ee8fa58786ef980d55a80fef73f7021b" + "sha1": "3af3e6ae0ae9abce0b199200a3dc36353641b21e" } } diff -Nru rust-plist-0.4.2/debian/cargo-checksum.json rust-plist-0.5.4/debian/cargo-checksum.json --- rust-plist-0.4.2/debian/cargo-checksum.json 2019-07-20 19:35:01.000000000 +0000 +++ rust-plist-0.5.4/debian/cargo-checksum.json 2020-06-10 19:39:10.000000000 +0000 @@ -1 +1 @@ -{"package":"5f2a9f075f6394100e7c105ed1af73fb1859d6fd14e49d4290d578120beb167f","files":{}} +{"package":"Could not get crate checksum","files":{}} diff -Nru rust-plist-0.4.2/debian/changelog rust-plist-0.5.4/debian/changelog --- rust-plist-0.4.2/debian/changelog 2019-07-20 19:35:01.000000000 +0000 +++ rust-plist-0.5.4/debian/changelog 2020-06-10 19:39:10.000000000 +0000 @@ -1,3 +1,24 @@ +rust-plist (0.5.4-2) unstable; urgency=medium + + * Team upload. + * enable use with base64 0.12.* + + -- Daniel Kahn Gillmor Wed, 10 Jun 2020 15:39:10 -0400 + +rust-plist (0.5.4-1) unstable; urgency=medium + + * Team upload. + * Package plist 0.5.4 from crates.io using debcargo 2.4.2 + + -- Sylvestre Ledru Tue, 14 Apr 2020 12:14:44 +0200 + +rust-plist (0.5.1-1) unstable; urgency=medium + + * Team upload. + * Package plist 0.5.1 from crates.io using debcargo 2.4.2 + + -- Ximin Luo Wed, 08 Jan 2020 23:20:49 +0000 + rust-plist (0.4.2-1) unstable; urgency=medium * Package plist 0.4.2 from crates.io using debcargo 2.2.10 diff -Nru rust-plist-0.4.2/debian/control rust-plist-0.5.4/debian/control --- rust-plist-0.4.2/debian/control 2019-07-20 19:35:01.000000000 +0000 +++ rust-plist-0.5.4/debian/control 2020-06-10 19:39:10.000000000 +0000 @@ -2,20 +2,20 @@ Section: rust Priority: optional Build-Depends: debhelper (>= 11), - dh-cargo (>= 15), + dh-cargo (>= 18), cargo:native , rustc:native , libstd-rust-dev , - librust-base64-0.10+default-dev (>= 0.10.1-~~) , - librust-byteorder-1+default-dev (>= 1.1.0-~~) , - librust-humantime-1+default-dev (>= 1.1.1-~~) , + librust-base64-0.12+default-dev | librust-base64-0.11+default-dev | librust-base64-0.10+default-dev (>= 0.10.1-~~) , + librust-humantime-2+default-dev , + librust-indexmap-1+default-dev (>= 1.0.2-~~) , librust-line-wrap-0.1+default-dev (>= 0.1.1-~~) , librust-serde-1+default-dev (>= 1.0.2-~~) , librust-xml-rs-0.8+default-dev Maintainer: Debian Rust Maintainers Uploaders: Helen Koike -Standards-Version: 4.2.0 +Standards-Version: 4.4.1 Vcs-Git: https://salsa.debian.org/rust-team/debcargo-conf.git [src/plist] Vcs-Browser: https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/plist @@ -24,19 +24,21 @@ Multi-Arch: same Depends: ${misc:Depends}, - librust-base64-0.10+default-dev (>= 0.10.1-~~), - librust-byteorder-1+default-dev (>= 1.1.0-~~), - librust-humantime-1+default-dev (>= 1.1.1-~~), + librust-base64-0.12+default-dev | librust-base64-0.11+default-dev | librust-base64-0.10+default-dev (>= 0.10.1-~~), + librust-humantime-2+default-dev, + librust-indexmap-1+default-dev (>= 1.0.2-~~), librust-line-wrap-0.1+default-dev (>= 0.1.1-~~), librust-xml-rs-0.8+default-dev Recommends: librust-plist+default-dev (= ${binary:Version}) -Suggests: - librust-plist+serde-dev (= ${binary:Version}) Provides: + librust-plist+enable-unstable-features-that-may-break-with-minor-version-bumps-dev (= ${binary:Version}), librust-plist-0-dev (= ${binary:Version}), - librust-plist-0.4-dev (= ${binary:Version}), - librust-plist-0.4.2-dev (= ${binary:Version}) + librust-plist-0+enable-unstable-features-that-may-break-with-minor-version-bumps-dev (= ${binary:Version}), + librust-plist-0.5-dev (= ${binary:Version}), + librust-plist-0.5+enable-unstable-features-that-may-break-with-minor-version-bumps-dev (= ${binary:Version}), + librust-plist-0.5.4-dev (= ${binary:Version}), + librust-plist-0.5.4+enable-unstable-features-that-may-break-with-minor-version-bumps-dev (= ${binary:Version}) Description: Rusty plist parser - Rust source code Supports Serde serialization. . @@ -51,28 +53,17 @@ librust-plist-dev (= ${binary:Version}), librust-serde-1+default-dev (>= 1.0.2-~~) Provides: + librust-plist+serde-dev (= ${binary:Version}), librust-plist-0+default-dev (= ${binary:Version}), - librust-plist-0.4+default-dev (= ${binary:Version}), - librust-plist-0.4.2+default-dev (= ${binary:Version}) -Description: Rusty plist parser - feature "default" + librust-plist-0+serde-dev (= ${binary:Version}), + librust-plist-0.5+default-dev (= ${binary:Version}), + librust-plist-0.5+serde-dev (= ${binary:Version}), + librust-plist-0.5.4+default-dev (= ${binary:Version}), + librust-plist-0.5.4+serde-dev (= ${binary:Version}) +Description: Rusty plist parser - feature "default" and 1 more Supports Serde serialization. . - This metapackage enables feature default for the Rust plist crate, by pulling + This metapackage enables feature "default" for the Rust plist crate, by pulling in any additional dependencies needed by that feature. - -Package: librust-plist+serde-dev -Architecture: any -Multi-Arch: same -Depends: - ${misc:Depends}, - librust-plist-dev (= ${binary:Version}), - librust-serde-1+default-dev (>= 1.0.2-~~) -Provides: - librust-plist-0+serde-dev (= ${binary:Version}), - librust-plist-0.4+serde-dev (= ${binary:Version}), - librust-plist-0.4.2+serde-dev (= ${binary:Version}) -Description: Rusty plist parser - feature "serde" - Supports Serde serialization. . - This metapackage enables feature serde for the Rust plist crate, by pulling in - any additional dependencies needed by that feature. + Additionally, this package also provides the "serde" feature. diff -Nru rust-plist-0.4.2/debian/copyright.debcargo.hint rust-plist-0.5.4/debian/copyright.debcargo.hint --- rust-plist-0.4.2/debian/copyright.debcargo.hint 2019-07-20 19:35:01.000000000 +0000 +++ rust-plist-0.5.4/debian/copyright.debcargo.hint 2020-06-10 19:39:10.000000000 +0000 @@ -21,8 +21,8 @@ Files: debian/* Copyright: - 2019 Debian Rust Maintainers - 2019 Helen Koike + 2019-2020 Debian Rust Maintainers + 2019-2020 Helen Koike License: MIT License: MIT diff -Nru rust-plist-0.4.2/debian/patches/series rust-plist-0.5.4/debian/patches/series --- rust-plist-0.4.2/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/debian/patches/series 2020-06-10 19:39:10.000000000 +0000 @@ -0,0 +1 @@ +update-base64.patch diff -Nru rust-plist-0.4.2/debian/patches/update-base64.patch rust-plist-0.5.4/debian/patches/update-base64.patch --- rust-plist-0.4.2/debian/patches/update-base64.patch 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/debian/patches/update-base64.patch 2020-06-10 19:39:10.000000000 +0000 @@ -0,0 +1,13 @@ +Index: plist/Cargo.toml +=================================================================== +--- plist.orig/Cargo.toml ++++ plist/Cargo.toml +@@ -22,7 +22,7 @@ categories = ["config", "encoding", "par + license = "MIT" + repository = "https://github.com/ebarnard/rust-plist/" + [dependencies.base64] +-version = "0.10.1" ++version = ">= 0.10.1, < 0.13" + + [dependencies.humantime] + version = "2.0.0" diff -Nru rust-plist-0.4.2/debian/tests/control rust-plist-0.5.4/debian/tests/control --- rust-plist-0.4.2/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/debian/tests/control 2020-06-10 19:39:10.000000000 +0000 @@ -0,0 +1,14 @@ +Test-Command: /usr/share/cargo/bin/cargo-auto-test plist 0.5.4 --all-targets --all-features +Features: test-name=@ +Depends: dh-cargo (>= 18), librust-serde-derive-1+default-dev (>= 1.0.2-~~), @ +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test plist 0.5.4 --all-targets --no-default-features +Features: test-name=librust-plist-dev +Depends: dh-cargo (>= 18), librust-serde-derive-1+default-dev (>= 1.0.2-~~), @ +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test plist 0.5.4 --all-targets --features default +Features: test-name=librust-plist+default-dev +Depends: dh-cargo (>= 18), librust-serde-derive-1+default-dev (>= 1.0.2-~~), @ +Restrictions: allow-stderr, skip-not-installable diff -Nru rust-plist-0.4.2/debian/watch rust-plist-0.5.4/debian/watch --- rust-plist-0.4.2/debian/watch 2019-07-20 19:35:01.000000000 +0000 +++ rust-plist-0.5.4/debian/watch 2020-06-10 19:39:10.000000000 +0000 @@ -2,4 +2,3 @@ opts=filenamemangle=s/.*\/(.*)\/download/plist-$1\.tar\.gz/g,\ uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \ https://qa.debian.org/cgi-bin/fakeupstream.cgi?upstream=crates.io/plist .*/crates/plist/@ANY_VERSION@/download - diff -Nru rust-plist-0.4.2/.gitignore rust-plist-0.5.4/.gitignore --- rust-plist-0.4.2/.gitignore 2015-09-23 20:12:33.000000000 +0000 +++ rust-plist-0.5.4/.gitignore 2020-01-21 21:04:17.000000000 +0000 @@ -1,2 +1,3 @@ target Cargo.lock +.idea diff -Nru rust-plist-0.4.2/README.md rust-plist-0.5.4/README.md --- rust-plist-0.4.2/README.md 2017-02-22 17:49:34.000000000 +0000 +++ rust-plist-0.5.4/README.md 2019-07-06 14:08:21.000000000 +0000 @@ -2,6 +2,8 @@ A rusty plist parser. +Many features from previous versions are now hidden behind the `enable_unstable_features_that_may_break_with_minor_version_bumps` feature. These will break in minor version releases after the 1.0 release. If you really really must use them you should specify a tilde requirement e.g. `plist = "~1.0.3"` in you `Cargo.toml` so that the plist crate is not automatically updated to version 1.1. + [![Build Status](https://travis-ci.org/ebarnard/rust-plist.svg?branch=master)](https://travis-ci.org/ebarnard/rust-plist) [Documentation](https://docs.rs/plist/) diff -Nru rust-plist-0.4.2/rustfmt.toml rust-plist-0.5.4/rustfmt.toml --- rust-plist-0.4.2/rustfmt.toml 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/rustfmt.toml 2019-07-02 20:19:28.000000000 +0000 @@ -0,0 +1 @@ +merge_imports = true diff -Nru rust-plist-0.4.2/src/date.rs rust-plist-0.5.4/src/date.rs --- rust-plist-0.4.2/src/date.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/src/date.rs 2019-07-26 09:36:25.000000000 +0000 @@ -1,14 +1,24 @@ use humantime; -use std::fmt; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -/// A UTC timestamp. Used for serialization to and from the plist date type. -#[derive(Clone, Copy, Hash, PartialEq)] +use std::{ + fmt, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +/// A UTC timestamp used for serialization to and from the plist date type. +/// +/// Note that while this type implements `Serialize` and `Deserialize` it will behave strangely if +/// used with serializers from outside this crate. +#[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct Date { inner: SystemTime, } +pub(crate) struct InfiniteOrNanDate; + impl Date { + /// The unix timestamp of the plist epoch. + const PLIST_EPOCH_UNIX_TIMESTAMP: Duration = Duration::from_secs(978_307_200); + pub(crate) fn from_rfc3339(date: &str) -> Result { Ok(Date { inner: humantime::parse_rfc3339(date).map_err(|_| ())?, @@ -19,14 +29,14 @@ format!("{}", humantime::format_rfc3339(self.inner)) } - pub(crate) fn from_seconds_since_plist_epoch(timestamp: f64) -> Result { + pub(crate) fn from_seconds_since_plist_epoch( + timestamp: f64, + ) -> Result { // `timestamp` is the number of seconds since the plist epoch of 1/1/2001 00:00:00. - // `PLIST_EPOCH_UNIX_TIMESTAMP` is the unix timestamp of the plist epoch. - const PLIST_EPOCH_UNIX_TIMESTAMP: u64 = 978_307_200; - let plist_epoch = UNIX_EPOCH + Duration::from_secs(PLIST_EPOCH_UNIX_TIMESTAMP); + let plist_epoch = UNIX_EPOCH + Date::PLIST_EPOCH_UNIX_TIMESTAMP; if !timestamp.is_finite() { - return Err(()); + return Err(InfiniteOrNanDate); } let is_negative = timestamp < 0.0; @@ -44,6 +54,20 @@ Ok(Date { inner }) } + + pub(crate) fn to_seconds_since_plist_epoch(&self) -> f64 { + // needed until #![feature(duration_float)] is stabilized + fn as_secs_f64(d: Duration) -> f64 { + const NANOS_PER_SEC: f64 = 1_000_000_000.00; + (d.as_secs() as f64) + f64::from(d.subsec_nanos()) / NANOS_PER_SEC + } + + let plist_epoch = UNIX_EPOCH + Date::PLIST_EPOCH_UNIX_TIMESTAMP; + match self.inner.duration_since(plist_epoch) { + Ok(dur_since_plist_epoch) => as_secs_f64(dur_since_plist_epoch), + Err(err) => -as_secs_f64(err.duration()), + } + } } impl fmt::Debug for Date { @@ -67,11 +91,13 @@ #[cfg(feature = "serde")] pub mod serde_impls { - use serde::de::{Deserialize, Deserializer, Error, Unexpected, Visitor}; - use serde::ser::{Serialize, Serializer}; + use serde::{ + de::{Deserialize, Deserializer, Error, Unexpected, Visitor}, + ser::{Serialize, Serializer}, + }; use std::fmt; - use Date; + use crate::Date; pub const DATE_NEWTYPE_STRUCT_NAME: &str = "PLIST-DATE"; diff -Nru rust-plist-0.4.2/src/de.rs rust-plist-0.5.4/src/de.rs --- rust-plist-0.4.2/src/de.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/src/de.rs 2020-01-21 20:38:04.000000000 +0000 @@ -1,26 +1,28 @@ use serde::de; -use std::fmt::Display; -use std::fs::File; -use std::io::{BufReader, Read, Seek}; -use std::iter::Peekable; -use std::path::Path; - -use stream::{self, Event}; -use {u64_to_usize, Error}; +use std::{ + fmt::Display, + fs::File, + io::{BufReader, Cursor, Read, Seek}, + iter::Peekable, + mem, + path::Path, +}; + +use crate::{ + error::{self, Error, ErrorKind, EventKind}, + stream::{self, Event}, + u64_to_usize, +}; macro_rules! expect { - ($next:expr, $pat:pat) => { - match $next { - Some(Ok(v @ $pat)) => v, - None => return Err(Error::UnexpectedEof), - _ => return Err(event_mismatch_error()), - } - }; - ($next:expr, $pat:pat => $save:expr) => { + ($next:expr, $kind:expr) => { match $next { - Some(Ok($pat)) => $save, - None => return Err(Error::UnexpectedEof), - _ => return Err(event_mismatch_error()), + 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()), } }; } @@ -28,29 +30,33 @@ macro_rules! try_next { ($next:expr) => { match $next { - Some(Ok(v)) => v, - Some(Err(_)) => return Err(event_mismatch_error()), - None => return Err(Error::UnexpectedEof), + Some(Ok(event)) => event, + Some(Err(err)) => return Err(err)?, + None => return Err(ErrorKind::UnexpectedEndOfEventStream.without_position())?, } }; } -fn event_mismatch_error() -> Error { - Error::InvalidData -} - +#[doc(hidden)] impl de::Error for Error { fn custom(msg: T) -> Self { - Error::Serde(msg.to_string()) + ErrorKind::Serde(msg.to_string()).without_position() } } +enum OptionMode { + Root, + StructField, + Explicit, +} + /// A structure that deserializes plist event streams into Rust values. pub struct Deserializer where I: IntoIterator>, { events: Peekable<::IntoIter>, + option_mode: OptionMode, } impl Deserializer @@ -60,8 +66,20 @@ pub fn new(iter: I) -> Deserializer { Deserializer { events: iter.into_iter().peekable(), + option_mode: OptionMode::Root, } } + + fn with_option_mode) -> Result>( + &mut self, + option_mode: OptionMode, + f: F, + ) -> Result { + let prev_option_mode = mem::replace(&mut self.option_mode, option_mode); + let ret = f(&mut *self); + self.option_mode = prev_option_mode; + ret + } } impl<'de, 'a, I> de::Deserializer<'de> for &'a mut Deserializer @@ -70,7 +88,7 @@ { type Error = Error; - fn deserialize_any(self, visitor: V) -> Result + fn deserialize_any(self, visitor: V) -> Result where V: de::Visitor<'de>, { @@ -78,26 +96,37 @@ Event::StartArray(len) => { let len = len.and_then(u64_to_usize); let ret = visitor.visit_seq(MapAndSeqAccess::new(self, false, len))?; - expect!(self.events.next(), Event::EndArray); + expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } - Event::EndArray => Err(event_mismatch_error()), - Event::StartDictionary(len) => { let len = len.and_then(u64_to_usize); let ret = visitor.visit_map(MapAndSeqAccess::new(self, false, len))?; - expect!(self.events.next(), Event::EndDictionary); + expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } - Event::EndDictionary => Err(event_mismatch_error()), + event @ Event::EndCollection => Err(error::unexpected_event_type( + EventKind::ValueOrStartCollection, + &event, + )), + + Event::Boolean(v) => visitor.visit_bool(v), + Event::Data(v) => visitor.visit_byte_buf(v), + Event::Date(v) => visitor.visit_string(v.to_rfc3339()), + Event::Integer(v) => { + if let Some(v) = v.as_unsigned() { + visitor.visit_u64(v) + } else if let Some(v) = v.as_signed() { + visitor.visit_i64(v) + } else { + unreachable!() + } + } + Event::Real(v) => visitor.visit_f64(v), + Event::String(v) => visitor.visit_string(v), + Event::Uid(v) => visitor.visit_u64(v.get()), - Event::BooleanValue(v) => visitor.visit_bool(v), - Event::DataValue(v) => visitor.visit_byte_buf(v), - Event::DateValue(v) => visitor.visit_string(v.to_rfc3339()), - Event::IntegerValue(v) if v.is_positive() => visitor.visit_u64(v as u64), - Event::IntegerValue(v) => visitor.visit_i64(v as i64), - Event::RealValue(v) => visitor.visit_f64(v), - Event::StringValue(v) => visitor.visit_string(v), + Event::__Nonexhaustive => unreachable!(), } } @@ -107,39 +136,54 @@ tuple_struct tuple ignored_any identifier } - fn deserialize_unit(self, visitor: V) -> Result + fn deserialize_unit(self, visitor: V) -> Result where V: de::Visitor<'de>, { - expect!(self.events.next(), Event::StringValue(_)); + expect!(self.events.next(), EventKind::String); visitor.visit_unit() } - fn deserialize_option(self, visitor: V) -> Result + fn deserialize_option(self, visitor: V) -> Result where V: de::Visitor<'de>, { - expect!(self.events.next(), Event::StartDictionary(_)); - - let ret = match try_next!(self.events.next()) { - Event::StringValue(ref s) if &s[..] == "None" => { - expect!(self.events.next(), Event::StringValue(_)); - visitor.visit_none::()? + 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)) + } } - Event::StringValue(ref s) if &s[..] == "Some" => visitor.visit_some(&mut *self)?, - _ => return Err(event_mismatch_error()), - }; + OptionMode::StructField => { + // None struct values are ignored so if we're here the value must be Some. + self.with_option_mode(OptionMode::Explicit, |this| Ok(visitor.visit_some(this)?)) + } + OptionMode::Explicit => { + expect!(self.events.next(), EventKind::StartDictionary); - expect!(self.events.next(), Event::EndDictionary); + 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))?, + }; - Ok(ret) + expect!(self.events.next(), EventKind::EndCollection); + + Ok(ret) + } + } } fn deserialize_newtype_struct( self, _name: &'static str, visitor: V, - ) -> Result + ) -> Result where V: de::Visitor<'de>, { @@ -151,13 +195,13 @@ _name: &'static str, _fields: &'static [&'static str], visitor: V, - ) -> Result + ) -> Result where V: de::Visitor<'de>, { - expect!(self.events.next(), Event::StartDictionary(_)); + expect!(self.events.next(), EventKind::StartDictionary); let ret = visitor.visit_map(MapAndSeqAccess::new(self, true, None))?; - expect!(self.events.next(), Event::EndDictionary); + expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } @@ -166,13 +210,13 @@ _enum: &'static str, _variants: &'static [&'static str], visitor: V, - ) -> Result + ) -> Result where V: de::Visitor<'de>, { - expect!(self.events.next(), Event::StartDictionary(_)); + expect!(self.events.next(), EventKind::StartDictionary); let ret = visitor.visit_enum(&mut *self)?; - expect!(self.events.next(), Event::EndDictionary); + expect!(self.events.next(), EventKind::EndCollection); Ok(ret) } } @@ -184,7 +228,7 @@ type Error = Error; type Variant = Self; - fn variant_seed(self, seed: V) -> Result<(V::Value, Self), Self::Error> + fn variant_seed(self, seed: V) -> Result<(V::Value, Self), Error> where V: de::DeserializeSeed<'de>, { @@ -198,111 +242,34 @@ { type Error = Error; - fn unit_variant(self) -> Result<(), Self::Error> { - <() as de::Deserialize>::deserialize(self) + fn unit_variant(self) -> Result<(), Error> { + de::Deserialize::deserialize(self) } - fn newtype_variant_seed(self, seed: T) -> Result + 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 + fn tuple_variant(self, len: usize, visitor: V) -> Result where V: de::Visitor<'de>, { - ::deserialize_tuple(self, len, visitor) + de::Deserializer::deserialize_tuple(self, len, visitor) } fn struct_variant( self, fields: &'static [&'static str], visitor: V, - ) -> Result + ) -> Result where V: de::Visitor<'de>, { let name = ""; - ::deserialize_struct(self, name, fields, visitor) - } -} - -pub struct StructValueDeserializer<'a, I: 'a> -where - I: IntoIterator>, -{ - de: &'a mut Deserializer, -} - -impl<'de, 'a, I> de::Deserializer<'de> for StructValueDeserializer<'a, I> -where - I: IntoIterator>, -{ - type Error = Error; - - fn deserialize_any(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - self.de.deserialize_any(visitor) - } - - forward_to_deserialize_any! { - bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string - seq bytes byte_buf map unit_struct - tuple_struct tuple ignored_any identifier - } - - fn deserialize_unit(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - self.de.deserialize_unit(visitor) - } - - fn deserialize_option(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - // None struct values are ignored so if we're here the value must be Some. - visitor.visit_some(self.de) - } - - fn deserialize_newtype_struct( - self, - name: &'static str, - visitor: V, - ) -> Result - where - V: de::Visitor<'de>, - { - self.de.deserialize_newtype_struct(name, visitor) - } - - fn deserialize_struct( - self, - name: &'static str, - fields: &'static [&'static str], - visitor: V, - ) -> Result - where - V: de::Visitor<'de>, - { - self.de.deserialize_struct(name, fields, visitor) - } - - fn deserialize_enum( - self, - enum_: &'static str, - variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: de::Visitor<'de>, - { - self.de.deserialize_enum(enum_, variants, visitor) + de::Deserializer::deserialize_struct(self, name, fields, visitor) } } @@ -338,16 +305,18 @@ { type Error = Error; - fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + fn next_element_seed(&mut self, seed: T) -> Result, Error> where T: de::DeserializeSeed<'de>, { - if let Some(&Ok(Event::EndArray)) = self.de.events.peek() { + if let Some(&Ok(Event::EndCollection)) = self.de.events.peek() { return Ok(None); } self.remaining = self.remaining.map(|r| r.saturating_sub(1)); - seed.deserialize(&mut *self.de).map(Some) + self.de + .with_option_mode(OptionMode::Explicit, |this| seed.deserialize(this)) + .map(Some) } fn size_hint(&self) -> Option { @@ -361,27 +330,31 @@ { type Error = Error; - fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> + fn next_key_seed(&mut self, seed: K) -> Result, Error> where K: de::DeserializeSeed<'de>, { - if let Some(&Ok(Event::EndDictionary)) = self.de.events.peek() { + if let Some(&Ok(Event::EndCollection)) = self.de.events.peek() { return Ok(None); } self.remaining = self.remaining.map(|r| r.saturating_sub(1)); - seed.deserialize(&mut *self.de).map(Some) + self.de + .with_option_mode(OptionMode::Explicit, |this| seed.deserialize(this)) + .map(Some) } - fn next_value_seed(&mut self, seed: V) -> Result + fn next_value_seed(&mut self, seed: V) -> Result where V: de::DeserializeSeed<'de>, { - if self.is_struct { - seed.deserialize(StructValueDeserializer { de: &mut *self.de }) + let option_mode = if self.is_struct { + OptionMode::StructField } else { - seed.deserialize(&mut *self.de) - } + OptionMode::Explicit + }; + self.de + .with_option_mode(option_mode, |this| Ok(seed.deserialize(this)?)) } fn size_hint(&self) -> Option { @@ -389,20 +362,26 @@ } } +/// 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)?; + 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 file of any encoding. +/// 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 file. +/// 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); diff -Nru rust-plist-0.4.2/src/dictionary.rs rust-plist-0.5.4/src/dictionary.rs --- rust-plist-0.4.2/src/dictionary.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/src/dictionary.rs 2019-07-26 10:28:28.000000000 +0000 @@ -0,0 +1,696 @@ +//! A map of String to plist::Value. +//! +//! The map is currently backed by an [`IndexMap`]. This may be changed in a future minor release. +//! +//! [`IndexMap`]: https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html + +//use serde::{de, ser}; +use indexmap::{map, IndexMap}; +use std::{ + fmt::{self, Debug}, + iter::FromIterator, + ops, +}; + +use crate::Value; + +/// Represents a plist dictionary type. +pub struct Dictionary { + map: IndexMap, +} + +impl Dictionary { + /// Makes a new empty `Dictionary`. + #[inline] + pub fn new() -> Self { + Dictionary { + map: IndexMap::new(), + } + } + + /// Clears the dictionary, removing all values. + #[inline] + pub fn clear(&mut self) { + self.map.clear() + } + + /// Returns a reference to the value corresponding to the key. + #[inline] + pub fn get(&self, key: &str) -> Option<&Value> { + self.map.get(key) + } + + /// Returns true if the dictionary contains a value for the specified key. + #[inline] + pub fn contains_key(&self, key: &str) -> bool { + self.map.contains_key(key) + } + + /// Returns a mutable reference to the value corresponding to the key. + #[inline] + pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> { + self.map.get_mut(key) + } + + /// Inserts a key-value pair into the dictionary. + /// + /// If the dictionary did not have this key present, `None` is returned. + /// + /// If the dictionary did have this key present, the value is updated, and the old value is + /// returned. + #[inline] + pub fn insert(&mut self, k: String, v: Value) -> Option { + self.map.insert(k, v) + } + + /// Removes a key from the dictionary, returning the value at the key if the key was previously + /// in the dictionary. + #[inline] + pub fn remove(&mut self, key: &str) -> Option { + self.map.remove(key) + } + + /// Gets the given key's corresponding entry in the dictionary for in-place manipulation. + // Entry functionality is unstable until I can figure out how to use either Cow or + // T: AsRef + Into + #[cfg(any( + test, + feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" + ))] + pub fn entry(&mut self, key: S) -> Entry + where + S: Into, + { + match self.map.entry(key.into()) { + map::Entry::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant }), + map::Entry::Occupied(occupied) => Entry::Occupied(OccupiedEntry { occupied }), + } + } + + /// Returns the number of elements in the dictionary. + #[inline] + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns true if the dictionary contains no elements. + #[inline] + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + /// Gets an iterator over the entries of the dictionary. + #[inline] + pub fn iter(&self) -> Iter { + Iter { + iter: self.map.iter(), + } + } + + /// Gets a mutable iterator over the entries of the dictionary. + #[inline] + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + iter: self.map.iter_mut(), + } + } + + /// Gets an iterator over the keys of the dictionary. + #[inline] + pub fn keys(&self) -> Keys { + Keys { + iter: self.map.keys(), + } + } + + /// Gets an iterator over the values of the dictionary. + #[inline] + pub fn values(&self) -> Values { + Values { + iter: self.map.values(), + } + } + + /// Gets an iterator over mutable values of the dictionary. + #[inline] + pub fn values_mut(&mut self) -> ValuesMut { + ValuesMut { + iter: self.map.values_mut(), + } + } +} + +impl Default for Dictionary { + #[inline] + fn default() -> Self { + Dictionary { + map: Default::default(), + } + } +} + +impl Clone for Dictionary { + #[inline] + fn clone(&self) -> Self { + Dictionary { + map: self.map.clone(), + } + } +} + +impl PartialEq for Dictionary { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.map.eq(&other.map) + } +} + +/// Access an element of this dictionary. Panics if the given key is not present in the dictionary. +/// +/// ``` +/// # use plist::Value; +/// # +/// # let val = &Value::String("".to_owned()); +/// # let _ = +/// match *val { +/// Value::Array(ref arr) => arr[0].as_string(), +/// Value::Dictionary(ref dict) => dict["type"].as_string(), +/// Value::String(ref s) => Some(s.as_str()), +/// _ => None, +/// } +/// # ; +/// ``` +impl<'a> ops::Index<&'a str> for Dictionary { + type Output = Value; + + fn index(&self, index: &str) -> &Value { + self.map.index(index) + } +} + +/// Mutably access an element of this dictionary. Panics if the given key is not present in the +/// dictionary. +/// +/// ``` +/// # let mut dict = plist::Dictionary::new(); +/// # dict.insert("key".to_owned(), plist::Value::Boolean(false)); +/// # +/// dict["key"] = "value".into(); +/// ``` +impl<'a> ops::IndexMut<&'a str> for Dictionary { + fn index_mut(&mut self, index: &str) -> &mut Value { + self.map.get_mut(index).expect("no entry found for key") + } +} + +impl Debug for Dictionary { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.map.fmt(formatter) + } +} + +/*impl ser::Serialize for Dictionary { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + use serde::ser::SerializeMap; + let mut map = try!(serializer.serialize_map(Some(self.len()))); + for (k, v) in self { + try!(map.serialize_key(k)); + try!(map.serialize_value(v)); + } + map.end() + } +} + +impl<'de> de::Deserialize<'de> for Dictionary { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = Dictionary; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map") + } + + #[inline] + fn visit_unit(self) -> Result + where + E: de::Error, + { + Ok(Dictionary::new()) + } + + #[inline] + fn visit_map(self, mut visitor: V) -> Result + where + V: de::MapAccess<'de>, + { + let mut values = Dictionary::new(); + + while let Some((key, value)) = try!(visitor.next_entry()) { + values.insert(key, value); + } + + Ok(values) + } + } + + deserializer.deserialize_map(Visitor) + } +}*/ + +impl FromIterator<(String, Value)> for Dictionary { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + Dictionary { + map: FromIterator::from_iter(iter), + } + } +} + +impl Extend<(String, Value)> for Dictionary { + fn extend(&mut self, iter: T) + where + T: IntoIterator, + { + self.map.extend(iter); + } +} + +macro_rules! delegate_iterator { + (($name:ident $($generics:tt)*) => $item:ty) => { + impl $($generics)* Iterator for $name $($generics)* { + type Item = $item; + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + } + + /*impl $($generics)* DoubleEndedIterator for $name $($generics)* { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back() + } + }*/ + + impl $($generics)* ExactSizeIterator for $name $($generics)* { + #[inline] + fn len(&self) -> usize { + self.iter.len() + } + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +/// A view into a single entry in a dictionary, which may either be vacant or occupied. +/// This enum is constructed from the [`entry`] method on [`Dictionary`]. +/// +/// [`entry`]: struct.Dictionary.html#method.entry +/// [`Dictionary`]: struct.Dictionary.html +#[cfg(any( + test, + feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" +))] +pub enum Entry<'a> { + /// A vacant Entry. + Vacant(VacantEntry<'a>), + /// An occupied Entry. + Occupied(OccupiedEntry<'a>), +} + +/// A vacant Entry. It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +#[cfg(any( + test, + feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" +))] +pub struct VacantEntry<'a> { + vacant: map::VacantEntry<'a, String, Value>, +} + +/// An occupied Entry. It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +#[cfg(any( + test, + feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" +))] +pub struct OccupiedEntry<'a> { + occupied: map::OccupiedEntry<'a, String, Value>, +} + +#[cfg(any( + test, + feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" +))] +impl<'a> Entry<'a> { + /// Returns a reference to this entry's key. + /// + /// # Examples + /// + /// ``` + /// let mut dict = plist::Dictionary::new(); + /// assert_eq!(dict.entry("serde").key(), &"serde"); + /// ``` + pub fn key(&self) -> &String { + match *self { + Entry::Vacant(ref e) => e.key(), + Entry::Occupied(ref e) => e.key(), + } + } + + /// Ensures a value is in the entry by inserting the default if empty, and returns a mutable + /// reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// let mut dict = plist::Dictionary::new(); + /// dict.entry("serde").or_insert(12.into()); + /// + /// assert_eq!(dict["serde"], 12.into()); + /// ``` + pub fn or_insert(self, default: Value) -> &'a mut Value { + match self { + Entry::Vacant(entry) => entry.insert(default), + Entry::Occupied(entry) => entry.into_mut(), + } + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// let mut dict = plist::Dictionary::new(); + /// dict.entry("serde").or_insert_with(|| "hoho".into()); + /// + /// assert_eq!(dict["serde"], "hoho".into()); + /// ``` + pub fn or_insert_with(self, default: F) -> &'a mut Value + where + F: FnOnce() -> Value, + { + match self { + Entry::Vacant(entry) => entry.insert(default()), + Entry::Occupied(entry) => entry.into_mut(), + } + } +} + +#[cfg(any( + test, + feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" +))] +impl<'a> VacantEntry<'a> { + /// Gets a reference to the key that would be used when inserting a value through the + /// VacantEntry. + /// + /// # Examples + /// + /// ``` + /// use plist::dictionary::Entry; + /// + /// let mut dict = plist::Dictionary::new(); + /// + /// match dict.entry("serde") { + /// Entry::Vacant(vacant) => assert_eq!(vacant.key(), &"serde"), + /// Entry::Occupied(_) => unimplemented!(), + /// } + /// ``` + #[inline] + pub fn key(&self) -> &String { + self.vacant.key() + } + + /// Sets the value of the entry with the VacantEntry's key, and returns a mutable reference + /// to it. + /// + /// # Examples + /// + /// ``` + /// use plist::dictionary::Entry; + /// + /// let mut dict = plist::Dictionary::new(); + /// + /// match dict.entry("serde") { + /// Entry::Vacant(vacant) => vacant.insert("hoho".into()), + /// Entry::Occupied(_) => unimplemented!(), + /// }; + /// ``` + #[inline] + pub fn insert(self, value: Value) -> &'a mut Value { + self.vacant.insert(value) + } +} + +#[cfg(any( + test, + feature = "enable_unstable_features_that_may_break_with_minor_version_bumps" +))] +impl<'a> OccupiedEntry<'a> { + /// Gets a reference to the key in the entry. + /// + /// # Examples + /// + /// ``` + /// use plist::dictionary::Entry; + /// + /// let mut dict = plist::Dictionary::new(); + /// dict.insert("serde".to_owned(), 12.into()); + /// + /// match dict.entry("serde") { + /// Entry::Occupied(occupied) => assert_eq!(occupied.key(), &"serde"), + /// Entry::Vacant(_) => unimplemented!(), + /// } + /// ``` + #[inline] + pub fn key(&self) -> &String { + self.occupied.key() + } + + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use plist::Value; + /// use plist::dictionary::Entry; + /// + /// let mut dict = plist::Dictionary::new(); + /// dict.insert("serde".to_owned(), 12.into()); + /// + /// match dict.entry("serde") { + /// Entry::Occupied(occupied) => assert_eq!(occupied.get(), &Value::from(12)), + /// Entry::Vacant(_) => unimplemented!(), + /// } + /// ``` + #[inline] + pub fn get(&self) -> &Value { + self.occupied.get() + } + + /// Gets a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use plist::Value; + /// use plist::dictionary::Entry; + /// + /// let mut dict = plist::Dictionary::new(); + /// dict.insert("serde".to_owned(), Value::Array(vec![1.into(), 2.into(), 3.into()])); + /// + /// match dict.entry("serde") { + /// Entry::Occupied(mut occupied) => { + /// occupied.get_mut().as_array_mut().unwrap().push(4.into()); + /// } + /// Entry::Vacant(_) => unimplemented!(), + /// } + /// + /// assert_eq!(dict["serde"].as_array().unwrap().len(), 4); + /// ``` + #[inline] + pub fn get_mut(&mut self) -> &mut Value { + self.occupied.get_mut() + } + + /// Converts the entry into a mutable reference to its value. + /// + /// # Examples + /// + /// ``` + /// use plist::Value; + /// use plist::dictionary::Entry; + /// + /// let mut dict = plist::Dictionary::new(); + /// dict.insert("serde".to_owned(), Value::Array(vec![1.into(), 2.into(), 3.into()])); + /// + /// match dict.entry("serde") { + /// Entry::Occupied(mut occupied) => { + /// occupied.into_mut().as_array_mut().unwrap().push(4.into()); + /// } + /// Entry::Vacant(_) => unimplemented!(), + /// } + /// + /// assert_eq!(dict["serde"].as_array().unwrap().len(), 4); + /// ``` + #[inline] + pub fn into_mut(self) -> &'a mut Value { + self.occupied.into_mut() + } + + /// Sets the value of the entry with the `OccupiedEntry`'s key, and returns + /// the entry's old value. + /// + /// # Examples + /// + /// ``` + /// use plist::Value; + /// use plist::dictionary::Entry; + /// + /// let mut dict = plist::Dictionary::new(); + /// dict.insert("serde".to_owned(), 12.into()); + /// + /// match dict.entry("serde") { + /// Entry::Occupied(mut occupied) => { + /// assert_eq!(occupied.insert(13.into()), 12.into()); + /// assert_eq!(occupied.get(), &Value::from(13)); + /// } + /// Entry::Vacant(_) => unimplemented!(), + /// } + /// ``` + #[inline] + pub fn insert(&mut self, value: Value) -> Value { + self.occupied.insert(value) + } + + /// Takes the value of the entry out of the dictionary, and returns it. + /// + /// # Examples + /// + /// ``` + /// use plist::dictionary::Entry; + /// + /// let mut dict = plist::Dictionary::new(); + /// dict.insert("serde".to_owned(), 12.into()); + /// + /// match dict.entry("serde") { + /// Entry::Occupied(occupied) => assert_eq!(occupied.remove(), 12.into()), + /// Entry::Vacant(_) => unimplemented!(), + /// } + /// ``` + #[inline] + pub fn remove(self) -> Value { + self.occupied.remove() + } +} + +////////////////////////////////////////////////////////////////////////////// + +impl<'a> IntoIterator for &'a Dictionary { + type Item = (&'a String, &'a Value); + type IntoIter = Iter<'a>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + Iter { + iter: self.map.iter(), + } + } +} + +/// An iterator over a plist::Dictionary's entries. +pub struct Iter<'a> { + iter: IterImpl<'a>, +} + +type IterImpl<'a> = map::Iter<'a, String, Value>; + +delegate_iterator!((Iter<'a>) => (&'a String, &'a Value)); + +////////////////////////////////////////////////////////////////////////////// + +impl<'a> IntoIterator for &'a mut Dictionary { + type Item = (&'a String, &'a mut Value); + type IntoIter = IterMut<'a>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + IterMut { + iter: self.map.iter_mut(), + } + } +} + +/// A mutable iterator over a plist::Dictionary's entries. +pub struct IterMut<'a> { + iter: map::IterMut<'a, String, Value>, +} + +delegate_iterator!((IterMut<'a>) => (&'a String, &'a mut Value)); + +////////////////////////////////////////////////////////////////////////////// + +impl IntoIterator for Dictionary { + type Item = (String, Value); + type IntoIter = IntoIter; + #[inline] + fn into_iter(self) -> Self::IntoIter { + IntoIter { + iter: self.map.into_iter(), + } + } +} + +/// An owning iterator over a plist::Dictionary's entries. +pub struct IntoIter { + iter: map::IntoIter, +} + +delegate_iterator!((IntoIter) => (String, Value)); + +////////////////////////////////////////////////////////////////////////////// + +/// An iterator over a plist::Dictionary's keys. +pub struct Keys<'a> { + iter: map::Keys<'a, String, Value>, +} + +delegate_iterator!((Keys<'a>) => &'a String); + +////////////////////////////////////////////////////////////////////////////// + +/// An iterator over a plist::Dictionary's values. +pub struct Values<'a> { + iter: map::Values<'a, String, Value>, +} + +delegate_iterator!((Values<'a>) => &'a Value); + +////////////////////////////////////////////////////////////////////////////// + +/// A mutable iterator over a plist::Dictionary's values. +pub struct ValuesMut<'a> { + iter: map::ValuesMut<'a, String, Value>, +} + +delegate_iterator!((ValuesMut<'a>) => &'a mut Value); diff -Nru rust-plist-0.4.2/src/error.rs rust-plist-0.5.4/src/error.rs --- rust-plist-0.4.2/src/error.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/src/error.rs 2019-07-31 17:06:08.000000000 +0000 @@ -0,0 +1,204 @@ +use std::{error, fmt, io}; + +use crate::stream::Event; + +/// This type represents all possible errors that can occur when working with plist data. +#[derive(Debug)] +pub struct Error { + inner: Box, +} + +#[derive(Debug)] +pub(crate) struct ErrorImpl { + kind: ErrorKind, + file_position: Option, +} + +#[derive(Debug)] +pub(crate) enum ErrorKind { + UnexpectedEof, + UnexpectedEndOfEventStream, + UnexpectedEventType { + expected: EventKind, + found: EventKind, + }, + + // Xml format-specific errors + UnclosedXmlElement, + UnpairedXmlClosingTag, + UnexpectedXmlCharactersExpectedElement, + UnexpectedXmlOpeningTag, + UnknownXmlElement, + InvalidXmlSyntax, + InvalidXmlUtf8, + InvalidDataString, + InvalidDateString, + InvalidIntegerString, + InvalidRealString, + UidNotSupportedInXmlPlist, + + // Binary format-specific errors + ObjectTooLarge, + InvalidMagic, + InvalidTrailerObjectOffsetSize, // the size of byte offsets to objects in the object table + InvalidTrailerObjectReferenceSize, // the size of indices into the object table + InvalidObjectLength, + ObjectReferenceTooLarge, + ObjectOffsetTooLarge, + RecursiveObject, + NullObjectUnimplemented, + FillObjectUnimplemented, + IntegerOutOfRange, + InfiniteOrNanDate, + InvalidUtf8String, + InvalidUtf16String, + UnknownObjectType(u8), + + Io(io::Error), + Serde(String), +} + +#[derive(Debug)] +pub(crate) enum FilePosition { + LineColumn(u64, u64), + Offset(u64), +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub(crate) enum EventKind { + StartArray, + StartDictionary, + EndCollection, + Boolean, + Data, + Date, + Integer, + Real, + String, + Uid, + + ValueOrStartCollection, + DictionaryKeyOrEndCollection, +} + +impl Error { + /// Returns true if this error was caused by a failure to read or write bytes on an IO stream. + pub fn is_io(&self) -> bool { + self.as_io().is_some() + } + + /// Returns true if this error was caused by prematurely reaching the end of the input data. + pub fn is_eof(&self) -> bool { + if let ErrorKind::UnexpectedEof = self.inner.kind { + true + } else { + false + } + } + + /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO + /// stream. + pub fn as_io(&self) -> Option<&io::Error> { + if let ErrorKind::Io(err) = &self.inner.kind { + Some(err) + } else { + None + } + } + + /// Returns the underlying error if it was caused by a failure to read or write bytes on an IO + /// stream or `self` if it was not. + pub fn into_io(self) -> Result { + if let ErrorKind::Io(err) = self.inner.kind { + Ok(err) + } else { + Err(self) + } + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self.inner.kind { + ErrorKind::Io(err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.inner.kind, f) + } +} + +impl ErrorKind { + pub fn with_byte_offset(self, offset: u64) -> Error { + self.with_position(FilePosition::Offset(offset)) + } + + pub fn with_position(self, pos: FilePosition) -> Error { + Error { + inner: Box::new(ErrorImpl { + kind: self, + file_position: Some(pos), + }), + } + } + + pub fn without_position(self) -> Error { + Error { + inner: Box::new(ErrorImpl { + kind: self, + file_position: None, + }), + } + } +} + +impl EventKind { + pub fn of_event(event: &Event) -> EventKind { + match event { + Event::StartArray(_) => EventKind::StartArray, + Event::StartDictionary(_) => EventKind::StartDictionary, + Event::EndCollection => EventKind::EndCollection, + Event::Boolean(_) => EventKind::Boolean, + Event::Data(_) => EventKind::Data, + Event::Date(_) => EventKind::Date, + Event::Integer(_) => EventKind::Integer, + Event::Real(_) => EventKind::Real, + Event::String(_) => EventKind::String, + Event::Uid(_) => EventKind::Uid, + Event::__Nonexhaustive => unreachable!(), + } + } +} + +impl fmt::Display for EventKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + EventKind::StartArray => "StartArray", + EventKind::StartDictionary => "StartDictionary", + EventKind::EndCollection => "EndCollection", + EventKind::Boolean => "Boolean", + EventKind::Data => "Data", + EventKind::Date => "Date", + EventKind::Integer => "Integer", + EventKind::Real => "Real", + EventKind::String => "String", + EventKind::Uid => "Uid", + EventKind::ValueOrStartCollection => "value or start collection", + EventKind::DictionaryKeyOrEndCollection => "dictionary key or end collection", + } + .fmt(f) + } +} + +pub(crate) fn from_io_without_position(err: io::Error) -> Error { + ErrorKind::Io(err).without_position() +} + +pub(crate) fn unexpected_event_type(expected: EventKind, found: &Event) -> Error { + let found = EventKind::of_event(&found); + ErrorKind::UnexpectedEventType { expected, found }.without_position() +} diff -Nru rust-plist-0.4.2/src/integer.rs rust-plist-0.5.4/src/integer.rs --- rust-plist-0.4.2/src/integer.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/src/integer.rs 2019-07-31 17:02:02.000000000 +0000 @@ -0,0 +1,202 @@ +use std::{fmt, num::ParseIntError}; + +/// An integer that can be represented by either an `i64` or a `u64`. +#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Integer { + value: i128, +} + +impl Integer { + /// Returns the value as an `i64` if it can be represented by that type. + pub fn as_signed(self) -> Option { + 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()); + } +} diff -Nru rust-plist-0.4.2/src/lib.rs rust-plist-0.5.4/src/lib.rs --- rust-plist-0.4.2/src/lib.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/src/lib.rs 2020-01-21 20:38:04.000000000 +0000 @@ -8,7 +8,7 @@ //! //! ```toml //! [dependencies] -//! plist = "0.4" +//! plist = "0.5" //! ``` //! //! And put this in your crate root: @@ -19,16 +19,7 @@ //! //! ## Examples //! -//! ```rust -//! use plist::Value; -//! -//! let value = Value::from_file("tests/data/xml.plist").unwrap(); -//! -//! match value { -//! Value::Array(_array) => (), -//! _ => () -//! } -//! ``` +//! ### Using `serde` //! //! ```rust //! extern crate plist; @@ -40,30 +31,65 @@ //! # fn main() { //! #[derive(Deserialize)] //! #[serde(rename_all = "PascalCase")] -//! struct Info { +//! struct Book { +//! title: String, //! author: String, -//! height: f32, +//! excerpt: String, +//! copies_sold: u64, //! } //! -//! let info: Info = plist::from_file("tests/data/xml.plist").unwrap(); +//! 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. -extern crate base64; -extern crate byteorder; -extern crate humantime; -extern crate line_wrap; -extern crate xml as xml_rs; +pub mod dictionary; +#[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")] pub mod stream; +#[cfg(not(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps"))] +mod stream; mod date; +mod error; +mod integer; +mod uid; mod value; pub use date::Date; +pub use dictionary::Dictionary; +pub use error::Error; +pub use integer::Integer; +pub use uid::Uid; pub use value::Value; // Optional serde module @@ -74,54 +100,26 @@ 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_file, from_reader, from_reader_xml, Deserializer}; -#[cfg(feature = "serde")] -pub use self::ser::{to_writer_xml, Serializer}; +pub use self::{ + de::{from_bytes, from_file, from_reader, from_reader_xml}, + ser::{to_file_binary, to_file_xml, to_writer_binary, to_writer_xml}, +}; -use std::fmt; -use std::io; +#[cfg(all(test, feature = "serde"))] +#[macro_use] +extern crate serde_derive; -#[derive(Debug)] -pub enum Error { - InvalidData, - UnexpectedEof, - Io(io::Error), - Serde(String), -} - -impl ::std::error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::InvalidData => "invalid data", - Error::UnexpectedEof => "unexpected eof", - Error::Io(ref err) => err.description(), - Error::Serde(ref err) => &err, - } - } - - fn cause(&self) -> Option<&::std::error::Error> { - match *self { - Error::Io(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Io(ref err) => err.fmt(fmt), - _ => ::description(self).fmt(fmt), - } - } -} - -impl From for Error { - fn from(err: io::Error) -> Error { - Error::Io(err) - } -} +#[cfg(all(test, feature = "serde"))] +mod serde_tests; fn u64_to_usize(len_u64: u64) -> Option { let len = len_u64 as usize; diff -Nru rust-plist-0.4.2/src/serde_tests.rs rust-plist-0.5.4/src/serde_tests.rs --- rust-plist-0.4.2/src/serde_tests.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/src/serde_tests.rs 2019-07-31 17:01:47.000000000 +0000 @@ -0,0 +1,512 @@ +use serde::{de::DeserializeOwned, ser::Serialize}; +use std::{collections::BTreeMap, fmt::Debug, time::SystemTime}; + +use crate::{ + stream::{private::Sealed, Event, Writer}, + Date, Deserializer, Error, Integer, Serializer, Uid, +}; + +struct VecWriter { + events: Vec, +} + +impl VecWriter { + pub fn new() -> VecWriter { + VecWriter { events: Vec::new() } + } + + pub fn into_inner(self) -> Vec { + self.events + } +} + +impl Writer for VecWriter { + fn write_start_array(&mut self, len: Option) -> Result<(), Error> { + self.events.push(Event::StartArray(len)); + Ok(()) + } + + fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error> { + self.events.push(Event::StartDictionary(len)); + Ok(()) + } + + fn write_end_collection(&mut self) -> Result<(), Error> { + self.events.push(Event::EndCollection); + Ok(()) + } + + fn write_boolean(&mut self, value: bool) -> Result<(), Error> { + self.events.push(Event::Boolean(value)); + Ok(()) + } + + fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { + self.events.push(Event::Data(value.to_owned())); + Ok(()) + } + + fn write_date(&mut self, value: Date) -> Result<(), Error> { + self.events.push(Event::Date(value)); + Ok(()) + } + + fn write_integer(&mut self, value: Integer) -> Result<(), Error> { + self.events.push(Event::Integer(value)); + Ok(()) + } + + fn write_real(&mut self, value: f64) -> Result<(), Error> { + self.events.push(Event::Real(value)); + Ok(()) + } + + fn write_string(&mut self, value: &str) -> Result<(), Error> { + self.events.push(Event::String(value.to_owned())); + Ok(()) + } + + fn write_uid(&mut self, value: Uid) -> Result<(), Error> { + self.events.push(Event::Uid(value)); + Ok(()) + } +} + +impl Sealed for VecWriter {} + +fn new_serializer() -> Serializer { + Serializer::new(VecWriter::new()) +} + +fn new_deserializer(events: Vec) -> Deserializer>> { + let result_events = events.into_iter().map(Ok).collect(); + Deserializer::new(result_events) +} + +fn assert_roundtrip(obj: T, comparison: Option<&[Event]>) +where + T: Debug + DeserializeOwned + PartialEq + Serialize, +{ + let mut se = new_serializer(); + + obj.serialize(&mut se).unwrap(); + + let events = se.into_inner().into_inner(); + + if let Some(comparison) = comparison { + assert_eq!(&events[..], comparison); + } + + let mut de = new_deserializer(events); + + let new_obj = T::deserialize(&mut de).unwrap(); + + assert_eq!(new_obj, obj); +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +enum Animal { + Cow, + Dog(DogOuter), + Frog(Result, Option>), + Cat { + age: Integer, + name: String, + firmware: Option>, + }, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct DogOuter { + inner: Vec, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct DogInner { + a: (), + b: usize, + c: Vec, + d: Option, +} + +#[test] +fn cow() { + let cow = Animal::Cow; + + let comparison = &[ + Event::StartDictionary(Some(1)), + Event::String("Cow".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + ]; + + assert_roundtrip(cow, Some(comparison)); +} + +#[test] +fn dog() { + let dog = Animal::Dog(DogOuter { + inner: vec![DogInner { + a: (), + b: 12, + c: vec!["a".to_string(), "b".to_string()], + d: Some(Uid::new(42)), + }], + }); + + let comparison = &[ + Event::StartDictionary(Some(1)), + Event::String("Dog".to_owned()), + Event::StartDictionary(None), + Event::String("inner".to_owned()), + Event::StartArray(Some(1)), + Event::StartDictionary(None), + Event::String("a".to_owned()), + Event::String("".to_owned()), + Event::String("b".to_owned()), + Event::Integer(12.into()), + Event::String("c".to_owned()), + Event::StartArray(Some(2)), + Event::String("a".to_owned()), + Event::String("b".to_owned()), + Event::EndCollection, + Event::String("d".to_owned()), + Event::Uid(Uid::new(42)), + Event::EndCollection, + Event::EndCollection, + Event::EndCollection, + Event::EndCollection, + ]; + + assert_roundtrip(dog, Some(comparison)); +} + +#[test] +fn frog() { + let frog = Animal::Frog( + Ok("hello".to_owned()), + Some(vec![1.0, 2.0, 3.14159, 0.000000001, 1.27e31]), + ); + + let comparison = &[ + Event::StartDictionary(Some(1)), + Event::String("Frog".to_owned()), + Event::StartArray(Some(2)), + Event::StartDictionary(Some(1)), + Event::String("Ok".to_owned()), + Event::String("hello".to_owned()), + Event::EndCollection, + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::StartArray(Some(5)), + Event::Real(1.0), + Event::Real(2.0), + Event::Real(3.14159), + Event::Real(0.000000001), + Event::Real(1.27e31), + Event::EndCollection, + Event::EndCollection, + Event::EndCollection, + Event::EndCollection, + ]; + + assert_roundtrip(frog, Some(comparison)); +} + +#[test] +fn cat_with_firmware() { + let cat = Animal::Cat { + age: 12.into(), + name: "Paws".to_owned(), + firmware: Some(vec![0, 1, 2, 3, 4, 5, 6, 7, 8]), + }; + + let comparison = &[ + Event::StartDictionary(Some(1)), + Event::String("Cat".to_owned()), + Event::StartDictionary(None), + Event::String("age".to_owned()), + Event::Integer(12.into()), + Event::String("name".to_owned()), + Event::String("Paws".to_owned()), + Event::String("firmware".to_owned()), + Event::StartArray(Some(9)), + Event::Integer(0.into()), + Event::Integer(1.into()), + Event::Integer(2.into()), + Event::Integer(3.into()), + Event::Integer(4.into()), + Event::Integer(5.into()), + Event::Integer(6.into()), + Event::Integer(7.into()), + Event::Integer(8.into()), + Event::EndCollection, + Event::EndCollection, + Event::EndCollection, + ]; + + assert_roundtrip(cat, Some(comparison)); +} + +#[test] +fn cat_without_firmware() { + let cat = Animal::Cat { + age: Integer::from(-12), + name: "Paws".to_owned(), + firmware: None, + }; + + let comparison = &[ + Event::StartDictionary(Some(1)), + Event::String("Cat".to_owned()), + Event::StartDictionary(None), + Event::String("age".to_owned()), + Event::Integer(Integer::from(-12)), + Event::String("name".to_owned()), + Event::String("Paws".to_owned()), + Event::EndCollection, + Event::EndCollection, + ]; + + assert_roundtrip(cat, Some(comparison)); +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct NewtypeStruct(NewtypeInner); + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct NewtypeInner(u8, u8, u8); + +#[test] +fn newtype_struct() { + let newtype = NewtypeStruct(NewtypeInner(34, 32, 13)); + + let comparison = &[ + Event::StartArray(Some(3)), + Event::Integer(34.into()), + Event::Integer(32.into()), + Event::Integer(13.into()), + Event::EndCollection, + ]; + + assert_roundtrip(newtype, Some(comparison)); +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct TypeWithOptions { + a: Option, + b: Option>, + c: Option>, +} + +#[test] +fn type_with_options() { + let inner = TypeWithOptions { + a: None, + b: Some(Some(12)), + c: None, + }; + + let obj = TypeWithOptions { + a: Some("hello".to_owned()), + b: Some(None), + c: Some(Box::new(inner)), + }; + + let comparison = &[ + Event::StartDictionary(None), + Event::String("a".to_owned()), + Event::String("hello".to_owned()), + Event::String("b".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("None".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + Event::String("c".to_owned()), + Event::StartDictionary(None), + Event::String("b".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::Integer(12.into()), + Event::EndCollection, + Event::EndCollection, + Event::EndCollection, + ]; + + assert_roundtrip(obj, Some(comparison)); +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct TypeWithDate { + a: Option, + b: Option, +} + +#[test] +fn type_with_date() { + let date: Date = SystemTime::now().into(); + + let obj = TypeWithDate { + a: Some(28), + b: Some(date.clone()), + }; + + let comparison = &[ + Event::StartDictionary(None), + Event::String("a".to_owned()), + Event::Integer(28.into()), + Event::String("b".to_owned()), + Event::Date(date), + Event::EndCollection, + ]; + + assert_roundtrip(obj, Some(comparison)); +} + +#[test] +fn option_some() { + let obj = Some(12); + + let comparison = &[Event::Integer(12.into())]; + + assert_roundtrip(obj, Some(comparison)); +} + +#[test] +fn option_none() { + let obj: Option = None; + + let comparison = &[]; + + assert_roundtrip(obj, Some(comparison)); +} + +#[test] +fn option_some_some() { + let obj = Some(Some(12)); + + let comparison = &[ + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::Integer(12.into()), + Event::EndCollection, + ]; + + assert_roundtrip(obj, Some(comparison)); +} + +#[test] +fn option_some_none() { + let obj: Option> = Some(None); + + let comparison = &[ + Event::StartDictionary(Some(1)), + Event::String("None".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + ]; + + assert_roundtrip(obj, Some(comparison)); +} + +#[test] +fn option_dictionary_values() { + let mut obj = BTreeMap::new(); + obj.insert("a".to_owned(), None); + obj.insert("b".to_owned(), Some(None)); + obj.insert("c".to_owned(), Some(Some(144))); + + let comparison = &[ + Event::StartDictionary(Some(3)), + Event::String("a".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("None".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + Event::String("b".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("None".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + Event::EndCollection, + Event::String("c".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::Integer(144.into()), + Event::EndCollection, + Event::EndCollection, + Event::EndCollection, + ]; + + assert_roundtrip(obj, Some(comparison)); +} + +#[test] +fn option_dictionary_keys() { + let mut obj = BTreeMap::new(); + obj.insert(None, 1); + obj.insert(Some(None), 2); + obj.insert(Some(Some(144)), 3); + + let comparison = &[ + Event::StartDictionary(Some(3)), + Event::StartDictionary(Some(1)), + Event::String("None".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + Event::Integer(1.into()), + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("None".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + Event::EndCollection, + Event::Integer(2.into()), + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::Integer(144.into()), + Event::EndCollection, + Event::EndCollection, + Event::Integer(3.into()), + Event::EndCollection, + ]; + + assert_roundtrip(obj, Some(comparison)); +} + +#[test] +fn option_array() { + let obj = vec![None, Some(None), Some(Some(144))]; + + let comparison = &[ + Event::StartArray(Some(3)), + Event::StartDictionary(Some(1)), + Event::String("None".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("None".to_owned()), + Event::String("".to_owned()), + Event::EndCollection, + Event::EndCollection, + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::StartDictionary(Some(1)), + Event::String("Some".to_owned()), + Event::Integer(144.into()), + Event::EndCollection, + Event::EndCollection, + Event::EndCollection, + ]; + + assert_roundtrip(obj, Some(comparison)); +} diff -Nru rust-plist-0.4.2/src/ser.rs rust-plist-0.5.4/src/ser.rs --- rust-plist-0.4.2/src/ser.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/src/ser.rs 2019-07-26 09:36:25.000000000 +0000 @@ -1,47 +1,120 @@ use serde::ser; -use std::fmt::Display; -use std::io::Write; - -use date::serde_impls::DATE_NEWTYPE_STRUCT_NAME; -use stream::{self, Event, Writer}; -use {Date, Error}; +use std::{ + fmt::Display, + fs::File, + io::{BufWriter, Write}, + mem, + path::Path, +}; + +use crate::{ + date::serde_impls::DATE_NEWTYPE_STRUCT_NAME, + error::{self, Error, ErrorKind}, + stream::{self, Writer}, + uid::serde_impls::UID_NEWTYPE_STRUCT_NAME, + Date, Integer, Uid, +}; +#[doc(hidden)] impl ser::Error for Error { fn custom(msg: T) -> Self { - Error::Serde(msg.to_string()) + 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 } - } - - fn emit(&mut self, event: Event) -> Result<(), Error> { - self.writer.write(&event)?; - Ok(()) + Serializer { + writer, + option_mode: OptionMode::Root, + } } pub fn into_inner(self) -> W { self.writer } - // Emit {key: value} - fn single_key_dict(&mut self, key: String) -> Result<(), Error> { - self.emit(Event::StartDictionary(Some(1)))?; - self.emit(Event::StringValue(key))?; - Ok(()) + fn 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 single_key_dict_end(&mut self) -> Result<(), Error> { - self.emit(Event::EndDictionary)?; + fn maybe_write_pending_struct_field_name(&mut self) -> Result<(), Error> { + if let OptionMode::StructField(field_name) = self.option_mode { + self.option_mode = OptionMode::StructFieldNameWritten; + self.writer.write_string(field_name)?; + } Ok(()) } + + fn write_start_array(&mut self, len: Option) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_start_array(len) + } + + fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_start_dictionary(len) + } + + fn write_end_collection(&mut self) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_end_collection() + } + + fn write_boolean(&mut self, value: bool) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_boolean(value) + } + + fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_data(value) + } + + fn write_date(&mut self, value: Date) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_date(value) + } + + fn write_integer(&mut self, value: Integer) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_integer(value) + } + + fn write_real(&mut self, value: f64) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_real(value) + } + + fn write_string(&mut self, value: &str) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_string(value) + } + + fn write_uid(&mut self, value: Uid) -> Result<(), Error> { + self.maybe_write_pending_struct_field_name()?; + self.writer.write_uid(value) + } } impl<'a, W: Writer> ser::Serializer for &'a mut Serializer { @@ -57,79 +130,101 @@ type SerializeStructVariant = Compound<'a, W>; fn serialize_bool(self, v: bool) -> Result<(), Self::Error> { - self.emit(Event::BooleanValue(v)) + self.write_boolean(v) } - fn serialize_i8(self, v: i8) -> Result<(), Self::Error> { + fn serialize_i8(self, v: i8) -> Result<(), Error> { self.serialize_i64(v.into()) } - fn serialize_i16(self, v: i16) -> Result<(), Self::Error> { + fn serialize_i16(self, v: i16) -> Result<(), Error> { self.serialize_i64(v.into()) } - fn serialize_i32(self, v: i32) -> Result<(), Self::Error> { + fn serialize_i32(self, v: i32) -> Result<(), Error> { self.serialize_i64(v.into()) } fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { - self.emit(Event::IntegerValue(v)) + self.write_integer(v.into()) } - fn serialize_u8(self, v: u8) -> Result<(), Self::Error> { + fn serialize_u8(self, v: u8) -> Result<(), Error> { self.serialize_u64(v.into()) } - fn serialize_u16(self, v: u16) -> Result<(), Self::Error> { + fn serialize_u16(self, v: u16) -> Result<(), Error> { self.serialize_u64(v.into()) } - fn serialize_u32(self, v: u32) -> Result<(), Self::Error> { + fn serialize_u32(self, v: u32) -> Result<(), Error> { self.serialize_u64(v.into()) } fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { - self.emit(Event::IntegerValue(v as i64)) + self.write_integer(v.into()) } - fn serialize_f32(self, v: f32) -> Result<(), Self::Error> { + fn serialize_f32(self, v: f32) -> Result<(), Error> { self.serialize_f64(v.into()) } - fn serialize_f64(self, v: f64) -> Result<(), Self::Error> { - self.emit(Event::RealValue(v)) + fn serialize_f64(self, v: f64) -> Result<(), Error> { + self.write_real(v) } fn serialize_char(self, v: char) -> Result<(), Self::Error> { - self.emit(Event::StringValue(v.to_string())) + let mut buf = [0; 4]; + let v = v.encode_utf8(&mut buf); + self.write_string(v) } - fn serialize_str(self, v: &str) -> Result<(), Self::Error> { - self.emit(Event::StringValue(v.to_owned())) + fn serialize_str(self, v: &str) -> Result<(), Error> { + self.write_string(v) } - fn serialize_bytes(self, v: &[u8]) -> Result<(), Self::Error> { - self.emit(Event::DataValue(v.to_owned())) + fn serialize_bytes(self, v: &[u8]) -> Result<(), Error> { + self.write_data(v) } - fn serialize_none(self) -> Result<(), Self::Error> { - self.single_key_dict("None".to_owned())?; - self.serialize_unit()?; - self.single_key_dict_end() + 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<(), Self::Error> { - self.single_key_dict("Some".to_owned())?; - value.serialize(&mut *self)?; - self.single_key_dict_end() + 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<(), Self::Error> { - // Emit empty string - self.emit(Event::StringValue(String::new())) + fn serialize_unit(self) -> Result<(), Error> { + self.write_string("") } - fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Self::Error> { + fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> { self.serialize_unit() } @@ -138,22 +233,22 @@ _name: &'static str, _variant_index: u32, variant: &'static str, - ) -> Result<(), Self::Error> { - self.single_key_dict(variant.to_owned())?; + ) -> Result<(), Error> { + self.write_start_dictionary(Some(1))?; + self.write_string(variant)?; self.serialize_unit()?; - self.single_key_dict_end()?; - Ok(()) + self.write_end_collection() } fn serialize_newtype_struct( self, name: &'static str, value: &T, - ) -> Result<(), Self::Error> { - if name == DATE_NEWTYPE_STRUCT_NAME { - value.serialize(DateSerializer { ser: &mut *self }) - } else { - value.serialize(self) + ) -> 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), } } @@ -163,19 +258,20 @@ _variant_index: u32, variant: &'static str, value: &T, - ) -> Result<(), Self::Error> { - self.single_key_dict(variant.to_owned())?; + ) -> Result<(), Error> { + self.write_start_dictionary(Some(1))?; + self.write_string(variant)?; value.serialize(&mut *self)?; - self.single_key_dict_end() + self.write_end_collection() } - fn serialize_seq(self, len: Option) -> Result { + fn serialize_seq(self, len: Option) -> Result { let len = len.map(|len| len as u64); - self.emit(Event::StartArray(len))?; + self.write_start_array(len)?; Ok(Compound { ser: self }) } - fn serialize_tuple(self, len: usize) -> Result { + fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } @@ -183,7 +279,7 @@ self, _name: &'static str, len: usize, - ) -> Result { + ) -> Result { self.serialize_tuple(len) } @@ -193,14 +289,15 @@ _variant_index: u32, variant: &'static str, len: usize, - ) -> Result { - self.single_key_dict(variant.to_owned())?; + ) -> Result { + self.write_start_dictionary(Some(1))?; + self.write_string(variant)?; self.serialize_tuple(len) } - fn serialize_map(self, len: Option) -> Result { + fn serialize_map(self, len: Option) -> Result { let len = len.map(|len| len as u64); - self.emit(Event::StartDictionary(len))?; + self.write_start_dictionary(len)?; Ok(Compound { ser: self }) } @@ -208,7 +305,7 @@ self, _name: &'static str, _len: usize, - ) -> Result { + ) -> Result { // The number of struct fields is not known as fields with None values are ignored. self.serialize_map(None) } @@ -219,203 +316,186 @@ _variant_index: u32, variant: &'static str, len: usize, - ) -> Result { - self.single_key_dict(variant.to_owned())?; + ) -> Result { + self.write_start_dictionary(Some(1))?; + self.write_string(variant)?; self.serialize_struct(name, len) } } -struct StructFieldSerializer<'a, W: 'a + Writer> { +struct DateSerializer<'a, W: 'a + Writer> { ser: &'a mut Serializer, - field_name: &'static str, } -impl<'a, W: Writer> StructFieldSerializer<'a, W> { - fn use_ser(self) -> Result<&'a mut Serializer, Error> { - // We are going to serialize something so write the struct field name. - self.ser - .emit(Event::StringValue(self.field_name.to_owned()))?; - Ok(self.ser) +impl<'a, W: Writer> DateSerializer<'a, W> { + fn expecting_date_error(&self) -> Error { + ser::Error::custom("plist date string expected") } } -impl<'a, W: Writer> ser::Serializer for StructFieldSerializer<'a, W> { +impl<'a, W: Writer> ser::Serializer for DateSerializer<'a, W> { type Ok = (); type Error = Error; - type SerializeSeq = Compound<'a, W>; - type SerializeTuple = Compound<'a, W>; - type SerializeTupleStruct = Compound<'a, W>; - type SerializeTupleVariant = Compound<'a, W>; - type SerializeMap = Compound<'a, W>; - type SerializeStruct = Compound<'a, W>; - type SerializeStructVariant = Compound<'a, W>; + 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, v: bool) -> Result<(), Self::Error> { - self.use_ser()?.serialize_bool(v) + fn serialize_bool(self, _: bool) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_i8(self, v: i8) -> Result<(), Self::Error> { - self.use_ser()?.serialize_i8(v) + fn serialize_i8(self, _: i8) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_i16(self, v: i16) -> Result<(), Self::Error> { - self.use_ser()?.serialize_i16(v) + fn serialize_i16(self, _: i16) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_i32(self, v: i32) -> Result<(), Self::Error> { - self.use_ser()?.serialize_i32(v) + fn serialize_i32(self, _: i32) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { - self.use_ser()?.serialize_i64(v) + fn serialize_i64(self, _: i64) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_u8(self, v: u8) -> Result<(), Self::Error> { - self.use_ser()?.serialize_u8(v) + fn serialize_u8(self, _: u8) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_u16(self, v: u16) -> Result<(), Self::Error> { - self.use_ser()?.serialize_u16(v) + fn serialize_u16(self, _: u16) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_u32(self, v: u32) -> Result<(), Self::Error> { - self.use_ser()?.serialize_u32(v) + fn serialize_u32(self, _: u32) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { - self.use_ser()?.serialize_u64(v) + fn serialize_u64(self, _: u64) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_f32(self, v: f32) -> Result<(), Self::Error> { - self.use_ser()?.serialize_f32(v) + fn serialize_f32(self, _: f32) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_f64(self, v: f64) -> Result<(), Self::Error> { - self.use_ser()?.serialize_f64(v) + fn serialize_f64(self, _: f64) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_char(self, v: char) -> Result<(), Self::Error> { - self.use_ser()?.serialize_char(v) + fn serialize_char(self, _: char) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_str(self, v: &str) -> Result<(), Self::Error> { - self.use_ser()?.serialize_str(v) + fn serialize_str(self, v: &str) -> Result<(), Error> { + let date = Date::from_rfc3339(v).map_err(|()| self.expecting_date_error())?; + self.ser.write_date(date) } - fn serialize_bytes(self, v: &[u8]) -> Result<(), Self::Error> { - self.use_ser()?.serialize_bytes(v) + fn serialize_bytes(self, _: &[u8]) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_none(self) -> Result<(), Self::Error> { - // Don't write a dict for None if the Option is in a struct. - Ok(()) + fn serialize_none(self) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_some(self, value: &T) -> Result<(), Self::Error> { - let ser = self.use_ser()?; - value.serialize(ser) + fn serialize_some(self, _: &T) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_unit(self) -> Result<(), Self::Error> { - self.use_ser()?.serialize_unit() + fn serialize_unit(self) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_unit_struct(self, name: &'static str) -> Result<(), Self::Error> { - self.use_ser()?.serialize_unit_struct(name) + fn serialize_unit_struct(self, _: &'static str) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_unit_variant( - self, - name: &'static str, - variant_index: u32, - variant: &'static str, - ) -> Result<(), Self::Error> { - self.use_ser()? - .serialize_unit_variant(name, variant_index, variant) + fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<(), Error> { + Err(self.expecting_date_error()) } fn serialize_newtype_struct( self, - name: &'static str, - value: &T, - ) -> Result<(), Self::Error> { - self.use_ser()?.serialize_newtype_struct(name, value) + _: &'static str, + _: &T, + ) -> Result<(), Error> { + Err(self.expecting_date_error()) } fn serialize_newtype_variant( self, - name: &'static str, - variant_index: u32, - variant: &'static str, - value: &T, - ) -> Result<(), Self::Error> { - self.use_ser()? - .serialize_newtype_variant(name, variant_index, variant, value) + _: &'static str, + _: u32, + _: &'static str, + _: &T, + ) -> Result<(), Error> { + Err(self.expecting_date_error()) } - fn serialize_seq(self, len: Option) -> Result { - self.use_ser()?.serialize_seq(len) + fn serialize_seq(self, _: Option) -> Result { + Err(self.expecting_date_error()) } - fn serialize_tuple(self, len: usize) -> Result { - self.use_ser()?.serialize_tuple(len) + fn serialize_tuple(self, _: usize) -> Result { + Err(self.expecting_date_error()) } fn serialize_tuple_struct( self, - name: &'static str, - len: usize, - ) -> Result { - self.use_ser()?.serialize_tuple_struct(name, len) + _: &'static str, + _: usize, + ) -> Result { + Err(self.expecting_date_error()) } fn serialize_tuple_variant( self, - name: &'static str, - variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - self.use_ser()? - .serialize_tuple_variant(name, variant_index, variant, len) + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Err(self.expecting_date_error()) } - fn serialize_map(self, len: Option) -> Result { - self.use_ser()?.serialize_map(len) + fn serialize_map(self, _: Option) -> Result { + Err(self.expecting_date_error()) } - fn serialize_struct( - self, - name: &'static str, - len: usize, - ) -> Result { - self.use_ser()?.serialize_struct(name, len) + fn serialize_struct(self, _: &'static str, _: usize) -> Result { + Err(self.expecting_date_error()) } fn serialize_struct_variant( self, - name: &'static str, - variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - self.use_ser()? - .serialize_struct_variant(name, variant_index, variant, len) + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Err(self.expecting_date_error()) } } -struct DateSerializer<'a, W: 'a + Writer> { +struct UidSerializer<'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> UidSerializer<'a, W> { + fn expecting_uid_error(&self) -> Error { + ser::Error::custom("plist uid expected") } } -impl<'a, W: Writer> ser::Serializer for DateSerializer<'a, W> { +impl<'a, W: Writer> ser::Serializer for UidSerializer<'a, W> { type Ok = (); type Error = Error; @@ -427,94 +507,88 @@ type SerializeStruct = ser::Impossible<(), Error>; type SerializeStructVariant = ser::Impossible<(), Error>; - fn serialize_bool(self, _: bool) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_bool(self, _: bool) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_i8(self, _: i8) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_i8(self, _: i8) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_i16(self, _: i16) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_i16(self, _: i16) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_i32(self, _: i32) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_i32(self, _: i32) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_i64(self, _: i64) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_i64(self, _: i64) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_u8(self, _: u8) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_u8(self, _: u8) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_u16(self, _: u16) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_u16(self, _: u16) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_u32(self, _: u32) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_u32(self, _: u32) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_u64(self, _: u64) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_u64(self, v: u64) -> Result<(), Error> { + self.ser.write_uid(Uid::new(v)) } - fn serialize_f32(self, _: f32) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_f32(self, _: f32) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_f64(self, _: f64) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_f64(self, _: f64) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_char(self, _: char) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_char(self, _: char) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_str(self, v: &str) -> Result<(), Self::Error> { - let date = Date::from_rfc3339(v).map_err(|()| self.expecting_date_error())?; - self.ser.emit(Event::DateValue(date)) + fn serialize_str(self, _: &str) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_bytes(self, _: &[u8]) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_bytes(self, _: &[u8]) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_none(self) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_none(self) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_some(self, _: &T) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_some(self, _: &T) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_unit(self) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + fn serialize_unit(self) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_unit_struct(self, _: &'static str) -> Result<(), Self::Error> { - Err(self.expecting_date_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<(), Self::Error> { - Err(self.expecting_date_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<(), Self::Error> { - Err(self.expecting_date_error()) + ) -> Result<(), Error> { + Err(self.expecting_uid_error()) } fn serialize_newtype_variant( @@ -523,24 +597,24 @@ _: u32, _: &'static str, _: &T, - ) -> Result<(), Self::Error> { - Err(self.expecting_date_error()) + ) -> Result<(), Error> { + Err(self.expecting_uid_error()) } - fn serialize_seq(self, _: Option) -> Result { - Err(self.expecting_date_error()) + fn serialize_seq(self, _: Option) -> Result { + Err(self.expecting_uid_error()) } - fn serialize_tuple(self, _: usize) -> Result { - Err(self.expecting_date_error()) + fn serialize_tuple(self, _: usize) -> Result { + Err(self.expecting_uid_error()) } fn serialize_tuple_struct( self, _: &'static str, _: usize, - ) -> Result { - Err(self.expecting_date_error()) + ) -> Result { + Err(self.expecting_uid_error()) } fn serialize_tuple_variant( @@ -549,20 +623,16 @@ _: u32, _: &'static str, _: usize, - ) -> Result { - Err(self.expecting_date_error()) + ) -> Result { + Err(self.expecting_uid_error()) } - fn serialize_map(self, _: Option) -> Result { - Err(self.expecting_date_error()) + fn serialize_map(self, _: Option) -> Result { + Err(self.expecting_uid_error()) } - fn serialize_struct( - self, - _: &'static str, - _: usize, - ) -> Result { - Err(self.expecting_date_error()) + fn serialize_struct(self, _: &'static str, _: usize) -> Result { + Err(self.expecting_uid_error()) } fn serialize_struct_variant( @@ -571,8 +641,8 @@ _: u32, _: &'static str, _: usize, - ) -> Result { - Err(self.expecting_date_error()) + ) -> Result { + Err(self.expecting_uid_error()) } } @@ -585,15 +655,13 @@ type Ok = (); type Error = Error; - fn serialize_element( - &mut self, - value: &T, - ) -> Result<(), Self::Error> { - value.serialize(&mut *self.ser) + fn serialize_element(&mut self, value: &T) -> Result<(), Error> { + self.ser + .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { - self.ser.emit(Event::EndArray) + self.ser.write_end_collection() } } @@ -601,15 +669,13 @@ type Ok = (); type Error = Error; - fn serialize_element( - &mut self, - value: &T, - ) -> Result<(), Self::Error> { - ::serialize_element(self, value) + fn serialize_element(&mut self, value: &T) -> Result<(), Error> { + self.ser + .serialize_with_option_mode(OptionMode::Explicit, value) } - fn end(self) -> Result { - ::end(self) + fn end(self) -> Result<(), Error> { + self.ser.write_end_collection() } } @@ -617,15 +683,13 @@ type Ok = (); type Error = Error; - fn serialize_field( - &mut self, - value: &T, - ) -> Result<(), Self::Error> { - ::serialize_element(self, value) + fn serialize_field(&mut self, value: &T) -> Result<(), Error> { + self.ser + .serialize_with_option_mode(OptionMode::Explicit, value) } - fn end(self) -> Result { - ::end(self) + fn end(self) -> Result<(), Error> { + self.ser.write_end_collection() } } @@ -633,16 +697,14 @@ type Ok = (); type Error = Error; - fn serialize_field( - &mut self, - value: &T, - ) -> Result<(), Self::Error> { - ::serialize_element(self, value) + fn serialize_field(&mut self, value: &T) -> Result<(), Error> { + self.ser + .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { - self.ser.emit(Event::EndArray)?; - self.ser.single_key_dict_end() + self.ser.write_end_collection()?; + self.ser.write_end_collection() } } @@ -650,19 +712,18 @@ type Ok = (); type Error = Error; - fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> { - key.serialize(&mut *self.ser) + 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<(), Self::Error> { - value.serialize(&mut *self.ser) + fn serialize_value(&mut self, value: &T) -> Result<(), Error> { + self.ser + .serialize_with_option_mode(OptionMode::Explicit, value) } fn end(self) -> Result { - self.ser.emit(Event::EndDictionary) + self.ser.write_end_collection() } } @@ -674,17 +735,15 @@ &mut self, key: &'static str, value: &T, - ) -> Result<(), Self::Error> { + ) -> 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. - value.serialize(StructFieldSerializer { - field_name: key, - ser: &mut *self.ser, - }) + self.ser + .serialize_with_option_mode(OptionMode::StructField(key), value) } - fn end(self) -> Result { - ::end(self) + fn end(self) -> Result<(), Error> { + self.ser.write_end_collection() } } @@ -696,17 +755,41 @@ &mut self, key: &'static str, value: &T, - ) -> Result<(), Self::Error> { - ::serialize_field(self, key, value) + ) -> Result<(), Error> { + self.ser + .serialize_with_option_mode(OptionMode::StructField(key), value) } - fn end(self) -> Result { - self.ser.emit(Event::EndDictionary)?; - self.ser.single_key_dict_end() + fn end(self) -> Result<(), Error> { + self.ser.write_end_collection()?; + self.ser.write_end_collection() } } -/// Serializes the given data structure as an XML encoded plist file. +/// Serializes the given data structure to a file as a binary encoded plist. +pub fn to_file_binary, T: ser::Serialize>(path: P, value: &T) -> Result<(), Error> { + let mut file = File::create(path).map_err(error::from_io_without_position)?; + to_writer_binary(BufWriter::new(&mut file), value)?; + file.sync_all().map_err(error::from_io_without_position)?; + Ok(()) +} + +/// Serializes the given data structure to a file as an XML encoded plist. +pub fn to_file_xml, T: ser::Serialize>(path: P, value: &T) -> Result<(), Error> { + let mut file = File::create(path).map_err(error::from_io_without_position)?; + to_writer_xml(BufWriter::new(&mut file), value)?; + file.sync_all().map_err(error::from_io_without_position)?; + Ok(()) +} + +/// Serializes the given data structure to a byte stream as a binary encoded plist. +pub fn to_writer_binary(writer: W, value: &T) -> Result<(), Error> { + let writer = stream::BinaryWriter::new(writer); + let mut ser = Serializer::new(writer); + value.serialize(&mut ser) +} + +/// Serializes the given data structure to a byte stream as an XML encoded plist. pub fn to_writer_xml(writer: W, value: &T) -> Result<(), Error> { let writer = stream::XmlWriter::new(writer); let mut ser = Serializer::new(writer); diff -Nru rust-plist-0.4.2/src/stream/binary_reader.rs rust-plist-0.5.4/src/stream/binary_reader.rs --- rust-plist-0.4.2/src/stream/binary_reader.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/src/stream/binary_reader.rs 2020-04-06 14:41:57.000000000 +0000 @@ -1,22 +1,14 @@ -use byteorder::{BigEndian, ReadBytesExt}; -use std::io::{Read, Seek, SeekFrom}; -use std::mem::size_of; -use std::string::{FromUtf16Error, FromUtf8Error}; - -use stream::Event; -use {u64_to_usize, Date, Error}; - -impl From for Error { - fn from(_: FromUtf8Error) -> Error { - Error::InvalidData - } -} - -impl From for Error { - fn from(_: FromUtf16Error) -> Error { - Error::InvalidData - } -} +use std::{ + io::{self, Read, Seek, SeekFrom}, + mem::size_of, +}; + +use crate::{ + date::{Date, InfiniteOrNanDate}, + error::{Error, ErrorKind}, + stream::Event, + u64_to_usize, Uid, +}; struct StackItem { object_ref: u64, @@ -35,12 +27,41 @@ stack: Vec, object_offsets: Vec, object_on_stack: Vec, - reader: R, + reader: PosReader, ref_size: u8, root_object: u64, - // The largest single allocation allowed for this plist. - // Equal to the number of bytes in the plist minus the magic number and trailer. - max_allocation_bytes: usize, + 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 { @@ -49,56 +70,58 @@ stack: Vec::new(), object_offsets: Vec::new(), object_on_stack: Vec::new(), - reader, + reader: PosReader { reader, pos: 0 }, ref_size: 0, root_object: 0, - max_allocation_bytes: 0, + trailer_start_offset: 0, } } - fn can_allocate(&self, len: u64, size: usize) -> bool { - let byte_len = len.saturating_mul(size as u64); - byte_len <= self.max_allocation_bytes as u64 - } - fn allocate_vec(&self, len: u64, size: usize) -> Result, Error> { - if self.can_allocate(len, size) { - Ok(Vec::with_capacity(len as usize)) - } else { - Err(Error::InvalidData) - } + // 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_exact(&mut magic)?; + self.reader.read_all(&mut magic)?; if &magic != b"bplist00" { - return Err(Error::InvalidData); + 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 trailer_start = self.reader.seek(SeekFrom::End(-32 + 6))?; + let mut zeros = [0; 6]; + self.reader.read_all(&mut zeros)?; - let offset_size = self.reader.read_u8()?; + let offset_size = self.read_u8()?; match offset_size { 1 | 2 | 4 | 8 => (), - _ => return Err(Error::InvalidData), + _ => return Err(self.with_pos(ErrorKind::InvalidTrailerObjectOffsetSize)), } - self.ref_size = self.reader.read_u8()?; + self.ref_size = self.read_u8()?; match self.ref_size { 1 | 2 | 4 | 8 => (), - _ => return Err(Error::InvalidData), + _ => return Err(self.with_pos(ErrorKind::InvalidTrailerObjectReferenceSize)), } - let num_objects = self.reader.read_u64::()?; - self.root_object = self.reader.read_u64::()?; - let offset_table_offset = self.reader.read_u64::()?; - - // File size minus trailer and header - // Truncated to max(usize) - self.max_allocation_bytes = trailer_start.saturating_sub(8) as usize; + 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))?; @@ -108,53 +131,62 @@ 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.reader.read_u8()?.into()), - 2 => ints.push(self.reader.read_u16::()?.into()), - 4 => ints.push(self.reader.read_u32::()?.into()), - 8 => ints.push(self.reader.read_u64::()?), - _ => return Err(Error::InvalidData), + 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.reader.read_u8()? & 0x03; + let len_power_of_two = self.read_u8()? & 0x03; Ok(match len_power_of_two { - 0 => self.reader.read_u8()?.into(), - 1 => self.reader.read_u16::()?.into(), - 2 => self.reader.read_u32::()?.into(), - 3 => self.reader.read_u64::()?, - _ => return Err(Error::InvalidData), + 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_exact(&mut data)?; + 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(Error::InvalidData)?; + 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(Error::InvalidData)?; + .ok_or_else(|| self.with_pos(ErrorKind::ObjectReferenceTooLarge))?; + if offset >= self.trailer_start_offset { + return Err(self.with_pos(ErrorKind::ObjectOffsetTooLarge)); + } Ok(self.reader.seek(SeekFrom::Start(offset))?) } @@ -162,7 +194,7 @@ let object_ref = u64_to_usize(item.object_ref).expect("internal consistency error"); let is_on_stack = &mut self.object_on_stack[object_ref]; if *is_on_stack { - return Err(Error::InvalidData); + return Err(self.with_pos(ErrorKind::RecursiveObject)); } *is_on_stack = true; self.stack.push(item); @@ -195,56 +227,56 @@ // We're at the end of an array or dict. Pop the top stack item and return. let stack_item = self.pop_stack_item(); match stack_item.ty { - StackType::Array => return Ok(Some(Event::EndArray)), - StackType::Dict => return Ok(Some(Event::EndDictionary)), + StackType::Array | StackType::Dict => return Ok(Some(Event::EndCollection)), } } }; self.seek_to_object(object_ref)?; - let token = self.reader.read_u8()?; + let token = self.read_u8()?; let ty = (token & 0xf0) >> 4; let size = token & 0x0f; let result = match (ty, size) { - (0x0, 0x00) => return Err(Error::InvalidData), // null - (0x0, 0x08) => Some(Event::BooleanValue(false)), - (0x0, 0x09) => Some(Event::BooleanValue(true)), - (0x0, 0x0f) => return Err(Error::InvalidData), // fill - (0x1, 0) => Some(Event::IntegerValue(self.reader.read_u8()?.into())), - (0x1, 1) => Some(Event::IntegerValue( - self.reader.read_u16::()?.into(), - )), - (0x1, 2) => Some(Event::IntegerValue( - self.reader.read_u32::()?.into(), - )), - (0x1, 3) => Some(Event::IntegerValue(self.reader.read_i64::()?)), - (0x1, 4) => return Err(Error::InvalidData), // 128 bit int - (0x1, _) => return Err(Error::InvalidData), // variable length int - (0x2, 2) => Some(Event::RealValue( - self.reader.read_f32::()?.into(), - )), - (0x2, 3) => Some(Event::RealValue(self.reader.read_f64::()?)), - (0x2, _) => return Err(Error::InvalidData), // odd length float + (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 = self.reader.read_f64::()?; - Some(Event::DateValue( - Date::from_seconds_since_plist_epoch(secs).map_err(|()| Error::InvalidData)?, - )) + 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::DataValue(self.read_data(len)?)) + Some(Event::Data(self.read_data(len)?)) } (0x5, n) => { // ASCII string let len = self.read_object_len(n)?; let raw = self.read_data(len)?; - let string = String::from_utf8(raw)?; - Some(Event::StringValue(string)) + let string = String::from_utf8(raw) + .map_err(|_| self.with_pos(ErrorKind::InvalidUtf8String))?; + Some(Event::String(string)) } (0x6, n) => { // UTF-16 string @@ -252,11 +284,24 @@ let mut raw_utf16 = self.allocate_vec(len_utf16_codepoints, size_of::())?; for _ in 0..len_utf16_codepoints { - raw_utf16.push(self.reader.read_u16::()?); + raw_utf16.push(self.read_be_u16()?); } - let string = String::from_utf16(&raw_utf16)?; - Some(Event::StringValue(string)) + let string = String::from_utf16(&raw_utf16) + .map_err(|_| self.with_pos(ErrorKind::InvalidUtf16String))?; + Some(Event::String(string)) + } + (0x8, n) if n < 8 => { + // Uid + let mut buf = [0; 8]; + // `len_bytes` is at most 8. + let len_bytes = n as usize + 1; + // Values are stored in big-endian so we must put the least significant bytes at + // the end of the buffer. + self.reader.read_all(&mut buf[8 - len_bytes..])?; + let value = u64::from_be_bytes(buf); + + Some(Event::Uid(Uid::new(value))) } (0xa, n) => { // Array @@ -279,9 +324,13 @@ let key_refs = self.read_refs(len)?; let value_refs = self.read_refs(len)?; - let mut child_object_refs = self.allocate_vec(len * 2, self.ref_size as usize)?; + let 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 + 1 { + 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]); @@ -295,11 +344,51 @@ Some(Event::StartDictionary(Some(len as u64))) } - (_, _) => return Err(Error::InvalidData), + (_, _) => 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 { @@ -321,40 +410,56 @@ #[cfg(test)] mod tests { use humantime::parse_rfc3339_weak; - use std::fs::File; - use std::path::Path; + use std::{fs::File, path::Path}; use super::*; - use stream::Event; - use stream::Event::*; + use crate::{stream::Event, Uid}; #[test] fn streaming_parser() { + use crate::stream::Event::*; + let reader = File::open(&Path::new("./tests/data/binary.plist")).unwrap(); let streaming_parser = BinaryReader::new(reader); let events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); let comparison = &[ - StartDictionary(Some(6)), - StringValue("Lines".to_owned()), + StartDictionary(Some(13)), + String("Author".into()), + String("William Shakespeare".into()), + String("Birthdate".into()), + Date(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), + 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)), - StringValue("It is a tale told by an idiot,".to_owned()), - StringValue("Full of sound and fury, signifying nothing.".to_owned()), - EndArray, - StringValue("Death".to_owned()), - IntegerValue(1564), - StringValue("Height".to_owned()), - RealValue(1.60), - StringValue("Birthdate".to_owned()), - DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), - StringValue("Author".to_owned()), - StringValue("William Shakespeare".to_owned()), - StringValue("Data".to_owned()), - DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), - EndDictionary, + String("It is a tale told by an idiot,".into()), + String("Full of sound and fury, signifying nothing.".into()), + EndCollection, + String("Death".into()), + Integer(1564.into()), + String("Blank".into()), + String("".into()), + String("BiggestNumber".into()), + Integer(18446744073709551615u64.into()), + String("IsTrue".into()), + Boolean(true), + String("Data".into()), + Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), + EndCollection, ]; - assert_eq!(events, comparison); + assert_eq!(events, &comparison[..]); } #[test] @@ -363,9 +468,9 @@ let streaming_parser = BinaryReader::new(reader); let mut events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); - assert_eq!(events[2], StringValue("\u{2605} or better".to_owned())); + assert_eq!(events[2], Event::String("\u{2605} or better".to_owned())); - let poem = if let StringValue(ref mut poem) = events[4] { + let poem = if let Event::String(ref mut poem) = events[4] { poem } else { panic!("not a string") @@ -373,4 +478,16 @@ assert_eq!(poem.len(), 643); assert_eq!(poem.pop().unwrap(), '\u{2605}'); } + + #[test] + fn nskeyedarchiver_plist() { + let reader = File::open(&Path::new("./tests/data/binary_NSKeyedArchiver.plist")).unwrap(); + let streaming_parser = BinaryReader::new(reader); + let events: Vec = streaming_parser.map(|e| e.unwrap()).collect(); + + assert_eq!(events[10], Event::Uid(Uid::new(4))); + assert_eq!(events[12], Event::Uid(Uid::new(2))); + assert_eq!(events[18], Event::Uid(Uid::new(3))); + assert_eq!(events[46], Event::Uid(Uid::new(1))); + } } diff -Nru rust-plist-0.4.2/src/stream/binary_writer.rs rust-plist-0.5.4/src/stream/binary_writer.rs --- rust-plist-0.4.2/src/stream/binary_writer.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/src/stream/binary_writer.rs 2020-04-06 14:18:03.000000000 +0000 @@ -0,0 +1,736 @@ +// TODO: Revisit the design of `Event` once the `HashMap` raw interface is stabilised. +// Ideally `Value`s would be stored inline in `Event`. + +use indexmap::IndexMap; +use std::{ + borrow::Cow, + io::{self, Write}, + mem, + num::NonZeroUsize, +}; + +use crate::{ + error::{self, Error, ErrorKind, EventKind}, + stream::Writer, + Date, Integer, Uid, +}; + +pub struct BinaryWriter { + writer: PosWriter, + events: Vec, + dictionary_key_events: Vec, + values: IndexMap, ValueState>, + /// Pointers into `events` for each of the currently unclosed `Collection` events. + collection_stack: Vec, + /// The number of `Collection` and unique `Value` events in `events`. + num_objects: usize, +} + +struct PosWriter { + writer: W, + pos: usize, +} + +#[derive(Clone)] +struct ObjectRef(NonZeroUsize); + +/// An array of `len` elements is stored as a `Collection` event followed by `skip_len` events +/// containing the contents of the array. e.g. +/// +/// Collection(ty: Array, len: 2, skip_len: 2) +/// Value +/// Value +/// +/// If the array contains another array or dictionary `len` and `skip_len` will differ. e.g. +/// +/// Collection(ty: Array, len: 2, skip_len: 3) +/// Value +/// Collection(ty: Array, len: 1, skip_len: 1) +/// Value +/// +/// A dictionary of `len` (key, value) pairs is stored as a `Collection` event followed by +/// `skip_len` events containing the contents of the dictionary. The dictionary values are stored +/// first. These are followed by a `DictionaryKeys` event and then the keys themselves. e.g. +/// +/// Collection(ty: Dictionary, len: 2, skip_len: 6) +/// Value +/// Collection(ty: Array, len: 1, skip_len: 1) +/// Value +/// DictionaryKeys(2) +/// Value (Key) +/// Value (Key) +/// +/// This arrangement simplifies writing dictionaries as they must be written in the order +/// (key, key, value, value) instead of (key, value, key, value) as they are passed to the writer. +/// Unclosed dictionaries have their keys stored in `dictionary_key_events` and these are only +/// moved to the end of the `BinaryWriter::events` array once the dictionary is closed in +/// `write_end_collection`. +enum Event { + Collection(Collection), + /// Index of the value in the `values` map. + Value(usize), + /// The number of dictionary keys following this event. + DictionaryKeys(usize), +} + +struct Collection { + ty: CollectionType, + /// The number of elements in an array or (key, value) pairs in a dictionary. + /// Unclosed dictionaries have a `len` equal to the number of keys plus the number of values + /// written so far. This is fixed up in `write_end_collection`. + len: usize, + /// The number of events to skip to get to the next element after the collection. + skip: usize, + object_ref: Option, +} + +#[derive(Eq, PartialEq)] +enum CollectionType { + Array, + Dictionary, +} + +#[derive(Eq, Hash, PartialEq)] +enum Value<'a> { + Boolean(bool), + Data(Cow<'a, [u8]>), + Date(Date), + Integer(Integer), + /// Floats are deduplicated based on their bitwise value. + Real(u64), + String(Cow<'a, str>), + Uid(Uid), +} + +enum ValueState { + /// The value has not been assigned an object reference. + Unassigned, + /// The value has been assigned an object reference but has not yet been written. + Unwritten(ObjectRef), + /// The value has been written with the given object reference. + Written(ObjectRef), +} + +impl BinaryWriter { + pub fn new(writer: W) -> BinaryWriter { + BinaryWriter { + writer: PosWriter { writer, pos: 0 }, + events: Vec::new(), + dictionary_key_events: Vec::new(), + values: IndexMap::new(), + collection_stack: Vec::new(), + num_objects: 0, + } + } + + fn write_start_collection(&mut self, ty: CollectionType) -> Result<(), Error> { + if self.expecting_dictionary_key() { + let ty_event_kind = match ty { + CollectionType::Array => EventKind::StartArray, + CollectionType::Dictionary => EventKind::StartDictionary, + }; + return Err(ErrorKind::UnexpectedEventType { + expected: EventKind::DictionaryKeyOrEndCollection, + found: ty_event_kind, + } + .without_position()); + } + self.increment_current_collection_len(); + self.collection_stack.push(self.events.len()); + self.events.push(Event::Collection(Collection { + ty, + len: 0, + skip: 0, + object_ref: None, + })); + self.num_objects += 1; + Ok(()) + } + + fn write_end_collection(&mut self) -> Result<(), Error> { + let collection_event_index = self.collection_stack.pop().ok_or_else(|| { + ErrorKind::UnexpectedEventType { + expected: EventKind::ValueOrStartCollection, + found: EventKind::EndCollection, + } + .without_position() + })?; + + let current_event_index = self.events.len() - 1; + let c = if let Event::Collection(c) = &mut self.events[collection_event_index] { + c + } else { + unreachable!("items in `collection_stack` always point to a collection event"); + }; + + c.skip = current_event_index - collection_event_index; + + if let CollectionType::Dictionary = c.ty { + // Ensure that every dictionary key is paired with a value. + if !is_even(c.len) { + return Err(ErrorKind::UnexpectedEventType { + expected: EventKind::DictionaryKeyOrEndCollection, + found: EventKind::EndCollection, + } + .without_position()); + } + + // Fix up the dictionary length. It should contain the number of key-value pairs, + // not the number of keys and values. + c.len /= 2; + + // To skip past a dictionary we also need to skip the `DictionaryKeys` event and the + // keys that follow it. + c.skip += 1 + c.len; + let len = c.len; + self.events.push(Event::DictionaryKeys(len)); + + // Move the cached dictionary keys to the end of the events array. + let keys_start_index = self.dictionary_key_events.len() - len; + self.events.extend( + self.dictionary_key_events + .drain(keys_start_index..) + .map(Event::Value), + ); + } + + if self.collection_stack.is_empty() { + self.write_plist()?; + } + + Ok(()) + } + + fn write_value(&mut self, value: Value) -> Result<(), Error> { + let expecting_dictionary_key = self.expecting_dictionary_key(); + + // Ensure that all dictionary keys are strings. + match (&value, expecting_dictionary_key) { + (Value::String(_), true) | (_, false) => (), + (_, true) => { + return Err(ErrorKind::UnexpectedEventType { + expected: EventKind::DictionaryKeyOrEndCollection, + found: value.event_kind(), + } + .without_position()) + } + } + + // Deduplicate `value`. There is one entry in `values` for each unqiue `Value` in the + // plist. + let value_index = if let Some((value_index, _, _)) = self.values.get_full(&value) { + value_index + } else { + self.num_objects += 1; + let value = value.into_owned(); + let (value_index, _) = self.values.insert_full(value, ValueState::Unassigned); + value_index + }; + + // Dictionary keys are buffered in `dictionary_key_events` until the dictionary is closed + // in `write_end_collection` when they are moved to the end of the `events` array. + if expecting_dictionary_key { + self.dictionary_key_events.push(value_index); + } else { + self.events.push(Event::Value(value_index)); + } + + self.increment_current_collection_len(); + + if self.collection_stack.is_empty() { + self.write_plist()?; + } + + Ok(()) + } + + fn expecting_dictionary_key(&self) -> bool { + if let Some(&event_index) = self.collection_stack.last() { + if let Event::Collection(c) = &self.events[event_index] { + c.ty == CollectionType::Dictionary && is_even(c.len) + } else { + unreachable!("items in `collection_stack` always point to a collection event"); + } + } else { + false + } + } + + fn increment_current_collection_len(&mut self) { + if let Some(&event_index) = self.collection_stack.last() { + if let Event::Collection(c) = &mut self.events[event_index] { + c.len += 1; + } else { + unreachable!("items in `collection_stack` always point to a collection event"); + } + } + } + + fn write_plist(&mut self) -> Result<(), Error> { + assert!(self.collection_stack.is_empty()); + + // Write header + self.writer.write_exact(b"bplist00")?; + + // Write objects + let mut events_vec = mem::replace(&mut self.events, Vec::new()); + let mut events = &mut events_vec[..]; + let ref_size = plist_ref_size(self.num_objects - 1); + let mut offset_table = vec![0; self.num_objects]; + + // Assign the first (root) event an object reference of zero. + let mut next_object_ref = ObjectRef::zero(); + match &mut events[0] { + Event::Value(value_index) => { + let (_, value_state) = value_mut(&mut self.values, *value_index); + *value_state = ValueState::Unwritten(next_object_ref.clone_and_increment_self()); + } + Event::Collection(c) => { + c.object_ref = Some(next_object_ref.clone_and_increment_self()); + } + Event::DictionaryKeys(_) => { + unreachable!("`events` starts with a value or collection event") + } + } + + while let Some((event, rest)) = events.split_first_mut() { + events = rest; + match event { + Event::Collection(c) => { + let collection_events = &mut events[..c.skip]; + self.write_plist_collection( + c, + collection_events, + ref_size, + &mut next_object_ref, + &mut offset_table, + )?; + } + Event::Value(value_index) => { + self.write_plist_value(*value_index, &mut offset_table)?; + } + // Dictionary keys will have already been written in `write_plist_collection` so we + // skip over them here. + Event::DictionaryKeys(len) => { + events = &mut events[*len..]; + } + } + } + + // Write object offset table + let offset_table_offset = self.writer.pos; + let offset_size = plist_ref_size(offset_table_offset); + for &offset in &offset_table { + write_plist_ref(&mut self.writer, offset_size, offset)?; + } + + // Write trailer + // 6 zero bytes padding + // 1 byte offset size + // 1 byte object ref size + // 8 bytes number of objects + // 8 bytes root object ref (always zero) + // 8 bytes file offset of the object offset table + let mut trailer = [0; 32]; + trailer[6] = offset_size; + trailer[7] = ref_size; + trailer[8..16].copy_from_slice(&(self.num_objects as u64).to_be_bytes()); + trailer[24..32].copy_from_slice(&(offset_table_offset as u64).to_be_bytes()); + self.writer.write_exact(&trailer)?; + + self.writer + .flush() + .map_err(error::from_io_without_position)?; + + // Reset plist writer + self.writer.pos = 0; + events_vec.clear(); + self.events = events_vec; + self.values.clear(); + self.num_objects = 0; + + Ok(()) + } + + fn write_plist_collection( + &mut self, + collection: &Collection, + events: &mut [Event], + ref_size: u8, + next_object_ref: &mut ObjectRef, + offset_table: &mut Vec, + ) -> Result<(), Error> { + if let Some(object_ref) = &collection.object_ref { + offset_table[object_ref.value()] = self.writer.pos; + } else { + unreachable!("collection object refs are assigned before this function is called"); + } + + // Split the events in the current collection into keys and values (arrays contain only + // values). This is required as dictionary keys appear after values in the `events array + // but all keys must be written before any values. + let (keys, values, ty) = match collection.ty { + CollectionType::Array => (&mut [][..], events, 0xa0), + CollectionType::Dictionary => { + let keys_start_offset = events.len() - collection.len - 1; + let (values, keys) = events.split_at_mut(keys_start_offset); + (&mut keys[1..], values, 0xd0) + } + }; + let mut collection_events = keys.iter_mut().chain(values); + + // Collections are written as a length prefixed array of object references. For an array + // the length is the number of elements. For a dictionary it is the number of (key, value) + // pairs. + write_plist_value_ty_and_size(&mut self.writer, ty, collection.len)?; + while let Some(event) = collection_events.next() { + let object_ref = match event { + Event::Collection(c) => { + // We only want to write references to top level elements in the collection so + // we skip over the contents of any sub-collections. + if c.skip > 0 { + let _ = collection_events.nth(c.skip - 1); + } + + // Collections are not deduplicated so they must be assigned an object + // reference here. + assert!(c.object_ref.is_none()); + let object_ref = next_object_ref.clone_and_increment_self(); + c.object_ref = Some(object_ref.clone()); + object_ref + } + Event::Value(value_index) => { + // Values are deduplicated so we only assign an object reference if we have not + // already done so previously. + let (_, value_state) = value_mut(&mut self.values, *value_index); + match value_state { + ValueState::Unassigned => { + let object_ref = next_object_ref.clone_and_increment_self(); + *value_state = ValueState::Unwritten(object_ref.clone()); + object_ref + } + ValueState::Unwritten(object_ref) | ValueState::Written(object_ref) => { + object_ref.clone() + } + } + } + Event::DictionaryKeys(_) => unreachable!( + "`DictionaryKeys` events are specifically excluded from the iterator" + ), + }; + write_plist_ref(&mut self.writer, ref_size, object_ref.value())?; + } + + // We write dictionary keys here as they appear after values in the `events` array but + // should come before values in the plist stream to reduce seeking on read. + for key in keys { + if let Event::Value(value_index) = key { + self.write_plist_value(*value_index, offset_table)?; + } else { + unreachable!("dictionary keys are assigned as values in `write_end_collection`"); + } + } + + Ok(()) + } + + fn write_plist_value( + &mut self, + value_index: usize, + offset_table: &mut Vec, + ) -> Result<(), Error> { + let (value, value_state) = value_mut(&mut self.values, value_index); + + let object_ref = match value_state { + ValueState::Unassigned => { + unreachable!("value object refs are assigned before this function is called"); + } + ValueState::Unwritten(object_ref) => object_ref.clone(), + ValueState::Written(_) => return Ok(()), + }; + + offset_table[object_ref.value()] = self.writer.pos; + *value_state = ValueState::Written(object_ref); + + match value { + Value::Boolean(true) => { + self.writer.write_exact(&[0x09])?; + } + Value::Boolean(false) => { + self.writer.write_exact(&[0x08])?; + } + Value::Data(v) => { + write_plist_value_ty_and_size(&mut self.writer, 0x40, v.len())?; + self.writer.write_exact(&v[..])?; + } + Value::Date(v) => { + let secs = v.to_seconds_since_plist_epoch(); + let mut buf: [_; 9] = [0x33, 0, 0, 0, 0, 0, 0, 0, 0]; + buf[1..].copy_from_slice(&secs.to_bits().to_be_bytes()); + self.writer.write_exact(&buf)?; + } + Value::Integer(v) => { + if let Some(v) = v.as_signed() { + if v >= 0 && v <= i64::from(u8::max_value()) { + self.writer.write_exact(&[0x10, v as u8])?; + } else if v >= 0 && v <= i64::from(u16::max_value()) { + let mut buf: [_; 3] = [0x11, 0, 0]; + buf[1..].copy_from_slice(&(v as u16).to_be_bytes()); + self.writer.write_exact(&buf)?; + } else if v >= 0 && v <= i64::from(u32::max_value()) { + let mut buf: [_; 5] = [0x12, 0, 0, 0, 0]; + buf[1..].copy_from_slice(&(v as u32).to_be_bytes()); + self.writer.write_exact(&buf)?; + } else { + let mut buf: [_; 9] = [0x13, 0, 0, 0, 0, 0, 0, 0, 0]; + buf[1..].copy_from_slice(&v.to_be_bytes()); + self.writer.write_exact(&buf)?; + } + } else if let Some(v) = v.as_unsigned() { + // `u64`s larger than `i64::max_value()` are stored as signed 128 bit + // integers. + let mut buf: [_; 17] = [0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + buf[1..].copy_from_slice(&i128::from(v).to_be_bytes()); + self.writer.write_exact(&buf)?; + } else { + unreachable!("an integer can be represented as either an i64 or u64"); + } + } + Value::Real(v) => { + let mut buf: [_; 9] = [0x23, 0, 0, 0, 0, 0, 0, 0, 0]; + buf[1..].copy_from_slice(&v.to_be_bytes()); + self.writer.write_exact(&buf)?; + } + Value::String(v) if v.is_ascii() => { + let ascii = v.as_bytes(); + write_plist_value_ty_and_size(&mut self.writer, 0x50, ascii.len())?; + self.writer.write_exact(ascii)?; + } + Value::String(v) => { + let utf16_len = v.encode_utf16().count(); + write_plist_value_ty_and_size(&mut self.writer, 0x60, utf16_len)?; + for c in v.encode_utf16() { + self.writer.write_exact(&c.to_be_bytes())?; + } + } + Value::Uid(v) => { + let v = v.get(); + if v <= u64::from(u8::max_value()) { + self.writer.write_exact(&[0x80, v as u8])?; + } else if v <= u64::from(u16::max_value()) { + let mut buf: [_; 3] = [0x81, 0, 0]; + buf[1..].copy_from_slice(&(v as u16).to_be_bytes()); + self.writer.write_exact(&buf)?; + } else if v <= u64::from(u32::max_value()) { + let mut buf: [_; 5] = [0x83, 0, 0, 0, 0]; + buf[1..].copy_from_slice(&(v as u32).to_be_bytes()); + self.writer.write_exact(&buf)?; + } else { + let mut buf: [_; 9] = [0x87, 0, 0, 0, 0, 0, 0, 0, 0]; + buf[1..].copy_from_slice(&(v as u64).to_be_bytes()); + self.writer.write_exact(&buf)?; + } + } + } + Ok(()) + } +} + +impl Writer for BinaryWriter { + fn write_start_array(&mut self, _len: Option) -> Result<(), Error> { + self.write_start_collection(CollectionType::Array) + } + fn write_start_dictionary(&mut self, _len: Option) -> Result<(), Error> { + self.write_start_collection(CollectionType::Dictionary) + } + fn write_end_collection(&mut self) -> Result<(), Error> { + self.write_end_collection() + } + + fn write_boolean(&mut self, value: bool) -> Result<(), Error> { + self.write_value(Value::Boolean(value)) + } + fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { + self.write_value(Value::Data(Cow::Borrowed(value))) + } + fn write_date(&mut self, value: Date) -> Result<(), Error> { + self.write_value(Value::Date(value)) + } + fn write_integer(&mut self, value: Integer) -> Result<(), Error> { + self.write_value(Value::Integer(value)) + } + fn write_real(&mut self, value: f64) -> Result<(), Error> { + self.write_value(Value::Real(value.to_bits())) + } + fn write_string(&mut self, value: &str) -> Result<(), Error> { + self.write_value(Value::String(Cow::Borrowed(value))) + } + fn write_uid(&mut self, value: Uid) -> Result<(), Error> { + self.write_value(Value::Uid(value)) + } +} + +fn is_even(value: usize) -> bool { + value & 1 == 0 +} + +fn value_mut<'a>( + values: &'a mut IndexMap, ValueState>, + value_index: usize, +) -> (&'a mut Value<'static>, &'a mut ValueState) { + values + .get_index_mut(value_index) + .expect("internal consistency error") +} + +fn write_plist_value_ty_and_size( + writer: &mut PosWriter, + token: u8, + size: usize, +) -> Result<(), Error> { + if size < 0x0f { + writer.write_exact(&[token | (size as u8)])?; + } else if size <= u8::max_value() as usize { + writer.write_exact(&[token | 0x0f, 0x10, size as u8])?; + } else if size <= u16::max_value() as usize { + let mut buf: [_; 4] = [token | 0x0f, 0x11, 0, 0]; + buf[2..].copy_from_slice(&(size as u16).to_be_bytes()); + writer.write_exact(&buf)?; + } else if size <= u32::max_value() as usize { + let mut buf: [_; 6] = [token | 0x0f, 0x12, 0, 0, 0, 0]; + buf[2..].copy_from_slice(&(size as u32).to_be_bytes()); + writer.write_exact(&buf)?; + } else { + let mut buf: [_; 10] = [token | 0x0f, 0x13, 0, 0, 0, 0, 0, 0, 0, 0]; + buf[2..].copy_from_slice(&(size as u64).to_be_bytes()); + writer.write_exact(&buf)?; + } + Ok(()) +} + +fn plist_ref_size(max_value: usize) -> u8 { + let significant_bits = 64 - (max_value as u64).leading_zeros() as u8; + // Convert to number of bytes + let significant_bytes = (significant_bits + 7) / 8; + // Round up to the next integer byte size which must be power of two. + significant_bytes.next_power_of_two() +} + +fn write_plist_ref( + writer: &mut PosWriter, + ref_size: u8, + value: usize, +) -> Result<(), Error> { + match ref_size { + 1 => writer.write_exact(&[value as u8]), + 2 => writer.write_exact(&(value as u16).to_be_bytes()), + 4 => writer.write_exact(&(value as u32).to_be_bytes()), + 8 => writer.write_exact(&(value as u64).to_be_bytes()), + _ => unreachable!("`ref_size` is a power of two less than or equal to 8"), + } +} + +impl PosWriter { + fn write_exact(&mut self, buf: &[u8]) -> Result<(), Error> { + self.write_all(buf) + .map_err(error::from_io_without_position)?; + Ok(()) + } +} + +impl Write for PosWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let count = self.writer.write(buf)?; + self.pos = self + .pos + .checked_add(count) + .expect("binary plist cannot be larger than `usize::max_value()` bytes"); + Ok(count) + } + + fn flush(&mut self) -> io::Result<()> { + self.writer.flush() + } +} + +impl ObjectRef { + fn zero() -> ObjectRef { + ObjectRef(NonZeroUsize::new(1).unwrap()) + } + + fn clone_and_increment_self(&mut self) -> ObjectRef { + let current = self.0; + self.0 = NonZeroUsize::new(current.get() + 1).unwrap(); + ObjectRef(current) + } + + fn value(&self) -> usize { + self.0.get() - 1 + } +} + +impl<'a> Value<'a> { + fn into_owned(self) -> Value<'static> { + match self { + Value::Boolean(v) => Value::Boolean(v), + Value::Data(v) => Value::Data(Cow::Owned(v.into_owned())), + Value::Date(v) => Value::Date(v), + Value::Integer(v) => Value::Integer(v), + Value::Real(v) => Value::Real(v), + Value::String(v) => Value::String(Cow::Owned(v.into_owned())), + Value::Uid(v) => Value::Uid(v), + } + } + + fn event_kind(&self) -> EventKind { + match self { + Value::Boolean(_) => EventKind::Boolean, + Value::Data(_) => EventKind::Data, + Value::Date(_) => EventKind::Date, + Value::Integer(_) => EventKind::Integer, + Value::Real(_) => EventKind::Real, + Value::String(_) => EventKind::String, + Value::Uid(_) => EventKind::Uid, + } + } +} + +#[cfg(test)] +mod tests { + use std::{fs::File, io::Cursor, path::Path}; + + use crate::{stream::BinaryReader, Value}; + + fn test_roundtrip(path: &Path) { + let reader = File::open(path).unwrap(); + let streaming_parser = BinaryReader::new(reader); + let value_to_encode = Value::from_events(streaming_parser).unwrap(); + + let mut buf = Cursor::new(Vec::new()); + value_to_encode.to_writer_binary(&mut buf).unwrap(); + + let buf_inner = buf.into_inner(); + + let streaming_parser = BinaryReader::new(Cursor::new(buf_inner)); + + let events: Vec> = streaming_parser.collect(); + let value_decoded_from_encode = Value::from_events(events.into_iter()).unwrap(); + + assert_eq!(value_to_encode, value_decoded_from_encode); + } + + #[test] + fn bplist_roundtrip() { + test_roundtrip(&Path::new("./tests/data/binary.plist")) + } + + #[test] + fn utf16_roundtrip() { + test_roundtrip(&Path::new("./tests/data/utf16_bplist.plist")) + } + + #[test] + fn nskeyedarchiver_roundtrip() { + test_roundtrip(&Path::new("./tests/data/binary_NSKeyedArchiver.plist")) + } +} diff -Nru rust-plist-0.4.2/src/stream/mod.rs rust-plist-0.5.4/src/stream/mod.rs --- rust-plist-0.4.2/src/stream/mod.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/src/stream/mod.rs 2019-07-31 15:26:13.000000000 +0000 @@ -3,14 +3,25 @@ mod binary_reader; pub use self::binary_reader::BinaryReader; +mod binary_writer; +pub use self::binary_writer::BinaryWriter; + mod xml_reader; pub use self::xml_reader::XmlReader; mod xml_writer; pub use self::xml_writer::XmlWriter; -use std::io::{Read, Seek, SeekFrom}; -use {Date, Error}; +use std::{ + io::{self, Read, Seek, SeekFrom}, + vec, +}; + +use crate::{ + dictionary, + error::{Error, ErrorKind}, + Date, Integer, Uid, Value, +}; /// An encoding of a plist as a flat structure. /// @@ -20,10 +31,10 @@ /// /// ```ignore rust /// StartDictionary -/// StringValue("Height") // Key -/// RealValue(181.2) // Value -/// StringValue("Age") // Key -/// IntegerValue(28) // Value +/// String("Height") // Key +/// Real(181.2) // Value +/// String("Age") // Key +/// Integer(28) // Value /// EndDictionary /// ``` #[derive(Clone, Debug, PartialEq)] @@ -31,17 +42,96 @@ // While the length of an array or dict cannot be feasably greater than max(usize) this better // conveys the concept of an effectively unbounded event stream. StartArray(Option), - EndArray, - StartDictionary(Option), - EndDictionary, + EndCollection, + + Boolean(bool), + Data(Vec), + Date(Date), + Integer(Integer), + Real(f64), + String(String), + Uid(Uid), + + #[doc(hidden)] + __Nonexhaustive, +} + +/// An `Event` stream returned by `Value::into_events`. +pub struct IntoEvents { + stack: Vec, +} - BooleanValue(bool), - DataValue(Vec), - DateValue(Date), - IntegerValue(i64), - RealValue(f64), - StringValue(String), +enum StackItem { + Root(Value), + Array(vec::IntoIter), + Dict(dictionary::IntoIter), + DictValue(Value), +} + +impl IntoEvents { + pub(crate) fn new(value: Value) -> IntoEvents { + IntoEvents { + stack: vec![StackItem::Root(value)], + } + } +} + +impl Iterator for IntoEvents { + type Item = Event; + + fn next(&mut self) -> Option { + fn handle_value(value: Value, stack: &mut Vec) -> Event { + match value { + Value::Array(array) => { + let len = array.len(); + let iter = array.into_iter(); + stack.push(StackItem::Array(iter)); + Event::StartArray(Some(len as u64)) + } + Value::Dictionary(dict) => { + let len = dict.len(); + let iter = dict.into_iter(); + stack.push(StackItem::Dict(iter)); + Event::StartDictionary(Some(len as u64)) + } + Value::Boolean(value) => Event::Boolean(value), + Value::Data(value) => Event::Data(value), + Value::Date(value) => Event::Date(value), + Value::Real(value) => Event::Real(value), + Value::Integer(value) => Event::Integer(value), + Value::String(value) => Event::String(value), + Value::Uid(value) => Event::Uid(value), + Value::__Nonexhaustive => unreachable!(), + } + } + + Some(match self.stack.pop()? { + StackItem::Root(value) => handle_value(value, &mut self.stack), + StackItem::Array(mut array) => { + if let Some(value) = array.next() { + // There might still be more items in the array so return it to the stack. + self.stack.push(StackItem::Array(array)); + handle_value(value, &mut self.stack) + } else { + Event::EndCollection + } + } + StackItem::Dict(mut dict) => { + if let Some((key, value)) = dict.next() { + // There might still be more items in the dictionary so return it to the stack. + self.stack.push(StackItem::Dict(dict)); + // The next event to be returned must be the dictionary value. + self.stack.push(StackItem::DictValue(value)); + // Return the key event now. + Event::String(key) + } else { + Event::EndCollection + } + } + StackItem::DictValue(value) => handle_value(value, &mut self.stack), + }) + } } pub struct Reader(ReaderInner); @@ -58,10 +148,14 @@ } fn is_binary(reader: &mut R) -> Result { - reader.seek(SeekFrom::Start(0))?; + 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)?; - reader.seek(SeekFrom::Start(0))?; + 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") } @@ -93,6 +187,41 @@ } /// Supports writing event streams in different plist encodings. -pub trait Writer { - fn write(&mut self, event: &Event) -> Result<(), Error>; +pub trait Writer: private::Sealed { + fn write(&mut self, event: &Event) -> Result<(), Error> { + match event { + Event::StartArray(len) => self.write_start_array(*len), + Event::StartDictionary(len) => self.write_start_dictionary(*len), + Event::EndCollection => self.write_end_collection(), + Event::Boolean(value) => self.write_boolean(*value), + Event::Data(value) => self.write_data(value), + Event::Date(value) => self.write_date(*value), + Event::Integer(value) => self.write_integer(*value), + Event::Real(value) => self.write_real(*value), + Event::String(value) => self.write_string(value), + Event::Uid(value) => self.write_uid(*value), + Event::__Nonexhaustive => unreachable!(), + } + } + + fn write_start_array(&mut self, len: Option) -> Result<(), Error>; + fn write_start_dictionary(&mut self, len: Option) -> Result<(), Error>; + fn write_end_collection(&mut self) -> Result<(), Error>; + + fn write_boolean(&mut self, value: bool) -> Result<(), Error>; + fn write_data(&mut self, value: &[u8]) -> Result<(), Error>; + fn write_date(&mut self, value: Date) -> Result<(), Error>; + fn write_integer(&mut self, value: Integer) -> Result<(), Error>; + fn write_real(&mut self, value: f64) -> Result<(), Error>; + fn write_string(&mut self, value: &str) -> Result<(), Error>; + fn write_uid(&mut self, value: Uid) -> Result<(), Error>; +} + +pub(crate) mod private { + use std::io::Write; + + pub trait Sealed {} + + impl Sealed for super::BinaryWriter {} + impl Sealed for super::XmlWriter {} } diff -Nru rust-plist-0.4.2/src/stream/xml_reader.rs rust-plist-0.5.4/src/stream/xml_reader.rs --- rust-plist-0.4.2/src/stream/xml_reader.rs 2019-06-11 21:20:38.000000000 +0000 +++ rust-plist-0.5.4/src/stream/xml_reader.rs 2020-04-06 14:25:03.000000000 +0000 @@ -1,10 +1,21 @@ use base64; -use std::io::Read; -use std::str::FromStr; -use xml_rs::reader::{EventReader, ParserConfig, XmlEvent}; - -use stream::Event; -use {Date, Error}; +use std::{ + io::{self, Read}, + str::FromStr, +}; +use xml_rs::{ + common::{is_whitespace_str, Position}, + reader::{ + Error as XmlReaderError, ErrorKind as XmlReaderErrorKind, EventReader, ParserConfig, + XmlEvent, + }, +}; + +use crate::{ + error::{Error, ErrorKind, FilePosition}, + stream::Event, + Date, Integer, +}; pub struct XmlReader { xml_reader: EventReader, @@ -30,112 +41,131 @@ } } - fn read_content(&mut self, f: F) -> Result - where - F: FnOnce(String) -> Result, - { - match self.xml_reader.next() { - Ok(XmlEvent::Characters(s)) => f(s), - Ok(event @ XmlEvent::EndElement { .. }) => { - self.queued_event = Some(event); - f("".to_owned()) + fn read_content(&mut self) -> Result { + loop { + match self.xml_reader.next() { + Ok(XmlEvent::Characters(s)) => return Ok(s), + Ok(event @ XmlEvent::EndElement { .. }) => { + self.queued_event = Some(event); + return Ok("".to_owned()); + } + Ok(XmlEvent::EndDocument) => { + return Err(self.with_pos(ErrorKind::UnclosedXmlElement)) + } + Ok(XmlEvent::StartElement { .. }) => { + return Err(self.with_pos(ErrorKind::UnexpectedXmlOpeningTag)); + } + Ok(XmlEvent::ProcessingInstruction { .. }) => (), + Ok(XmlEvent::StartDocument { .. }) + | Ok(XmlEvent::CData(_)) + | Ok(XmlEvent::Comment(_)) + | Ok(XmlEvent::Whitespace(_)) => { + unreachable!("parser does not output CData, Comment or Whitespace events"); + } + Err(err) => return Err(from_xml_error(err)), } - _ => Err(Error::InvalidData), } } - fn next_event(&mut self) -> ::std::result::Result { + fn next_event(&mut self) -> Result { if let Some(event) = self.queued_event.take() { Ok(event) } else { - self.xml_reader.next().map_err(|_| ()) + self.xml_reader.next() } } - fn read_next(&mut self) -> Option> { + fn read_next(&mut self) -> Result, Error> { loop { match self.next_event() { + Ok(XmlEvent::StartDocument { .. }) => {} Ok(XmlEvent::StartElement { name, .. }) => { // Add the current element to the element stack self.element_stack.push(name.local_name.clone()); match &name.local_name[..] { "plist" => (), - "array" => return Some(Ok(Event::StartArray(None))), - "dict" => return Some(Ok(Event::StartDictionary(None))), - "key" => return Some(self.read_content(|s| Ok(Event::StringValue(s)))), - "true" => return Some(Ok(Event::BooleanValue(true))), - "false" => return Some(Ok(Event::BooleanValue(false))), + "array" => return Ok(Some(Event::StartArray(None))), + "dict" => return Ok(Some(Event::StartDictionary(None))), + "key" => return Ok(Some(Event::String(self.read_content()?))), + "true" => return Ok(Some(Event::Boolean(true))), + "false" => return Ok(Some(Event::Boolean(false))), "data" => { - return Some(self.read_content(|mut s| { - // Strip whitespace and line endings from input string - s.retain(|c| !c.is_ascii_whitespace()); - let data = base64::decode(&s).map_err(|_| Error::InvalidData)?; - Ok(Event::DataValue(data)) - })); + let mut s = self.read_content()?; + // Strip whitespace and line endings from input string + s.retain(|c| !c.is_ascii_whitespace()); + let data = base64::decode(&s) + .map_err(|_| self.with_pos(ErrorKind::InvalidDataString))?; + return Ok(Some(Event::Data(data))); } "date" => { - return Some(self.read_content(|s| { - Ok(Event::DateValue( - Date::from_rfc3339(&s).map_err(|()| Error::InvalidData)?, - )) - })); + let s = self.read_content()?; + let date = Date::from_rfc3339(&s) + .map_err(|()| self.with_pos(ErrorKind::InvalidDateString))?; + return Ok(Some(Event::Date(date))); } "integer" => { - return Some(self.read_content(|s| { - if s.starts_with("0x") { - // NetBSD dialect adds the `0x` numeric objects, - // which are always unsigned. - // See the `PROP_NUMBER(3)` man page - let s = s.trim_left_matches("0x"); - Ok(Event::IntegerValue( - i64::from_str_radix(s, 16) - .map_err(|_| Error::InvalidData)?, - )) - } else { - Ok(Event::IntegerValue( - i64::from_str(&s).map_err(|_| Error::InvalidData)?, - )) + let s = self.read_content()?; + match Integer::from_str(&s) { + Ok(i) => return Ok(Some(Event::Integer(i))), + Err(_) => { + return Err(self.with_pos(ErrorKind::InvalidIntegerString)) } - })); + } } "real" => { - return Some(self.read_content(|s| match FromStr::from_str(&s) { - Ok(f) => Ok(Event::RealValue(f)), - Err(_) => Err(Error::InvalidData), - })); + let s = self.read_content()?; + match f64::from_str(&s) { + Ok(f) => return Ok(Some(Event::Real(f))), + Err(_) => return Err(self.with_pos(ErrorKind::InvalidRealString)), + } } - "string" => return Some(self.read_content(|s| Ok(Event::StringValue(s)))), - _ => return Some(Err(Error::InvalidData)), + "string" => return Ok(Some(Event::String(self.read_content()?))), + _ => return Err(self.with_pos(ErrorKind::UnknownXmlElement)), } } Ok(XmlEvent::EndElement { name, .. }) => { // Check the corrent element is being closed match self.element_stack.pop() { Some(ref open_name) if &name.local_name == open_name => (), - Some(ref _open_name) => return Some(Err(Error::InvalidData)), - None => return Some(Err(Error::InvalidData)), + Some(ref _open_name) => { + return Err(self.with_pos(ErrorKind::UnclosedXmlElement)) + } + None => return Err(self.with_pos(ErrorKind::UnpairedXmlClosingTag)), } match &name.local_name[..] { - "array" => return Some(Ok(Event::EndArray)), - "dict" => return Some(Ok(Event::EndDictionary)), - "plist" => (), - _ => (), + "array" | "dict" => return Ok(Some(Event::EndCollection)), + "plist" | _ => (), } } Ok(XmlEvent::EndDocument) => { if self.element_stack.is_empty() { - return None; + return Ok(None); } else { - return Some(Err(Error::UnexpectedEof)); + return Err(self.with_pos(ErrorKind::UnclosedXmlElement)); + } + } + + Ok(XmlEvent::Characters(c)) => { + if !is_whitespace_str(&c) { + return Err( + self.with_pos(ErrorKind::UnexpectedXmlCharactersExpectedElement) + ); } } - Err(_) => return Some(Err(Error::InvalidData)), - _ => (), + Ok(XmlEvent::CData(_)) | Ok(XmlEvent::Comment(_)) | Ok(XmlEvent::Whitespace(_)) => { + unreachable!("parser does not output CData, Comment or Whitespace events") + } + Ok(XmlEvent::ProcessingInstruction { .. }) => (), + Err(err) => return Err(from_xml_error(err)), } } } + + fn with_pos(&self, kind: ErrorKind) -> Error { + kind.with_position(convert_xml_pos(self.xml_reader.position())) + } } impl Iterator for XmlReader { @@ -146,29 +176,53 @@ None } else { match self.read_next() { - Some(Ok(event)) => Some(Ok(event)), - Some(Err(err)) => { + Ok(Some(event)) => Some(Ok(event)), + Ok(None) => { self.finished = true; - Some(Err(err)) + None } - None => { + Err(err) => { self.finished = true; - None + Some(Err(err)) } } } } } +fn convert_xml_pos(pos: xml_rs::common::TextPosition) -> FilePosition { + // TODO: pos.row and pos.column counts from 0. what do we want to do? + FilePosition::LineColumn(pos.row, pos.column) +} + +fn from_xml_error(err: XmlReaderError) -> Error { + let kind = match err.kind() { + XmlReaderErrorKind::Io(err) if err.kind() == io::ErrorKind::UnexpectedEof => { + ErrorKind::UnexpectedEof + } + XmlReaderErrorKind::Io(err) => { + let err = if let Some(code) = err.raw_os_error() { + io::Error::from_raw_os_error(code) + } else { + io::Error::new(err.kind(), err.to_string()) + }; + ErrorKind::Io(err) + } + XmlReaderErrorKind::Syntax(_) => ErrorKind::InvalidXmlSyntax, + XmlReaderErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof, + XmlReaderErrorKind::Utf8(_) => ErrorKind::InvalidXmlUtf8, + }; + + kind.with_position(convert_xml_pos(err.position())) +} + #[cfg(test)] mod tests { use humantime::parse_rfc3339_weak; - use std::fs::File; - use std::path::Path; + use std::{fs::File, path::Path}; use super::*; - use stream::Event; - use stream::Event::*; + use crate::stream::Event::{self, *}; #[test] fn streaming_parser() { @@ -178,26 +232,34 @@ let comparison = &[ StartDictionary(None), - StringValue("Author".to_owned()), - StringValue("William Shakespeare".to_owned()), - StringValue("Lines".to_owned()), + String("Author".to_owned()), + String("William Shakespeare".to_owned()), + String("Lines".to_owned()), StartArray(None), - StringValue("It is a tale told by an idiot,".to_owned()), - StringValue("Full of sound and fury, signifying nothing.".to_owned()), - EndArray, - StringValue("Death".to_owned()), - IntegerValue(1564), - StringValue("Height".to_owned()), - RealValue(1.60), - StringValue("Data".to_owned()), - DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), - StringValue("Birthdate".to_owned()), - DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), - StringValue("Blank".to_owned()), - StringValue("".to_owned()), - StringValue("HexademicalNumber".to_owned()), - IntegerValue(0xdead_beef_i64), - EndDictionary, + String("It is a tale told by an idiot,".to_owned()), + String("Full of sound and fury, signifying nothing.".to_owned()), + EndCollection, + String("Death".to_owned()), + Integer(1564.into()), + String("Height".to_owned()), + Real(1.60), + String("Data".to_owned()), + Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), + String("Birthdate".to_owned()), + Date(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), + String("Blank".to_owned()), + String("".to_owned()), + String("BiggestNumber".to_owned()), + Integer(18446744073709551615u64.into()), + String("SmallestNumber".to_owned()), + Integer((-9223372036854775808i64).into()), + String("HexademicalNumber".to_owned()), + Integer(0xdead_beef_u64.into()), + String("IsTrue".into()), + Boolean(true), + String("IsNotFalse".into()), + Boolean(false), + EndCollection, ]; assert_eq!(events, comparison); diff -Nru rust-plist-0.4.2/src/stream/xml_writer.rs rust-plist-0.5.4/src/stream/xml_writer.rs --- rust-plist-0.4.2/src/stream/xml_writer.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/src/stream/xml_writer.rs 2020-04-06 14:31:44.000000000 +0000 @@ -1,28 +1,23 @@ use base64; use line_wrap; -use std::borrow::Cow; -use std::io::Write; -use xml_rs::name::Name; -use xml_rs::namespace::Namespace; -use xml_rs::writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent}; - -use stream::{Event, Writer}; -use Error; +use std::{borrow::Cow, io::Write}; +use xml_rs::{ + name::Name, + namespace::Namespace, + writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent}, +}; + +use crate::{ + error::{self, Error, ErrorKind, EventKind}, + stream::Writer, + Date, Integer, Uid, +}; static XML_PROLOGUE: &str = r#" "#; -impl From for Error { - fn from(err: XmlWriterError) -> Error { - match err { - XmlWriterError::Io(err) => Error::Io(err), - _ => Error::InvalidData, - } - } -} - #[derive(PartialEq)] enum Element { Dictionary, @@ -67,112 +62,185 @@ } fn start_element(&mut self, name: &str) -> Result<(), Error> { - self.xml_writer.write(XmlEvent::StartElement { - name: Name::local(name), - attributes: Cow::Borrowed(&[]), - namespace: Cow::Borrowed(&self.empty_namespace), - })?; + self.xml_writer + .write(XmlEvent::StartElement { + name: Name::local(name), + attributes: Cow::Borrowed(&[]), + namespace: Cow::Borrowed(&self.empty_namespace), + }) + .map_err(from_xml_error)?; Ok(()) } fn end_element(&mut self, name: &str) -> Result<(), Error> { - self.xml_writer.write(XmlEvent::EndElement { - name: Some(Name::local(name)), - })?; + self.xml_writer + .write(XmlEvent::EndElement { + name: Some(Name::local(name)), + }) + .map_err(from_xml_error)?; Ok(()) } fn write_value(&mut self, value: &str) -> Result<(), Error> { - self.xml_writer.write(XmlEvent::Characters(value))?; + self.xml_writer + .write(XmlEvent::Characters(value)) + .map_err(from_xml_error)?; Ok(()) } - pub fn write(&mut self, event: &Event) -> Result<(), Error> { - ::write(self, event) - } - pub fn into_inner(self) -> W { self.xml_writer.into_inner() } -} -impl Writer for XmlWriter { - fn write(&mut self, event: &Event) -> Result<(), Error> { + fn write_event Result<(), Error>>( + &mut self, + f: F, + ) -> Result<(), Error> { if !self.written_prologue { self.xml_writer .inner_mut() - .write_all(XML_PROLOGUE.as_bytes())?; + .write_all(XML_PROLOGUE.as_bytes()) + .map_err(error::from_io_without_position)?; self.written_prologue = true; } - if self.expecting_key { - match *event { - Event::EndDictionary => match self.stack.pop() { - Some(Element::Dictionary) => { - self.end_element("dict")?; - self.expecting_key = self.stack.last() == Some(&Element::Dictionary); - } - _ => return Err(Error::InvalidData), - }, - Event::StringValue(ref value) => { - self.write_element_and_value("key", &*value)?; - self.expecting_key = false; + f(self)?; + + // If there are no more open tags then write the element + if self.stack.is_empty() { + // We didn't tell the xml_writer about the tag so we'll skip telling it + // about the tag as well. + self.xml_writer + .inner_mut() + .write_all(b"\n") + .map_err(error::from_io_without_position)?; + self.xml_writer + .inner_mut() + .flush() + .map_err(error::from_io_without_position)?; + } + + Ok(()) + } + + fn write_value_event Result<(), Error>>( + &mut self, + event_kind: EventKind, + f: F, + ) -> Result<(), Error> { + self.write_event(|this| { + if this.expecting_key { + return Err(ErrorKind::UnexpectedEventType { + expected: EventKind::DictionaryKeyOrEndCollection, + found: event_kind, } - _ => return Err(Error::InvalidData), + .without_position()); } - } else { - match *event { - Event::StartArray(_) => { - self.start_element("array")?; - self.stack.push(Element::Array); - } - Event::EndArray => match self.stack.pop() { - Some(Element::Array) => self.end_element("array")?, - _ => return Err(Error::InvalidData), - }, - - Event::StartDictionary(_) => { - self.start_element("dict")?; - self.stack.push(Element::Dictionary); - } - Event::EndDictionary => return Err(Error::InvalidData), + f(this)?; + this.expecting_key = this.stack.last() == Some(&Element::Dictionary); + Ok(()) + }) + } +} - Event::BooleanValue(true) => { - self.start_element("true")?; - self.end_element("true")?; - } - Event::BooleanValue(false) => { - self.start_element("false")?; - self.end_element("false")?; - } - Event::DataValue(ref value) => { - let base64_data = base64_encode_plist(&value, self.stack.len()); - self.write_element_and_value("data", &base64_data)?; - } - Event::DateValue(ref value) => { - self.write_element_and_value("date", &value.to_rfc3339())? +impl Writer for XmlWriter { + fn write_start_array(&mut self, _len: Option) -> Result<(), Error> { + self.write_value_event(EventKind::StartArray, |this| { + this.start_element("array")?; + this.stack.push(Element::Array); + Ok(()) + }) + } + + fn write_start_dictionary(&mut self, _len: Option) -> Result<(), Error> { + self.write_value_event(EventKind::StartDictionary, |this| { + this.start_element("dict")?; + this.stack.push(Element::Dictionary); + Ok(()) + }) + } + + fn write_end_collection(&mut self) -> Result<(), Error> { + self.write_event(|this| { + match (this.stack.pop(), this.expecting_key) { + (Some(Element::Dictionary), true) => { + this.end_element("dict")?; } - Event::IntegerValue(ref value) => { - self.write_element_and_value("integer", &value.to_string())? + (Some(Element::Array), _) => { + this.end_element("array")?; } - Event::RealValue(ref value) => { - self.write_element_and_value("real", &value.to_string())? + (Some(Element::Dictionary), false) | (None, _) => { + return Err(ErrorKind::UnexpectedEventType { + expected: EventKind::ValueOrStartCollection, + found: EventKind::EndCollection, + } + .without_position()); } - Event::StringValue(ref value) => self.write_element_and_value("string", &*value)?, - }; + } + this.expecting_key = this.stack.last() == Some(&Element::Dictionary); + Ok(()) + }) + } - self.expecting_key = self.stack.last() == Some(&Element::Dictionary); - } + fn write_boolean(&mut self, value: bool) -> Result<(), Error> { + self.write_value_event(EventKind::Boolean, |this| { + let value_str = if value { "true" } else { "false" }; + this.start_element(value_str)?; + this.end_element(value_str) + }) + } - // If there are no more open tags then write the element - if self.stack.len() == 0 { - // We didn't tell the xml_writer about the tag so we'll skip telling it - // about the tag as well. - self.xml_writer.inner_mut().write_all(b"\n")?; - } + fn write_data(&mut self, value: &[u8]) -> Result<(), Error> { + self.write_value_event(EventKind::Data, |this| { + let base64_data = base64_encode_plist(&value, this.stack.len()); + this.write_element_and_value("data", &base64_data) + }) + } - Ok(()) + fn write_date(&mut self, value: Date) -> Result<(), Error> { + self.write_value_event(EventKind::Date, |this| { + this.write_element_and_value("date", &value.to_rfc3339()) + }) + } + + fn write_integer(&mut self, value: Integer) -> Result<(), Error> { + self.write_value_event(EventKind::Integer, |this| { + this.write_element_and_value("integer", &value.to_string()) + }) + } + + fn write_real(&mut self, value: f64) -> Result<(), Error> { + self.write_value_event(EventKind::Real, |this| { + this.write_element_and_value("real", &value.to_string()) + }) + } + + fn write_string(&mut self, value: &str) -> Result<(), Error> { + self.write_event(|this| { + if this.expecting_key { + this.write_element_and_value("key", &*value)?; + this.expecting_key = false; + } else { + this.write_element_and_value("string", &*value)?; + this.expecting_key = this.stack.last() == Some(&Element::Dictionary); + } + Ok(()) + }) + } + + fn write_uid(&mut self, _value: Uid) -> Result<(), Error> { + Err(ErrorKind::UidNotSupportedInXmlPlist.without_position()) + } +} + +pub(crate) fn from_xml_error(err: XmlWriterError) -> Error { + match err { + XmlWriterError::Io(err) => ErrorKind::Io(err).without_position(), + XmlWriterError::DocumentStartAlreadyEmitted + | XmlWriterError::LastElementNameNotAvailable + | XmlWriterError::EndElementNameIsNotEqualToLastStartElementName + | XmlWriterError::EndElementNameIsNotSpecified => unreachable!(), } } @@ -198,7 +266,7 @@ let mut output = vec![0; base64_max_string_len_with_formatting]; // Start output with a line ending and indent - &mut output[..line_ending.len()].copy_from_slice(&line_ending); + output[..line_ending.len()].copy_from_slice(&line_ending); // Encode `data` as a base 64 string let base64_string_len = @@ -213,7 +281,7 @@ ); // Add the final line ending and indent - &mut output[line_ending.len() + base64_string_len + line_wrap_len..][..line_ending.len()] + output[line_ending.len() + base64_string_len + line_wrap_len..][..line_ending.len()] .copy_from_slice(&line_ending); // Ensure output is the correct length @@ -227,31 +295,39 @@ use std::io::Cursor; use super::*; - use stream::Event::*; + use crate::stream::Event; #[test] fn streaming_parser() { let plist = &[ - StartDictionary(None), - StringValue("Author".to_owned()), - StringValue("William Shakespeare".to_owned()), - StringValue("Lines".to_owned()), - StartArray(None), - StringValue("It is a tale told by an idiot,".to_owned()), - StringValue("Full of sound and fury, signifying nothing.".to_owned()), - DataValue((0..128).collect::>()), - EndArray, - StringValue("Death".to_owned()), - IntegerValue(1564), - StringValue("Height".to_owned()), - RealValue(1.60), - StringValue("Data".to_owned()), - DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), - StringValue("Birthdate".to_owned()), - DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), - StringValue("Comment".to_owned()), - StringValue("2 < 3".to_owned()), // make sure characters are escaped - EndDictionary, + Event::StartDictionary(None), + Event::String("Author".to_owned()), + Event::String("William Shakespeare".to_owned()), + Event::String("Lines".to_owned()), + Event::StartArray(None), + Event::String("It is a tale told by an idiot,".to_owned()), + Event::String("Full of sound and fury, signifying nothing.".to_owned()), + Event::Data((0..128).collect::>()), + Event::EndCollection, + Event::String("Death".to_owned()), + Event::Integer(1564.into()), + Event::String("Height".to_owned()), + Event::Real(1.60), + Event::String("Data".to_owned()), + Event::Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]), + Event::String("Birthdate".to_owned()), + Event::Date(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()), + Event::String("Comment".to_owned()), + Event::String("2 < 3".to_owned()), // make sure characters are escaped + Event::String("BiggestNumber".to_owned()), + Event::Integer(18446744073709551615u64.into()), + Event::String("SmallestNumber".to_owned()), + Event::Integer((-9223372036854775808i64).into()), + Event::String("IsTrue".into()), + Event::Boolean(true), + Event::String("IsNotFalse".into()), + Event::Boolean(false), + Event::EndCollection, ]; let mut cursor = Cursor::new(Vec::new()); @@ -292,6 +368,14 @@ \t1981-05-16T11:32:06Z \tComment \t2 < 3 +\tBiggestNumber +\t18446744073709551615 +\tSmallestNumber +\t-9223372036854775808 +\tIsTrue +\t +\tIsNotFalse +\t "; diff -Nru rust-plist-0.4.2/src/uid.rs rust-plist-0.5.4/src/uid.rs --- rust-plist-0.4.2/src/uid.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/src/uid.rs 2019-07-23 10:38:13.000000000 +0000 @@ -0,0 +1,90 @@ +use std::fmt; + +/// A plist `uid` value. These are found exclusively in plists created by `NSKeyedArchiver`. +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub struct Uid { + value: u64, +} + +impl Uid { + /// Creates a new `Uid` containing the given value. + pub fn new(value: u64) -> Uid { + Uid { value } + } + + /// Returns the value as a `u64`. + pub fn get(self) -> u64 { + self.value + } +} + +impl fmt::Debug for Uid { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.value.fmt(f) + } +} + +#[cfg(feature = "serde")] +pub mod serde_impls { + use serde::{ + de::{Deserialize, Deserializer, Error, Visitor}, + ser::{Serialize, Serializer}, + }; + use std::fmt; + + use crate::Uid; + + pub const UID_NEWTYPE_STRUCT_NAME: &str = "PLIST-UID"; + + impl Serialize for Uid { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_newtype_struct(UID_NEWTYPE_STRUCT_NAME, &self.get()) + } + } + + struct UidNewtypeVisitor; + + impl<'de> Visitor<'de> for UidNewtypeVisitor { + type Value = Uid; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a plist uid") + } + + fn visit_newtype_struct(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(UidU64Visitor) + } + } + + struct UidU64Visitor; + + impl<'de> Visitor<'de> for UidU64Visitor { + type Value = Uid; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a plist uid") + } + + fn visit_u64(self, v: u64) -> Result + where + E: Error, + { + Ok(Uid::new(v)) + } + } + + impl<'de> Deserialize<'de> for Uid { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_newtype_struct(UID_NEWTYPE_STRUCT_NAME, UidNewtypeVisitor) + } + } +} diff -Nru rust-plist-0.4.2/src/value.rs rust-plist-0.5.4/src/value.rs --- rust-plist-0.4.2/src/value.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/src/value.rs 2020-03-27 22:10:27.000000000 +0000 @@ -1,51 +1,79 @@ -use std::collections::BTreeMap; -use std::fs::File; -use std::io::Write; -use std::io::{BufReader, Read, Seek}; -use std::path::Path; - -use stream::{Event, Reader, Writer, XmlReader, XmlWriter}; -use {u64_to_usize, Date, Error}; +use std::{ + fs::File, + io::{BufReader, BufWriter, Read, Seek, Write}, + path::Path, +}; + +use crate::{ + error::{self, Error, ErrorKind, EventKind}, + stream::{BinaryWriter, Event, IntoEvents, Reader, Writer, XmlReader, XmlWriter}, + u64_to_usize, Date, Dictionary, Integer, Uid, +}; /// Represents any plist value. #[derive(Clone, Debug, PartialEq)] pub enum Value { Array(Vec), - Dictionary(BTreeMap), + Dictionary(Dictionary), Boolean(bool), Data(Vec), Date(Date), Real(f64), - Integer(i64), + Integer(Integer), String(String), + Uid(Uid), + #[doc(hidden)] + __Nonexhaustive, } impl Value { /// Reads a `Value` from a plist file of any encoding. pub fn from_file>(path: P) -> Result { - let file = File::open(path)?; + 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 file of any encoding. + /// 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 file. + /// 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 the given data structure as an XML encoded plist file. + /// Serializes a `Value` to a file as a binary encoded plist. + pub fn to_file_binary>(&self, path: P) -> Result<(), Error> { + let mut file = File::create(path).map_err(error::from_io_without_position)?; + self.to_writer_binary(BufWriter::new(&mut file))?; + file.sync_all().map_err(error::from_io_without_position)?; + Ok(()) + } + + /// Serializes a `Value` to a file as an XML encoded plist. + pub fn to_file_xml>(&self, path: P) -> Result<(), Error> { + let mut file = File::create(path).map_err(error::from_io_without_position)?; + self.to_writer_xml(BufWriter::new(&mut file))?; + file.sync_all().map_err(error::from_io_without_position)?; + Ok(()) + } + + /// Serializes a `Value` to a byte stream as a binary encoded plist. + pub fn to_writer_binary(&self, writer: W) -> Result<(), Error> { + let mut writer = BinaryWriter::new(writer); + self.to_writer_inner(&mut writer) + } + + /// Serializes a `Value` to a byte stream as an XML encoded plist. pub fn to_writer_xml(&self, writer: W) -> Result<(), Error> { let mut writer = XmlWriter::new(writer); - self.to_writer_xml_inner(&mut writer) + self.to_writer_inner(&mut writer) } - fn to_writer_xml_inner(&self, writer: &mut Writer) -> Result<(), Error> { + fn to_writer_inner(&self, writer: &mut dyn Writer) -> Result<(), Error> { let events = self.clone().into_events(); for event in events { writer.write(&event)?; @@ -53,7 +81,9 @@ Ok(()) } - /// Creates a `Value` from an event source. + /// Builds a single `Value` from an `Event` iterator. + /// On success any excess `Event`s will remain in the iterator. + #[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")] pub fn from_events(events: T) -> Result where T: IntoIterator>, @@ -61,36 +91,38 @@ Builder::new(events.into_iter()).build() } - /// Converts a `Value` into an `Event` stream. - pub fn into_events(self) -> Vec { - let mut events = Vec::new(); - self.into_events_inner(&mut events); - events + /// Builds a single `Value` from an `Event` iterator. + /// On success any excess `Event`s will remain in the iterator. + #[cfg(not(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps"))] + pub(crate) fn from_events(events: T) -> Result + where + T: IntoIterator>, + { + Builder::new(events.into_iter()).build() + } + + /// Converts a `Value` into an `Event` iterator. + #[cfg(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps")] + pub fn into_events(self) -> IntoEvents { + IntoEvents::new(self) + } + + /// Converts a `Value` into an `Event` iterator. + #[cfg(not(feature = "enable_unstable_features_that_may_break_with_minor_version_bumps"))] + pub(crate) fn into_events(self) -> IntoEvents { + IntoEvents::new(self) } - fn into_events_inner(self, events: &mut Vec) { + /// 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(array) => { - events.push(Event::StartArray(Some(array.len() as u64))); - for value in array { - value.into_events_inner(events); - } - events.push(Event::EndArray); - } - Value::Dictionary(dict) => { - events.push(Event::StartDictionary(Some(dict.len() as u64))); - for (key, value) in dict { - events.push(Event::StringValue(key)); - value.into_events_inner(events); - } - events.push(Event::EndDictionary); - } - Value::Boolean(value) => events.push(Event::BooleanValue(value)), - Value::Data(value) => events.push(Event::DataValue(value)), - Value::Date(value) => events.push(Event::DateValue(value)), - Value::Real(value) => events.push(Event::RealValue(value)), - Value::Integer(value) => events.push(Event::IntegerValue(value)), - Value::String(value) => events.push(Event::StringValue(value)), + Value::Array(dict) => Some(dict), + _ => None, } } @@ -117,9 +149,22 @@ /// If the `Value` is a Dictionary, returns the associated `BTreeMap`. /// /// Returns `None` otherwise. - pub fn as_dictionary(&self) -> Option<&BTreeMap> { + /// + /// 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 map) => Some(map), + Value::Dictionary(ref dict) => Some(dict), _ => None, } } @@ -127,9 +172,9 @@ /// If the `Value` is a Dictionary, returns the associated mutable `BTreeMap`. /// /// Returns `None` otherwise. - pub fn as_dictionary_mut(&mut self) -> Option<&mut BTreeMap> { + pub fn as_dictionary_mut(&mut self) -> Option<&mut Dictionary> { match *self { - Value::Dictionary(ref mut map) => Some(map), + Value::Dictionary(ref mut dict) => Some(dict), _ => None, } } @@ -187,12 +232,22 @@ } } - /// If the `Value` is an Integer, returns the associated `i64`. + /// 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_integer(&self) -> Option { + pub fn as_unsigned_integer(&self) -> Option { match *self { - Value::Integer(v) => Some(v), + Value::Integer(v) => v.as_unsigned(), _ => None, } } @@ -227,8 +282,8 @@ } } -impl From> for Value { - fn from(from: BTreeMap) -> Value { +impl From for Value { + fn from(from: Dictionary) -> Value { Value::Dictionary(from) } } @@ -271,43 +326,49 @@ impl From for Value { fn from(from: i64) -> Value { - Value::Integer(from) + Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: i32) -> Value { - Value::Integer(from.into()) + Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: i16) -> Value { - Value::Integer(from.into()) + Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: i8) -> Value { - Value::Integer(from.into()) + 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(from.into()) + Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u16) -> Value { - Value::Integer(from.into()) + Value::Integer(Integer::from(from)) } } impl From for Value { fn from(from: u8) -> Value { - Value::Integer(from.into()) + Value::Integer(Integer::from(from)) } } @@ -325,31 +386,37 @@ impl<'a> From<&'a i64> for Value { fn from(from: &'a i64) -> Value { - Value::Integer(*from) + Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a i32> for Value { fn from(from: &'a i32) -> Value { - Value::Integer((*from).into()) + Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a i16> for Value { fn from(from: &'a i16) -> Value { - Value::Integer((*from).into()) + Value::Integer(Integer::from(*from)) } } impl<'a> From<&'a i8> for Value { fn from(from: &'a i8) -> Value { - Value::Integer((*from).into()) + 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((*from).into()) + Value::Integer(Integer::from(*from)) } } @@ -392,14 +459,7 @@ fn build(mut self) -> Result { self.bump()?; - let plist = self.build_value()?; - - // Ensure the stream has been fully consumed - self.bump()?; - match self.token { - None => Ok(plist), - _ => Err(Error::InvalidData), - } + self.build_value() } fn bump(&mut self) -> Result<(), Error> { @@ -416,18 +476,22 @@ Some(Event::StartArray(len)) => Ok(Value::Array(self.build_array(len)?)), Some(Event::StartDictionary(len)) => Ok(Value::Dictionary(self.build_dict(len)?)), - Some(Event::BooleanValue(b)) => Ok(Value::Boolean(b)), - Some(Event::DataValue(d)) => Ok(Value::Data(d)), - Some(Event::DateValue(d)) => Ok(Value::Date(d)), - Some(Event::IntegerValue(i)) => Ok(Value::Integer(i)), - Some(Event::RealValue(f)) => Ok(Value::Real(f)), - Some(Event::StringValue(s)) => Ok(Value::String(s)), + Some(Event::Boolean(b)) => Ok(Value::Boolean(b)), + Some(Event::Data(d)) => Ok(Value::Data(d)), + Some(Event::Date(d)) => Ok(Value::Date(d)), + Some(Event::Integer(i)) => Ok(Value::Integer(i)), + Some(Event::Real(f)) => Ok(Value::Real(f)), + Some(Event::String(s)) => Ok(Value::String(s)), + Some(Event::Uid(u)) => Ok(Value::Uid(u)), + + Some(event @ Event::EndCollection) => Err(error::unexpected_event_type( + EventKind::ValueOrStartCollection, + &event, + )), - Some(Event::EndArray) => Err(Error::InvalidData), - Some(Event::EndDictionary) => Err(Error::InvalidData), + Some(Event::__Nonexhaustive) => unreachable!(), - // The stream should not have ended here - None => Err(Error::InvalidData), + None => Err(ErrorKind::UnexpectedEndOfEventStream.without_position()), } } @@ -439,7 +503,7 @@ loop { self.bump()?; - if let Some(Event::EndArray) = self.token { + if let Some(Event::EndCollection) = self.token { self.token.take(); return Ok(values); } @@ -447,21 +511,24 @@ } } - fn build_dict(&mut self, _len: Option) -> Result, Error> { - let mut values = BTreeMap::new(); + fn build_dict(&mut self, _len: Option) -> Result { + let mut dict = Dictionary::new(); loop { self.bump()?; match self.token.take() { - Some(Event::EndDictionary) => return Ok(values), - Some(Event::StringValue(s)) => { + Some(Event::EndCollection) => return Ok(dict), + Some(Event::String(s)) => { self.bump()?; - values.insert(s, self.build_value()?); + dict.insert(s, self.build_value()?); } - _ => { - // Only string keys are supported in plists - return Err(Error::InvalidData); + Some(event) => { + return Err(error::unexpected_event_type( + EventKind::DictionaryKeyOrEndCollection, + &event, + )) } + None => return Err(ErrorKind::UnexpectedEndOfEventStream.without_position()), } } } @@ -469,12 +536,10 @@ #[cfg(test)] mod tests { - use std::collections::BTreeMap; use std::time::SystemTime; use super::*; - use stream::Event::*; - use {Date, Value}; + use crate::{stream::Event::*, Date, Dictionary, Value}; #[test] fn value_accessors() { @@ -483,7 +548,7 @@ assert_eq!(array.as_array(), Some(&vec.clone())); assert_eq!(array.as_array_mut(), Some(&mut vec.clone())); - let mut map = BTreeMap::new(); + 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())); @@ -502,7 +567,13 @@ assert_eq!(Value::Date(date.clone()).as_date(), Some(date)); assert_eq!(Value::Real(0.0).as_real(), Some(0.0)); - assert_eq!(Value::Integer(1).as_integer(), Some(1)); + assert_eq!(Value::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(), @@ -515,18 +586,18 @@ // Input let events = vec![ StartDictionary(None), - StringValue("Author".to_owned()), - StringValue("William Shakespeare".to_owned()), - StringValue("Lines".to_owned()), + String("Author".to_owned()), + String("William Shakespeare".to_owned()), + String("Lines".to_owned()), StartArray(None), - StringValue("It is a tale told by an idiot,".to_owned()), - StringValue("Full of sound and fury, signifying nothing.".to_owned()), - EndArray, - StringValue("Birthdate".to_owned()), - IntegerValue(1564), - StringValue("Height".to_owned()), - RealValue(1.60), - EndDictionary, + String("It is a tale told by an idiot,".to_owned()), + String("Full of sound and fury, signifying nothing.".to_owned()), + EndCollection, + String("Birthdate".to_owned()), + Integer(1564.into()), + String("Height".to_owned()), + Real(1.60), + EndCollection, ]; let builder = Builder::new(events.into_iter().map(|e| Ok(e))); @@ -539,13 +610,13 @@ "Full of sound and fury, signifying nothing.".to_owned(), )); - let mut dict = BTreeMap::new(); + 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)); + dict.insert("Birthdate".to_owned(), Value::Integer(1564.into())); dict.insert("Height".to_owned(), Value::Real(1.60)); assert_eq!(plist.unwrap(), Value::Dictionary(dict)); Binary files /tmp/tmpfpt0s5/WKWpbgv6z0/rust-plist-0.4.2/tests/data/binary_NSKeyedArchiver.plist and /tmp/tmpfpt0s5/zGXrOfU66E/rust-plist-0.5.4/tests/data/binary_NSKeyedArchiver.plist differ Binary files /tmp/tmpfpt0s5/WKWpbgv6z0/rust-plist-0.4.2/tests/data/binary.plist and /tmp/tmpfpt0s5/zGXrOfU66E/rust-plist-0.5.4/tests/data/binary.plist differ diff -Nru rust-plist-0.4.2/tests/data/book.plist rust-plist-0.5.4/tests/data/book.plist --- rust-plist-0.4.2/tests/data/book.plist 1970-01-01 00:00:00.000000000 +0000 +++ rust-plist-0.5.4/tests/data/book.plist 2019-07-31 16:50:39.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + 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 + + diff -Nru rust-plist-0.4.2/tests/data/xml.plist rust-plist-0.5.4/tests/data/xml.plist --- rust-plist-0.4.2/tests/data/xml.plist 2019-06-10 18:15:26.000000000 +0000 +++ rust-plist-0.5.4/tests/data/xml.plist 2020-04-06 14:30:43.000000000 +0000 @@ -22,7 +22,15 @@ 1981-05-16T11:32:06Z Blank + BiggestNumber + 18446744073709551615 + SmallestNumber + -9223372036854775808 HexademicalNumber 0xDEADBEEF + IsTrue + + IsNotFalse + diff -Nru rust-plist-0.4.2/tests/fuzzer.rs rust-plist-0.5.4/tests/fuzzer.rs --- rust-plist-0.4.2/tests/fuzzer.rs 2019-06-10 16:33:53.000000000 +0000 +++ rust-plist-0.5.4/tests/fuzzer.rs 2019-07-23 10:38:13.000000000 +0000 @@ -65,7 +65,7 @@ } fn test_fuzzer_data_ok(data: &[u8]) { - assert!(test_fuzzer_data(data).is_ok()); + test_fuzzer_data(data).unwrap(); } fn test_fuzzer_data_err(data: &[u8]) { diff -Nru rust-plist-0.4.2/tests/serde_tests/mod.rs rust-plist-0.5.4/tests/serde_tests/mod.rs --- rust-plist-0.4.2/tests/serde_tests/mod.rs 2019-06-10 18:11:50.000000000 +0000 +++ rust-plist-0.5.4/tests/serde_tests/mod.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,280 +0,0 @@ -use plist::stream::Event; -use plist::stream::Event::*; -use plist::stream::Writer; -use plist::{Date, Deserializer, Error, Serializer}; -use serde::de::DeserializeOwned; -use serde::ser::Serialize; -use std::fmt::Debug; -use std::time::SystemTime; - -struct VecWriter { - events: Vec, -} - -impl VecWriter { - pub fn new() -> VecWriter { - VecWriter { events: Vec::new() } - } - - pub fn into_inner(self) -> Vec { - self.events - } -} - -impl Writer for VecWriter { - fn write(&mut self, event: &Event) -> Result<(), Error> { - self.events.push(event.clone()); - Ok(()) - } -} - -fn new_serializer() -> Serializer { - Serializer::new(VecWriter::new()) -} - -fn new_deserializer(events: Vec) -> Deserializer>> { - let result_events = events.into_iter().map(Ok).collect(); - Deserializer::new(result_events) -} - -fn assert_roundtrip(obj: T, comparison: Option<&[Event]>) -where - T: Debug + DeserializeOwned + PartialEq + Serialize, -{ - let mut se = new_serializer(); - - obj.serialize(&mut se).unwrap(); - - let events = se.into_inner().into_inner(); - - if let Some(comparison) = comparison { - assert_eq!(&events[..], comparison); - } - - let mut de = new_deserializer(events); - - let new_obj = T::deserialize(&mut de).unwrap(); - - assert_eq!(new_obj, obj); -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -enum Animal { - Cow, - Dog(DogOuter), - Frog(Result, Vec), - Cat { - age: usize, - name: String, - firmware: Option>, - }, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -struct DogOuter { - inner: Vec, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -struct DogInner { - a: (), - b: usize, - c: Vec, -} - -#[test] -fn cow() { - let cow = Animal::Cow; - - let comparison = &[ - StartDictionary(Some(1)), - StringValue("Cow".to_owned()), - StringValue("".to_owned()), - EndDictionary, - ]; - - assert_roundtrip(cow, Some(comparison)); -} - -#[test] -fn dog() { - let dog = Animal::Dog(DogOuter { - inner: vec![DogInner { - a: (), - b: 12, - c: vec!["a".to_string(), "b".to_string()], - }], - }); - - let comparison = &[ - StartDictionary(Some(1)), - StringValue("Dog".to_owned()), - StartDictionary(None), - StringValue("inner".to_owned()), - StartArray(Some(1)), - StartDictionary(None), - StringValue("a".to_owned()), - StringValue("".to_owned()), - StringValue("b".to_owned()), - IntegerValue(12), - StringValue("c".to_owned()), - StartArray(Some(2)), - StringValue("a".to_owned()), - StringValue("b".to_owned()), - EndArray, - EndDictionary, - EndArray, - EndDictionary, - EndDictionary, - ]; - - assert_roundtrip(dog, Some(comparison)); -} - -#[test] -fn frog() { - let frog = Animal::Frog( - Ok("hello".to_owned()), - vec![1.0, 2.0, 3.14159, 0.000000001, 1.27e31], - ); - - let comparison = &[ - StartDictionary(Some(1)), - StringValue("Frog".to_owned()), - StartArray(Some(2)), - StartDictionary(Some(1)), - StringValue("Ok".to_owned()), - StringValue("hello".to_owned()), - EndDictionary, - StartArray(Some(5)), - RealValue(1.0), - RealValue(2.0), - RealValue(3.14159), - RealValue(0.000000001), - RealValue(1.27e31), - EndArray, - EndArray, - EndDictionary, - ]; - - assert_roundtrip(frog, Some(comparison)); -} - -#[test] -fn cat() { - let cat = Animal::Cat { - age: 12, - name: "Paws".to_owned(), - firmware: Some(vec![0, 1, 2, 3, 4, 5, 6, 7, 8]), - }; - - let comparison = &[ - StartDictionary(Some(1)), - StringValue("Cat".to_owned()), - StartDictionary(None), - StringValue("age".to_owned()), - IntegerValue(12), - StringValue("name".to_owned()), - StringValue("Paws".to_owned()), - StringValue("firmware".to_owned()), - StartArray(Some(9)), - IntegerValue(0), - IntegerValue(1), - IntegerValue(2), - IntegerValue(3), - IntegerValue(4), - IntegerValue(5), - IntegerValue(6), - IntegerValue(7), - IntegerValue(8), - EndArray, - EndDictionary, - EndDictionary, - ]; - - assert_roundtrip(cat, Some(comparison)); -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -struct NewtypeStruct(NewtypeInner); - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -struct NewtypeInner(u8, u8, u8); - -#[test] -fn newtype_struct() { - let newtype = NewtypeStruct(NewtypeInner(34, 32, 13)); - - let comparison = &[ - StartArray(Some(3)), - IntegerValue(34), - IntegerValue(32), - IntegerValue(13), - EndArray, - ]; - - assert_roundtrip(newtype, Some(comparison)); -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -struct TypeWithOptions { - a: Option, - b: Option, - c: Option>, -} - -#[test] -fn type_with_options() { - let inner = TypeWithOptions { - a: None, - b: Some(12), - c: None, - }; - - let obj = TypeWithOptions { - a: Some("hello".to_owned()), - b: None, - c: Some(Box::new(inner)), - }; - - let comparison = &[ - StartDictionary(None), - StringValue("a".to_owned()), - StringValue("hello".to_owned()), - StringValue("c".to_owned()), - StartDictionary(None), - StringValue("b".to_owned()), - IntegerValue(12), - EndDictionary, - EndDictionary, - ]; - - assert_roundtrip(obj, Some(comparison)); -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -struct TypeWithDate { - a: Option, - b: Option, -} - -#[test] -fn type_with_date() { - let date: Date = SystemTime::now().into(); - - let obj = TypeWithDate { - a: Some(28), - b: Some(date.clone()), - }; - - let comparison = &[ - StartDictionary(None), - StringValue("a".to_owned()), - IntegerValue(28), - StringValue("b".to_owned()), - DateValue(date), - EndDictionary, - ]; - - assert_roundtrip(obj, Some(comparison)); -} diff -Nru rust-plist-0.4.2/tests/tests.rs rust-plist-0.5.4/tests/tests.rs --- rust-plist-0.4.2/tests/tests.rs 2018-10-08 15:54:03.000000000 +0000 +++ rust-plist-0.5.4/tests/tests.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -extern crate plist; - -#[cfg(feature = "serde")] -extern crate serde; -#[cfg(feature = "serde")] -#[macro_use] -extern crate serde_derive; - -#[cfg(feature = "serde")] -mod serde_tests; diff -Nru rust-plist-0.4.2/.travis.yml rust-plist-0.5.4/.travis.yml --- rust-plist-0.4.2/.travis.yml 2019-02-10 00:17:06.000000000 +0000 +++ rust-plist-0.5.4/.travis.yml 2020-01-21 20:38:04.000000000 +0000 @@ -8,7 +8,19 @@ - nightly - beta - stable - - 1.26.0 + - 1.32.0 +matrix: + include: + - os: linux + rust: stable-i686-unknown-linux-gnu + addons: + apt: + packages: + - gcc-multilib script: - - cargo test - - cargo test --no-default-features + - cargo check --no-default-features + - cargo check --no-default-features --features enable_unstable_features_that_may_break_with_minor_version_bumps + - cargo check --no-default-features --features serde + - cargo check --all-features + - cargo test --all-features + - cargo test --release --all-features