Skip to main content

deser_plist/
lib.rs

1//! Parse and serialize [property lists](https://en.wikipedia.org/wiki/Property_list)
2//! compatible with deser.
3//!
4//! Property lists are the configuration and serialization format of
5//! Apple's platforms (`Info.plist`, preferences, Xcode projects, keyed
6//! archives, ...).  They come in three formats which are all supported:
7//! XML, binary and the older OpenStep (ASCII) format.  Deserialization
8//! detects the format, the serializer writes the format of its
9//! [`SerializerConfig`] (XML by default).
10//!
11//! ```rust
12//! use deser::{Deserialize, Serialize};
13//! use deser_plist::{Format, SerializerConfig};
14//!
15//! #[derive(Debug, PartialEq, Serialize, Deserialize)]
16//! #[deser(rename_all = "PascalCase")]
17//! struct Info {
18//!     bundle_name: String,
19//!     bundle_version: u32,
20//! }
21//!
22//! let info = Info { bundle_name: "Demo".into(), bundle_version: 42 };
23//!
24//! let xml = deser_plist::to_string(&info).unwrap();
25//! assert!(xml.contains("<key>BundleName</key>\n\t<string>Demo</string>"));
26//! assert_eq!(deser_plist::from_slice::<Info>(xml.as_bytes()).unwrap(), info);
27//!
28//! let binary = SerializerConfig::builder().format(Format::Binary).build().to_vec(&info).unwrap();
29//! assert_eq!(deser_plist::from_slice::<Info>(&binary).unwrap(), info);
30//! ```
31//!
32//! # Data Model
33//!
34//! Property lists map onto the deser data model as follows:
35//!
36//! | Property list               | deser                                    |
37//! |-----------------------------|------------------------------------------|
38//! | dictionaries                | maps (keys are lexical atoms)            |
39//! | arrays, sets                | sequences                                |
40//! | strings                     | `Str` (OpenStep: lexical atoms)          |
41//! | integers                    | `U64`, `I64`, `i128`                     |
42//! | reals                       | `F64`                                    |
43//! | booleans                    | `Bool`                                   |
44//! | dates                       | [`Timestamp`]                            |
45//! | data                        | `Bytes`                                  |
46//! | UIDs                        | [`Uid`]                                  |
47//!
48//! Dates are passed through deser as the well-known [`Timestamp`] type,
49//! so `std::time::SystemTime` and the timestamp types of `jiff`, `chrono`
50//! and `time` work with the respective features of deser.  Binary
51//! property lists store dates as `f64` seconds, they are rounded to
52//! microseconds when read.  XML property lists store dates without
53//! fraction, it's truncated when written.
54//!
55//! UIDs (references of `NSKeyedArchiver` archives) are passed through as
56//! the [`Uid`] extension type which falls back to an integer.  Binary
57//! property lists have a type for them, the text formats write them as
58//! dictionaries with a single `CF$UID` key.  Like Core Foundation, such
59//! dictionaries are read back as UIDs from XML.
60//!
61//! Keys of dictionaries are passed on as lexical atoms, so maps with keys
62//! that are not strings (such as `BTreeMap<u32, _>`) work.  The OpenStep
63//! format only knows strings: all of its strings are lexical atoms which
64//! can be deserialized into numbers and booleans (`YES` and `NO`).  Like
65//! Core Foundation the reader also understands `.strings` files, which are
66//! a dictionary without braces.
67//!
68//! When serializing, property lists have no null value: map entries with
69//! null values (such as `None`) are skipped, null values elsewhere are an
70//! error.  Bytes are always written as data.  Offset date-times
71//! ([`Datetime`](deser_core::ext::Datetime)) are dates, other extension
72//! types (such as UUIDs and decimals) are written as their fallback,
73//! usually a string.  Keys have to be strings, numbers and booleans are
74//! converted into strings.  Integers can be of the range of `i128` in
75//! binary property lists, `i64` and `u64` in XML.  In the OpenStep format
76//! numbers and dates are written as strings and booleans as `YES` and
77//! `NO`.
78//!
79//! [`Timestamp`]: deser_core::ext::Timestamp
80//!
81//! # Features
82//!
83//! * `io` (enabled by default): reading and writing streams of the
84//!   standard library with [`from_reader`] and [`to_writer`] and the
85//!   readers and writers of [`deser::io`](deser_core::io)
86//!   ([`DeserializerConfig::reader`] and [`SerializerConfig::writer`]).
87//!   As property lists cannot be split, the whole stream is read before
88//!   it's parsed.  Requires `std`.  The stream serializer
89//!   ([`Serializer`]) and deserializer ([`StreamDeserializer`]) do not
90//!   need it.
91//! * `speedups` (enabled by default): has no effect yet, it exists so
92//!   that all formats have it.
93//! * `std` (enabled by default): uses the standard library.  Without it
94//!   this crate only needs `alloc` (see [`no_std`](https://docs.rs/deser/latest/deser/#no_std)).
95#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
96#![cfg_attr(not(any(feature = "std", test)), no_std)]
97
98extern crate alloc;
99
100mod common;
101mod de;
102mod format;
103mod read_ascii;
104mod read_binary;
105mod read_xml;
106mod ser;
107mod stream;
108mod uid;
109mod write_ascii;
110mod write_binary;
111mod write_text;
112mod write_xml;
113
114pub use self::de::{Deserializer, DeserializerConfig, DeserializerConfigBuilder, from_slice};
115pub use self::format::Format;
116#[cfg(feature = "io")]
117pub use self::ser::to_writer;
118pub use self::ser::{Serializer, SerializerConfig, SerializerConfigBuilder, to_string, to_vec};
119pub use self::stream::StreamDeserializer;
120#[cfg(feature = "io")]
121pub use self::stream::from_reader;
122pub use self::uid::Uid;
123
124// the examples of the readme are tested
125#[cfg(doctest)]
126#[doc = include_str!("../README.md")]
127struct ReadmeDoctests;