use std::borrow::Cow;
use deser_core::__format::{Float, format_finite};
use deser_core::ext::Number;
use deser_core::ser::{self, PausableSink, SerializeDriver, Written};
use deser_core::{Atom, BytesFormat, Error, ErrorKind, Event, Serialize, State};
use crate::Nesting;
use crate::encoding::encode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum ArrayFormat {
#[default]
Repeat,
Brackets,
Indices,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SerializerConfig {
arrays: ArrayFormat,
nesting: Nesting,
space_as_plus: bool,
bytes: BytesFormat,
}
impl Default for SerializerConfig {
fn default() -> SerializerConfig {
SerializerConfig::new()
}
}
impl SerializerConfig {
pub const fn new() -> SerializerConfig {
SerializerConfig {
arrays: ArrayFormat::Repeat,
nesting: Nesting::Brackets,
space_as_plus: true,
bytes: BytesFormat::BASE64,
}
}
pub const fn arrays(mut self, format: ArrayFormat) -> SerializerConfig {
self.arrays = format;
self
}
pub const fn nesting(mut self, nesting: Nesting) -> SerializerConfig {
self.nesting = nesting;
self
}
pub const fn space_as_plus(mut self, yes: bool) -> SerializerConfig {
self.space_as_plus = yes;
self
}
pub const fn bytes(mut self, format: BytesFormat) -> SerializerConfig {
self.bytes = format;
self
}
pub fn to_string(&self, value: &dyn Serialize) -> Result<String, Error> {
self.to_string_with(value, |_| {})
}
pub fn to_string_with<F>(&self, value: &dyn Serialize, setup: F) -> Result<String, Error>
where
F: FnOnce(&mut SerializeDriver<'_>),
{
let mut driver = SerializeDriver::new(value);
setup(&mut driver);
let mut out = String::new();
self.serialize_driver(&mut driver, &mut out)?;
Ok(out)
}
pub(crate) fn value_writer(&self, out: String) -> Writer {
Writer {
config: self.clone(),
separate: false,
out,
key: String::new(),
stack: Vec::new(),
limit: usize::MAX,
}
}
pub(crate) fn serialize_part(
&self,
value: &mut Option<Box<Writer>>,
driver: &mut SerializeDriver<'_>,
out: &mut String,
separate: &mut bool,
limit: usize,
) -> Result<bool, Error> {
if value.is_none() && limit == usize::MAX {
return self.serialize_whole(driver, out, separate).map(|()| true);
}
let len = out.len();
let mut writer = value.take().unwrap_or_else(|| {
let mut writer = self.value_writer(String::new());
writer.separate = *separate;
Box::new(writer)
});
let adopt = out.is_empty();
if adopt {
writer.out = std::mem::take(out);
}
writer.limit = limit;
let rv = driver.drive_until(&mut *writer);
let output = std::mem::take(&mut writer.out);
let done = match rv {
Ok(done) => done,
Err(err) => {
if adopt {
*out = output;
}
out.truncate(len);
return Err(err);
}
};
if adopt {
*out = output;
} else {
out.push_str(&output);
}
if done {
*separate = writer.separate;
} else {
*value = Some(writer);
}
Ok(done)
}
fn serialize_whole(
&self,
driver: &mut SerializeDriver<'_>,
out: &mut String,
separate: &mut bool,
) -> Result<(), Error> {
let len = out.len();
let mut writer = self.value_writer(std::mem::take(out));
writer.separate = *separate;
let rv = driver.drive(|event, state| writer.event(event, state));
*out = writer.out;
if let Err(err) = rv {
out.truncate(len);
return Err(err);
}
*separate = writer.separate;
Ok(())
}
pub(crate) fn serialize_driver(
&self,
driver: &mut SerializeDriver<'_>,
out: &mut String,
) -> Result<(), Error> {
self.serialize_whole(driver, out, &mut false)
}
}
pub struct Serializer {
config: SerializerConfig,
out: String,
separate: bool,
value: Option<Box<Writer>>,
in_progress: bool,
}
impl Default for Serializer {
fn default() -> Serializer {
Serializer::new()
}
}
impl Clone for Serializer {
fn clone(&self) -> Serializer {
Serializer {
config: self.config.clone(),
out: self.out.clone(),
separate: self.separate,
value: None,
in_progress: self.in_progress,
}
}
}
impl std::fmt::Debug for Serializer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Serializer")
.field("config", &self.config)
.field("output", &self.out)
.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: config.clone(),
out: String::new(),
separate: false,
value: None,
in_progress: false,
}
}
pub fn config(&self) -> &SerializerConfig {
&self.config
}
pub fn serialize(&mut self, value: &dyn Serialize) -> Result<(), Error> {
ser::Serializer::serialize(self, value)
}
pub fn serialize_with<F>(&mut self, value: &dyn Serialize, setup: F) -> Result<(), Error>
where
F: FnOnce(&mut SerializeDriver<'_>),
{
ser::Serializer::serialize_with(self, value, setup)
}
pub fn as_str(&self) -> &str {
&self.out
}
pub fn finish(self) -> String {
self.out
}
}
impl ser::Serializer for Serializer {
fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
if self.in_progress {
return Err(Error::in_progress());
}
ser::StreamSerializer::drive_partial(self, driver, usize::MAX).map(|_| ())
}
}
impl ser::StreamSerializer for Serializer {
fn output(&self) -> &[u8] {
self.out.as_bytes()
}
fn clear_output(&mut self) {
self.out.clear();
}
fn supports_partial(&self) -> bool {
true
}
fn drive_partial(
&mut self,
driver: &mut SerializeDriver<'_>,
limit: usize,
) -> Result<Written, Error> {
if self.value.is_none() && self.in_progress {
return Err(Error::in_progress());
}
if !self.config.serialize_part(
&mut self.value,
driver,
&mut self.out,
&mut self.separate,
limit,
)? {
self.in_progress = true;
return Ok(Written::Partial);
}
self.in_progress = false;
Ok(Written::Done)
}
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))
}
pub fn to_writer<W: std::io::Write>(
&self,
writer: W,
value: &dyn Serialize,
) -> Result<(), Error> {
self.writer(writer).write(value)
}
}
#[cfg(feature = "io")]
pub fn to_writer<W: std::io::Write>(writer: W, value: &dyn Serialize) -> Result<(), Error> {
SerializerConfig::new().to_writer(writer, value)
}
pub fn to_string(value: &dyn Serialize) -> Result<String, Error> {
SerializerConfig::new().to_string(value)
}
enum Frame {
Map { prefix: usize },
Seq { prefix: usize, index: usize },
Pairs,
Pair(u8),
}
pub(crate) struct Writer {
config: SerializerConfig,
separate: bool,
out: String,
key: String,
stack: Vec<Frame>,
limit: usize,
}
impl PausableSink for Writer {
fn event(
&mut self,
event: Event<'_>,
_value: &dyn Serialize,
state: &mut State,
) -> Result<(), Error> {
Writer::event(self, event, state)
}
fn pause(&mut self) -> bool {
self.out.len() >= self.limit
}
}
impl Writer {
fn event(&mut self, event: Event, state: &State) -> Result<(), Error> {
match (self.stack.last_mut(), event) {
(None, Event::MapStart(_)) => self.stack.push(Frame::Map { prefix: 0 }),
(None, Event::SeqStart(_)) => self.stack.push(Frame::Pairs),
(None, Event::Atom(Atom::Null)) => {}
(None, _) => {
return Err(Error::new(
ErrorKind::UnsupportedType,
"query strings hold maps or sequences of key-value pairs",
));
}
(Some(Frame::Map { .. }), Event::MapEnd) => {
self.stack.pop();
}
(Some(&mut Frame::Map { prefix }), event) => {
if state.is_map_key() {
let key = match event {
Event::Atom(ref atom) => self.key_text(atom)?,
_ => return Err(unsupported_key()),
};
self.key.truncate(prefix);
if prefix > 0 {
self.push_nested(&key);
} else {
self.key.push_str(&key);
}
} else {
self.value(event, false)?;
}
}
(Some(Frame::Seq { .. }), Event::SeqEnd) => {
self.stack.pop();
}
(
Some(&mut Frame::Seq {
prefix,
ref mut index,
}),
event,
) => {
let element = *index;
*index += 1;
self.key.truncate(prefix);
match self.config.arrays {
ArrayFormat::Repeat => {}
ArrayFormat::Brackets => self.key.push_str("[]"),
ArrayFormat::Indices => self.push_nested(&element.to_string()),
}
if self.config.arrays != ArrayFormat::Indices
&& matches!(event, Event::MapStart(_) | Event::SeqStart(_))
{
return Err(Error::new(
ErrorKind::UnsupportedType,
"sequences of maps or sequences require ArrayFormat::Indices",
));
}
self.value(event, true)?;
}
(Some(Frame::Pairs), Event::SeqStart(_)) => self.stack.push(Frame::Pair(0)),
(Some(Frame::Pairs), Event::SeqEnd) => {
self.stack.pop();
}
(Some(Frame::Pair(state @ 0)), Event::Atom(ref atom)) => {
*state = 1;
let key = self.key_text(atom)?;
self.key.clear();
self.key.push_str(&key);
}
(Some(Frame::Pair(state @ 1)), event) => {
*state = 2;
self.value(event, false)?;
}
(Some(Frame::Pair(2)), Event::SeqEnd) => {
self.stack.pop();
}
(Some(Frame::Pairs | Frame::Pair(_)), _) => {
return Err(Error::new(
ErrorKind::UnsupportedType,
"sequences at the top level must hold key-value pairs",
));
}
}
Ok(())
}
fn value(&mut self, event: Event, in_seq: bool) -> Result<(), Error> {
match event {
Event::Atom(atom) => {
let value = match self.value_text(&atom)? {
Some(value) => value,
None if in_seq => Cow::Borrowed(""),
None => return Ok(()),
};
if self.separate {
self.out.push('&');
}
self.separate = true;
encode(
self.key.as_bytes(),
self.config.space_as_plus,
&mut self.out,
);
self.out.push('=');
encode(value.as_bytes(), self.config.space_as_plus, &mut self.out);
}
Event::MapStart(_) => {
if self.config.nesting == Nesting::Flat {
return Err(Error::new(
ErrorKind::UnsupportedType,
"nested maps are not supported with Nesting::Flat",
));
}
self.stack.push(Frame::Map {
prefix: self.key.len(),
});
}
Event::SeqStart(_) => self.stack.push(Frame::Seq {
prefix: self.key.len(),
index: 0,
}),
Event::MapEnd | Event::SeqEnd => unreachable!("ends are handled by the frames"),
}
Ok(())
}
fn push_nested(&mut self, key: &str) {
match self.config.nesting {
Nesting::Dots => {
self.key.push('.');
self.key.push_str(key);
}
Nesting::Brackets | Nesting::Flat => {
self.key.push('[');
self.key.push_str(key);
self.key.push(']');
}
}
}
fn key_text<'a>(&self, atom: &'a Atom<'_>) -> Result<Cow<'a, str>, Error> {
match atom {
Atom::Null | Atom::Bytes(_) => Err(unsupported_key()),
atom => self.value_text(atom)?.ok_or_else(unsupported_key),
}
}
fn value_text<'a>(&self, atom: &'a Atom<'_>) -> Result<Option<Cow<'a, str>>, Error> {
Ok(Some(match *atom {
Atom::Null => return Ok(None),
Atom::Implicit(ref value) => {
return Ok(self
.value_text(&value.value().to_atom())?
.map(|text| Cow::Owned(text.into_owned())));
}
Atom::Bool(value) => Cow::Borrowed(if value { "true" } else { "false" }),
Atom::Str(ref value) | Atom::Lexical(ref value) => Cow::Borrowed(&**value),
Atom::Char(value) => Cow::Owned(value.to_string()),
Atom::U64(value) => Cow::Owned(value.to_string()),
Atom::I64(value) => Cow::Owned(value.to_string()),
Atom::F32(value) => Cow::Owned(float_text(value)),
Atom::F64(value) => Cow::Owned(float_text(value)),
Atom::Bytes(ref bytes) => {
let format = bytes.fallback.copied().unwrap_or(self.config.bytes);
Cow::Owned(
format
.encode(bytes)
.or_else(|| BytesFormat::BASE64.encode(bytes))
.unwrap_or_default(),
)
}
Atom::Ext(ref ext) => {
if let Some(number) = ext.downcast_value_ref::<Number>() {
Cow::Owned(number.as_str().to_string())
} else if let Some(value) = ext.downcast_ref::<u128>() {
Cow::Owned(value.to_string())
} else if let Some(value) = ext.downcast_ref::<i128>() {
Cow::Owned(value.to_string())
} else {
match ext.fallback() {
Atom::Ext(_) => {
return Err(Error::new(
ErrorKind::UnsupportedType,
format!("query strings do not support {}", ext.name()),
));
}
fallback => match self.value_text(&fallback)? {
Some(text) => Cow::Owned(text.into_owned()),
None => return Ok(None),
},
}
}
}
_ => {
return Err(Error::new(
ErrorKind::UnsupportedType,
format!("query strings do not support {}", atom.name()),
));
}
}))
}
}
#[cold]
fn unsupported_key() -> Error {
Error::new(
ErrorKind::UnsupportedType,
"keys of query strings must be strings, numbers or booleans",
)
}
fn float_text<F: Float>(value: F) -> String {
if value.is_finite() {
format_finite(value)
} else {
value.to_f64().to_string()
}
}