use alloc::borrow::Cow;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use crate::State;
use crate::error::{Error, ErrorKind};
use crate::event::Atom;
use crate::ser::driver::Held;
use crate::ser::{Boxed, Emit, MapEmitter, SerializeHandle, SerializeRef, StructEmitter};
use crate::unwind::pop_all;
pub(crate) struct Forwarded(Vec<Held>);
impl Drop for Forwarded {
fn drop(&mut self) {
pop_all(&mut self.0, drop);
}
}
impl Forwarded {
pub(crate) fn new() -> Forwarded {
Forwarded(Vec::new())
}
pub(crate) unsafe fn serialize<'a>(
&mut self,
value: SerializeRef<'a>,
state: &mut State,
) -> Result<Emit<'a>, Error> {
let mut emit = value.serialize(state)?;
loop {
match emit {
Emit::Forward(handle) => {
let held = unsafe { Held::new(handle) };
let value: SerializeRef<'a> = unsafe { held.get() };
self.0.push(held);
emit = value.serialize(state)?;
}
emit => return Ok(emit),
}
}
}
pub(crate) fn finish(&self, state: &mut State) -> Result<(), Error> {
for held in self.0.iter().rev() {
unsafe { held.get() }.finish(state)?;
}
Ok(())
}
}
enum Content<'a> {
Struct(Boxed<dyn StructEmitter + 'a>),
Map(Boxed<dyn MapEmitter + 'a>),
Empty,
}
pub struct FlattenedStruct<'a> {
content: Content<'a>,
forwarded: Forwarded,
done: bool,
}
impl<'a> FlattenedStruct<'a> {
pub fn new(value: SerializeRef<'a>, state: &mut State) -> Result<FlattenedStruct<'a>, Error> {
let mut forwarded = Forwarded::new();
let emit = unsafe { forwarded.serialize(value, state)? };
let content = match emit {
Emit::Struct(emitter) => Content::Struct(emitter),
Emit::Map(emitter) => Content::Map(emitter),
Emit::Atom(Atom::Null) => Content::Empty,
_ => {
return Err(Error::new(
ErrorKind::UnsupportedType,
"only structs and maps can be flattened",
));
}
};
Ok(FlattenedStruct {
content,
forwarded,
done: false,
})
}
pub(crate) fn is_null(&self) -> bool {
matches!(self.content, Content::Empty)
}
#[inline]
pub fn next(
&mut self,
state: &mut State,
) -> Result<Option<(Cow<'_, str>, SerializeHandle<'_>)>, Error> {
if self.done {
return Ok(None);
}
match self.content {
Content::Struct(ref mut emitter) => {
if let Some(item) = emitter.next(state)? {
return Ok(Some(item));
}
}
Content::Map(ref mut emitter) => {
let key = match emitter.next_key(state)? {
Some(key) => Some(map_key_string(key.get(), state)?),
None => None,
};
if let Some(key) = key {
return Ok(Some((Cow::Owned(key), emitter.next_value(state)?)));
}
}
Content::Empty => {}
}
self.done = true;
self.forwarded.finish(state)?;
Ok(None)
}
}
fn map_key_string(key: SerializeRef<'_>, state: &mut State) -> Result<String, Error> {
let rv = match key.serialize(state)? {
Emit::Atom(Atom::Str(key) | Atom::Lexical(key)) => key.into_owned(),
Emit::Atom(Atom::U64(value)) => value.to_string(),
Emit::Atom(Atom::I64(value)) => value.to_string(),
Emit::Atom(Atom::Bool(value)) => value.to_string(),
Emit::Atom(Atom::Char(value)) => value.to_string(),
_ => {
return Err(Error::new(
ErrorKind::UnsupportedType,
"the keys of flattened maps must be strings, integers, booleans or chars",
));
}
};
key.finish(state)?;
Ok(rv)
}