use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use deser_core::State;
use deser_core::ext::{BigInt, Datetime, ExtValue, Number, Timestamp};
use deser_core::ser::SerializeRef;
use deser_core::ser::{self, SerializeDriver};
use deser_core::{Atom, Error, ErrorKind, Event, Serialize};
use crate::common::{timestamp_from_plist, timestamp_to_plist};
use crate::format::Format;
use crate::uid::Uid;
use crate::write_binary;
use crate::write_text::TextWriter;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SerializerConfig {
format: Format,
context: deser_core::Context,
}
impl SerializerConfig {
pub const fn new() -> SerializerConfig {
SerializerConfig {
format: Format::Xml,
context: deser_core::Context::new(),
}
}
pub const fn builder() -> SerializerConfigBuilder {
SerializerConfigBuilder::new()
}
pub const fn into_builder(self) -> SerializerConfigBuilder {
SerializerConfigBuilder { value: self }
}
pub fn set_context(&mut self, context: deser_core::Context) {
self.context = context;
}
pub fn context(&self) -> &deser_core::Context {
&self.context
}
#[inline]
fn apply_context(&self, driver: &mut SerializeDriver<'_>) {
if !self.context.is_empty() {
driver.set_default_context(self.context.clone());
}
}
pub const fn set_format(&mut self, format: Format) {
self.format = format;
}
pub fn to_vec<T: Serialize + ?Sized>(&self, value: &T) -> Result<Vec<u8>, Error> {
self.to_vec_ref(SerializeRef::new(&value))
}
pub fn to_vec_with<F, T: Serialize + ?Sized>(
&self,
value: &T,
setup: F,
) -> Result<Vec<u8>, Error>
where
F: FnOnce(&mut SerializeDriver<'_>),
{
let mut driver = SerializeDriver::new(&value);
setup(&mut driver);
self.apply_context(&mut driver);
self.serialize_driver(&mut driver)
}
fn to_vec_ref(&self, value: SerializeRef<'_>) -> Result<Vec<u8>, Error> {
let mut driver = SerializeDriver::from_ref(value);
self.apply_context(&mut driver);
self.serialize_driver(&mut driver)
}
pub fn to_string<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, Error> {
if !self.format.is_text() {
return Err(Error::new(
ErrorKind::UnsupportedType,
"binary property lists cannot be written to strings",
));
}
let bytes = self.to_vec(value)?;
Ok(String::from_utf8(bytes).unwrap())
}
pub(crate) fn serialize_driver(
&self,
driver: &mut SerializeDriver<'_>,
) -> Result<Vec<u8>, Error> {
if self.format.is_text() {
let mut writer = TextWriter::new(self.format, String::with_capacity(256));
driver.drive_sink(&mut writer)?;
writer.finish()?;
return Ok(writer.out.into_bytes());
}
let mut builder = Builder::default();
driver.drive_sink(&mut builder)?;
let tree = builder.finish()?;
Ok(write_binary::write(&tree))
}
pub(crate) fn is_text(&self) -> bool {
self.format.is_text()
}
pub(crate) fn serialize_part(
&self,
value: &mut Option<Box<TextWriter>>,
driver: &mut SerializeDriver<'_>,
out: &mut Vec<u8>,
limit: usize,
) -> Result<bool, Error> {
let len = out.len();
let adopt = out.is_empty();
let buffer = match adopt {
true => String::new(),
false => String::with_capacity(256),
};
let mut local;
let writer: &mut TextWriter = match value {
Some(writer) => {
writer.out = buffer;
writer
}
None if limit == usize::MAX => {
local = TextWriter::new(self.format, buffer);
&mut local
}
None => value.insert(Box::new(TextWriter::new(self.format, buffer))),
};
let rv = if limit == usize::MAX {
driver.drive_sink(writer).map(|()| true)
} else {
writer.limit = limit;
driver.drive_until(writer)
};
let rv = rv.and_then(|done| {
if done {
writer.finish()?;
}
Ok(done)
});
let output = core::mem::take(&mut writer.out).into_bytes();
match rv {
Ok(done) => {
if adopt {
*out = output;
} else {
out.extend_from_slice(&output);
}
if done {
*value = None;
}
Ok(done)
}
Err(err) => {
*value = None;
out.truncate(len);
Err(err)
}
}
}
}
#[derive(Debug, Clone)]
#[must_use]
pub struct SerializerConfigBuilder {
value: SerializerConfig,
}
impl SerializerConfigBuilder {
pub const fn new() -> SerializerConfigBuilder {
SerializerConfigBuilder {
value: SerializerConfig::new(),
}
}
pub const fn format(mut self, format: Format) -> SerializerConfigBuilder {
self.value.set_format(format);
self
}
pub fn context(mut self, context: deser_core::Context) -> SerializerConfigBuilder {
self.value.set_context(context);
self
}
pub const fn build(self) -> SerializerConfig {
let value = unsafe { core::ptr::read(&self.value) };
core::mem::forget(self);
value
}
}
impl Default for SerializerConfigBuilder {
fn default() -> SerializerConfigBuilder {
SerializerConfigBuilder::new()
}
}
#[derive(Default)]
pub struct Serializer {
config: SerializerConfig,
out: Vec<u8>,
written: bool,
value: Option<Box<TextWriter>>,
in_progress: bool,
}
impl Clone for Serializer {
fn clone(&self) -> Serializer {
Serializer {
config: self.config.clone(),
out: self.out.clone(),
written: self.written,
value: None,
in_progress: self.in_progress,
}
}
}
impl core::fmt::Debug for Serializer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Serializer")
.field("config", &self.config)
.field("output", &self.out)
.field("written", &self.written)
.field("in_progress", &self.in_progress)
.finish()
}
}
impl Serializer {
pub fn new() -> Serializer {
Serializer::with_config(SerializerConfig::new())
}
pub fn with_config(config: SerializerConfig) -> Serializer {
Serializer {
config,
out: Vec::new(),
written: false,
value: None,
in_progress: false,
}
}
pub fn config(&self) -> &SerializerConfig {
&self.config
}
pub fn written(&self) -> bool {
self.written
}
pub fn serialize<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
ser::Serializer::serialize(self, value)
}
pub fn serialize_with<F, T: Serialize + ?Sized>(
&mut self,
value: &T,
setup: F,
) -> Result<(), Error>
where
F: FnOnce(&mut SerializeDriver<'_>),
{
ser::Serializer::serialize_with(self, value, setup)
}
pub fn output(&self) -> &[u8] {
&self.out
}
pub fn finish(self) -> Vec<u8> {
self.out
}
fn check_single(&self) -> Result<(), Error> {
if self.in_progress {
return Err(Error::in_progress());
}
if self.written {
return Err(Error::new(
ErrorKind::InvalidState,
"a property list holds a single value",
));
}
Ok(())
}
}
impl ser::Serializer for Serializer {
fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
if !self.config.context.is_empty() {
driver.set_default_context(self.config.context.clone());
}
self.check_single()?;
if self.config.is_text() {
self.config
.serialize_part(&mut self.value, driver, &mut self.out, usize::MAX)?;
} else {
let bytes = self.config.serialize_driver(driver)?;
self.out.extend_from_slice(&bytes);
}
self.written = true;
Ok(())
}
}
impl ser::StreamSerializer for Serializer {
fn output(&self) -> &[u8] {
&self.out
}
fn clear_output(&mut self) {
self.out.clear();
}
fn supports_partial(&self) -> bool {
self.config.is_text()
}
fn drive_partial(
&mut self,
driver: &mut SerializeDriver<'_>,
limit: usize,
) -> Result<bool, Error> {
if !self.config.context.is_empty() {
driver.set_default_context(self.config.context.clone());
}
if self.value.is_none() {
if limit == usize::MAX || !self.config.is_text() {
ser::Serializer::drive(self, driver)?;
return Ok(true);
}
self.check_single()?;
}
if !self
.config
.serialize_part(&mut self.value, driver, &mut self.out, limit)?
{
self.in_progress = true;
return Ok(false);
}
self.in_progress = false;
self.written = true;
Ok(true)
}
fn in_progress(&self) -> bool {
self.in_progress
}
}
#[cfg(feature = "io")]
impl SerializerConfig {
pub fn writer<W: std::io::Write>(&self, writer: W) -> deser_core::io::Writer<W, Serializer> {
deser_core::io::Writer::new(writer, Serializer::with_config(self.clone()))
}
pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
&self,
writer: W,
value: &T,
) -> Result<(), Error> {
self.writer(writer).write(value)
}
}
#[cfg(feature = "io")]
pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
writer: W,
value: &T,
) -> Result<(), Error> {
SerializerConfig::new().to_writer(writer, value)
}
pub fn to_vec<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, Error> {
SerializerConfig::new().to_vec(value)
}
pub fn to_string<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
SerializerConfig::new().to_string(value)
}
pub(crate) enum Node {
Bool(bool),
Int(i128),
Real(f64),
Real32(f32),
Str(String),
Data(Vec<u8>),
Date(Timestamp),
Uid(u64),
Array(Vec<usize>),
Dict(Vec<(String, usize)>),
}
pub(crate) struct Tree {
pub(crate) nodes: Vec<Node>,
}
enum Open {
Array(usize, Vec<usize>),
Dict(usize, Vec<(String, usize)>, Option<String>),
}
#[derive(Default)]
struct Builder {
nodes: Vec<Node>,
stack: Vec<Open>,
}
impl ser::EventSink for Builder {
fn event(
&mut self,
event: Event,
_value: SerializeRef<'_>,
_state: &mut State,
) -> Result<(), Error> {
Builder::event(self, event)
}
}
impl Builder {
fn finish(self) -> Result<Tree, Error> {
if self.nodes.is_empty() || !self.stack.is_empty() {
return Err(Error::new(ErrorKind::InvalidState, "incomplete value"));
}
Ok(Tree { nodes: self.nodes })
}
fn event(&mut self, event: Event) -> Result<(), Error> {
let Some(open) = self.stack.last_mut() else {
if !self.nodes.is_empty() {
return Err(Error::new(ErrorKind::InvalidState, "unexpected event"));
}
return match self.value(event)? {
Some(_) => Ok(()),
None => Err(Error::new(
ErrorKind::UnsupportedType,
"property lists cannot hold null values",
)),
};
};
match open {
Open::Dict(_, _, key @ None) => match event {
Event::Atom(atom) => {
*key = Some(key_to_string(atom)?);
Ok(())
}
Event::MapEnd => self.close(),
_ => Err(unsupported_key()),
},
Open::Dict(_, _, key @ Some(_)) => {
let key = key.take().unwrap();
if let Some(value) = self.value(event)? {
self.add_entry(key, value);
}
Ok(())
}
Open::Array(..) => {
if event == Event::SeqEnd {
return self.close();
}
match self.value(event)? {
Some(value) => {
self.add_item(value);
Ok(())
}
None => Err(Error::new(
ErrorKind::UnsupportedType,
"property lists cannot hold null values in arrays",
)),
}
}
}
}
fn parent(&mut self, value: usize) -> &mut Open {
let len = self.stack.len();
let opened = match self.stack[len - 1] {
Open::Array(id, _) | Open::Dict(id, _, _) => id == value,
};
&mut self.stack[if opened { len - 2 } else { len - 1 }]
}
fn add_entry(&mut self, key: String, value: usize) {
match self.parent(value) {
Open::Dict(_, entries, _) => entries.push((key, value)),
Open::Array(..) => unreachable!(),
}
}
fn add_item(&mut self, value: usize) {
match self.parent(value) {
Open::Array(_, items) => items.push(value),
Open::Dict(..) => unreachable!(),
}
}
fn close(&mut self) -> Result<(), Error> {
match self.stack.pop() {
Some(Open::Array(id, items)) => self.nodes[id] = Node::Array(items),
Some(Open::Dict(id, entries, None)) => self.nodes[id] = Node::Dict(entries),
Some(Open::Dict(_, _, Some(_))) => {
return Err(Error::new(ErrorKind::InvalidState, "map without value"));
}
None => return Err(Error::new(ErrorKind::InvalidState, "unexpected end")),
}
Ok(())
}
fn value(&mut self, event: Event) -> Result<Option<usize>, Error> {
let id = self.nodes.len();
let node = match event {
Event::Atom(atom) => match convert_atom(atom)? {
Some(Node::Date(value))
if timestamp_from_plist(timestamp_to_plist(&value)).is_none() =>
{
return Err(Error::new(
ErrorKind::OutOfRange,
"date out of range for binary property lists",
));
}
Some(node) => node,
None => return Ok(None),
},
Event::MapStart(_) => {
self.stack.push(Open::Dict(id, Vec::new(), None));
Node::Dict(Vec::new())
}
Event::SeqStart(_) => {
self.stack.push(Open::Array(id, Vec::new()));
Node::Array(Vec::new())
}
Event::MapEnd | Event::SeqEnd => {
return Err(Error::new(ErrorKind::InvalidState, "unexpected end event"));
}
};
self.nodes.push(node);
Ok(Some(id))
}
}
pub(crate) fn convert_atom(atom: Atom) -> Result<Option<Node>, Error> {
Ok(Some(match atom {
Atom::Null => return Ok(None),
Atom::Bool(value) => Node::Bool(value),
Atom::Str(value) | Atom::Lexical(value) => Node::Str(value.into_owned()),
Atom::Char(value) => Node::Str(value.to_string()),
Atom::U64(value) => Node::Int(value.into()),
Atom::I64(value) => Node::Int(value.into()),
Atom::F64(value) => Node::Real(value),
Atom::F32(value) => Node::Real32(value),
Atom::Bytes(value) => Node::Data(value.into_owned()),
Atom::Ext(ref ext) => return convert_ext(ext),
Atom::Implicit(value) => return convert_atom(value.value().to_atom()),
_ => return Err(Error::new(ErrorKind::UnsupportedType, "unknown atom")),
}))
}
#[cold]
fn convert_ext(ext: &ExtValue) -> Result<Option<Node>, Error> {
if let Some(value) = ext.downcast_ref::<Uid>() {
return Ok(Some(Node::Uid(value.get())));
}
if let Some(&value) = ext.downcast_ref::<Timestamp>() {
return Ok(Some(Node::Date(value)));
}
if let Some(&value) = ext.downcast_ref::<Datetime>()
&& value.offset.is_some()
&& let Ok(value) = Timestamp::try_from(value)
{
return Ok(Some(Node::Date(value)));
}
let out_of_range = || Error::new(ErrorKind::OutOfRange, "integer out of range for plist");
if let Some(&value) = ext.downcast_ref::<u128>() {
return Ok(Some(Node::Int(
i128::try_from(value).map_err(|_| out_of_range())?,
)));
}
if let Some(&value) = ext.downcast_ref::<i128>() {
return Ok(Some(Node::Int(value)));
}
if let Some(value) = ext.downcast_ref::<BigInt>().and_then(|x| x.to_i128()) {
return Ok(Some(Node::Int(value)));
}
if let Some(value) = ext.downcast_value_ref::<Number>()
&& let Ok(value) = value.as_str().parse::<i128>()
{
return Ok(Some(Node::Int(value)));
}
match ext.fallback() {
Atom::Ext(_) => Err(Error::new(
ErrorKind::UnsupportedType,
format!("property lists do not support {}", ext.name()),
)),
fallback => convert_atom(fallback),
}
}
pub(crate) fn key_to_string(atom: Atom) -> Result<String, Error> {
Ok(match atom {
Atom::Implicit(value) => return key_to_string(value.value().to_atom()),
Atom::Str(value) | Atom::Lexical(value) => value.into_owned(),
Atom::Char(value) => value.to_string(),
Atom::U64(value) => value.to_string(),
Atom::I64(value) => value.to_string(),
Atom::Bool(value) => value.to_string(),
Atom::Ext(ref ext) => {
if let Some(value) = ext.downcast_ref::<u128>() {
value.to_string()
} else if let Some(value) = ext.downcast_ref::<i128>() {
value.to_string()
} else {
match ext.fallback() {
Atom::Ext(_) => return Err(unsupported_key()),
fallback => return key_to_string(fallback),
}
}
}
_ => return Err(unsupported_key()),
})
}
#[cold]
pub(crate) fn unsupported_key() -> Error {
Error::new(
ErrorKind::UnsupportedType,
"dictionary keys of property lists must be strings",
)
}