use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use deser_core::de::{Deserialize, OwnedSink, Sink, SinkHandle, default_atom};
use deser_core::ser::SerializeRef;
use deser_core::ser::{
Boxed, Describe, Emit, SerializeHandle, StructEmitter, Variant, VariantKind, VariantRepr,
};
use deser_core::{Atom, Error, ErrorKind, Serialize, State, Text};
use crate::Names;
pub struct Mixed<T, W = KeepWhitespace>(pub Vec<T>, PhantomData<fn() -> W>);
pub trait Whitespace: sealed::Sealed + 'static {
#[doc(hidden)]
const KEEP: bool;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeepWhitespace;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SkipWhitespace;
impl Whitespace for KeepWhitespace {
const KEEP: bool = true;
}
impl Whitespace for SkipWhitespace {
const KEEP: bool = false;
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::KeepWhitespace {}
impl Sealed for super::SkipWhitespace {}
}
impl<T, W> Mixed<T, W> {
pub const fn new() -> Mixed<T, W> {
Mixed(Vec::new(), PhantomData)
}
pub fn into_inner(self) -> Vec<T> {
self.0
}
}
impl<T: fmt::Debug, W> fmt::Debug for Mixed<T, W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Mixed").field(&self.0).finish()
}
}
impl<T: Clone, W> Clone for Mixed<T, W> {
fn clone(&self) -> Mixed<T, W> {
Mixed(self.0.clone(), PhantomData)
}
}
impl<T: PartialEq, W> PartialEq for Mixed<T, W> {
fn eq(&self, other: &Mixed<T, W>) -> bool {
self.0 == other.0
}
}
impl<T: Eq, W> Eq for Mixed<T, W> {}
impl<T: PartialOrd, W> PartialOrd for Mixed<T, W> {
fn partial_cmp(&self, other: &Mixed<T, W>) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T: Ord, W> Ord for Mixed<T, W> {
fn cmp(&self, other: &Mixed<T, W>) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<T: Hash, W> Hash for Mixed<T, W> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl<T, W> Default for Mixed<T, W> {
fn default() -> Mixed<T, W> {
Mixed::new()
}
}
impl<T, W> Deref for Mixed<T, W> {
type Target = Vec<T>;
fn deref(&self) -> &Vec<T> {
&self.0
}
}
impl<T, W> DerefMut for Mixed<T, W> {
fn deref_mut(&mut self) -> &mut Vec<T> {
&mut self.0
}
}
impl<T, W> From<Vec<T>> for Mixed<T, W> {
fn from(values: Vec<T>) -> Mixed<T, W> {
Mixed(values, PhantomData)
}
}
impl<T, W> FromIterator<T> for Mixed<T, W> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Mixed<T, W> {
Mixed(iter.into_iter().collect(), PhantomData)
}
}
impl<T, W> IntoIterator for Mixed<T, W> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a, T, W> IntoIterator for &'a Mixed<T, W> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[derive(Debug, Default)]
pub(crate) struct WhitespaceDepths(pub(crate) Vec<usize>);
impl WhitespaceDepths {
pub(crate) fn applies(state: &State) -> bool {
state
.get::<WhitespaceDepths>()
.and_then(|keep| keep.0.last())
.is_some_and(|&depth| depth == state.depth())
}
pub(crate) fn prune(state: &mut State) {
let depth = state.depth();
if state.get::<WhitespaceDepths>().is_some() {
state
.get_mut::<WhitespaceDepths>()
.0
.retain(|&keep| keep <= depth);
}
}
}
impl<'de, T: Deserialize<'de>, W: Whitespace> Deserialize<'de> for Mixed<T, W> {
fn deserialize_into<'out>(
out: &'out mut Option<Self>,
state: &mut State,
) -> SinkHandle<'out, 'de> {
SinkHandle::arena(
MixedSink {
out,
values: Vec::new(),
key: None,
pending: None,
depth: None,
},
state,
)
}
fn expecting() -> Cow<'static, str> {
Cow::Borrowed("mixed content")
}
fn initial_value() -> Option<Self> {
Some(Mixed::new())
}
}
struct MixedSink<'a, 'de, T, W> {
out: &'a mut Option<Mixed<T, W>>,
values: Vec<T>,
key: Option<String>,
pending: Option<OwnedSink<'de, T>>,
depth: Option<usize>,
}
impl<'de, T: Deserialize<'de>, W: Whitespace> MixedSink<'_, 'de, T, W> {
fn keep_whitespace(&mut self, depth: usize, state: &mut State) {
if W::KEEP && self.depth.is_none() {
self.depth = Some(depth);
state.get_mut::<WhitespaceDepths>().0.push(depth);
}
}
fn is_content(text: &str) -> bool {
if W::KEEP {
!text.is_empty()
} else {
!text.trim().is_empty()
}
}
fn begin(&mut self, key: &str, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
self.end(state)?;
let pending = self.pending.insert(OwnedSink::deserialize(state));
let sink = pending.get_mut();
sink.map(state)?;
let mut key_sink = sink.next_key(state)?;
key_sink.atom(Atom::Lexical(Text::borrowed(key)), state)?;
key_sink.finish(state)?;
drop(key_sink);
sink.next_value(state)
}
fn end(&mut self, state: &mut State) -> Result<(), Error> {
if let Some(mut pending) = self.pending.take() {
pending.get_mut().finish(state)?;
self.values.extend(pending.take());
}
Ok(())
}
}
impl<'de, T: Deserialize<'de>, W: Whitespace> Sink<'de> for MixedSink<'_, 'de, T, W> {
fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
match atom {
Atom::Null => Ok(()),
Atom::Str(ref text) | Atom::Lexical(ref text) if !Self::is_content(text) => Ok(()),
Atom::Str(_) | Atom::Lexical(_) => {
let mut sink = self.begin(text_key(state), state)?;
sink.atom(atom, state)?;
sink.finish(state)
}
atom => default_atom(self, atom, state),
}
}
fn borrowed_atom(&mut self, atom: Atom<'de>, state: &mut State) -> Result<(), Error> {
match atom {
Atom::Str(ref text) | Atom::Lexical(ref text) if Self::is_content(text) => {
let mut sink = self.begin(text_key(state), state)?;
sink.borrowed_atom(atom, state)?;
sink.finish(state)
}
atom => self.atom(atom, state),
}
}
fn map(&mut self, state: &mut State) -> Result<(), Error> {
self.keep_whitespace(state.depth() + 1, state);
Ok(())
}
fn next_key(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
self.end(state)?;
Ok(String::deserialize_into(&mut self.key, state))
}
fn next_value(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
let key = self.key.take().unwrap_or_default();
Ok(self
.value_for_key(&key, state)?
.unwrap_or_else(SinkHandle::null))
}
fn value_for_key(
&mut self,
key: &str,
state: &mut State,
) -> Result<Option<SinkHandle<'_, 'de>>, Error> {
let prefix = names(state).attribute_prefix;
if !prefix.is_empty() && key.starts_with(prefix) {
return Ok(None);
}
self.keep_whitespace(state.depth(), state);
self.begin(key, state).map(Some)
}
fn finish(&mut self, state: &mut State) -> Result<(), Error> {
self.end(state)?;
if let Some(depth) = self.depth.take() {
let keep = &mut state.get_mut::<WhitespaceDepths>().0;
if keep.last() == Some(&depth) {
keep.pop();
}
}
*self.out = Some(Mixed(std::mem::take(&mut self.values), PhantomData));
Ok(())
}
fn recover(&mut self, err: Error, state: &mut State) -> Result<(), Error> {
self.key = None;
match self.pending {
Some(ref mut pending) => pending.get_mut().recover(err, state),
None => Err(err),
}
}
fn expecting(&self) -> Cow<'_, str> {
Cow::Borrowed("mixed content")
}
}
fn names(state: &State) -> &Names {
const DEFAULT: Names = Names::new();
state.get::<Names>().unwrap_or(&DEFAULT)
}
fn text_key(state: &State) -> &'static str {
names(state).text_key
}
#[derive(Debug, Default, Clone)]
pub(crate) struct KeepsWhitespace(pub(crate) bool);
impl<T: Serialize, W: Whitespace> Serialize for Mixed<T, W> {
fn serialize<'a>(value: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
if W::KEEP {
state.event_mut::<KeepsWhitespace>().0 = true;
}
Ok(Emit::structure(
MixedEmitter {
values: value.0.iter(),
current: None,
},
state,
))
}
}
enum Entries<'a> {
Struct(Boxed<dyn StructEmitter + 'a>),
Unit(Option<Cow<'a, str>>),
}
struct MixedEmitter<'a, T> {
values: std::slice::Iter<'a, T>,
current: Option<(&'a T, Entries<'a>)>,
}
impl<'a, T: Serialize> StructEmitter for MixedEmitter<'a, T> {
fn next(
&mut self,
state: &mut State,
) -> Result<Option<(Cow<'_, str>, SerializeHandle<'_>)>, Error> {
loop {
if let Some((_, ref mut entries)) = self.current {
let entry = match entries {
Entries::Struct(emitter) => emitter.next(state)?,
Entries::Unit(name) => name.take().map(|name| (name, SerializeHandle::to(&""))),
};
let entry = unsafe {
std::mem::transmute::<
Option<(Cow<'_, str>, SerializeHandle<'_>)>,
Option<(Cow<'a, str>, SerializeHandle<'a>)>,
>(entry)
};
if let Some(entry) = entry {
return Ok(Some(entry));
}
let (value, entries) = self.current.take().unwrap();
drop(entries);
T::finish(value, state)?;
}
let Some(value) = self.values.next() else {
return Ok(None);
};
let entries = match T::serialize(value, state)? {
Emit::Struct(emitter) => Entries::Struct(emitter),
Emit::Atom(Atom::Str(name) | Atom::Lexical(name))
if is_unit_variant(SerializeRef::new(value)) =>
{
Entries::Unit(Some(name.into_cow()))
}
Emit::Atom(Atom::Null) => Entries::Unit(None),
_ => {
return Err(Error::new(
ErrorKind::UnsupportedType,
"the values of mixed content must be structs or externally tagged enums",
));
}
};
self.current = Some((value, entries));
}
}
}
fn is_unit_variant(value: SerializeRef<'_>) -> bool {
struct Check(bool);
impl Describe for Check {
fn variant(&mut self, variant: &Variant<'_>) {
self.0 = variant.kind == VariantKind::Unit && variant.repr == VariantRepr::External;
}
}
let mut check = Check(false);
value.describe(&mut check);
check.0
}