use std::borrow::Cow;
use std::fmt::Debug;
use std::io::Write;
use std::mem::replace;
use quick_xml::{
escape::escape,
events::{BytesEnd, BytesStart, BytesText, Event},
Writer,
};
use super::{Error, ErrorKind, RawByteStr};
pub trait WithSerializer: Sized {
type Serializer<'x>: Serializer<'x, Self>
where
Self: 'x;
}
impl<X> WithSerializer for X
where
X: SerializeBytes + Debug,
{
type Serializer<'x>
= ContentSerializer<'x, X>
where
Self: 'x;
}
pub trait Serializer<'ser, T>: Iterator<Item = Result<Event<'ser>, Error>> + Debug + Sized {
fn init(value: &'ser T, name: Option<&'ser str>, is_root: bool) -> Result<Self, Error>;
}
pub trait SerializeSync: Sized {
type Error;
fn serialize<W: Write>(&self, root: &str, writer: &mut Writer<W>) -> Result<(), Self::Error>;
}
impl<X> SerializeSync for X
where
X: WithSerializer,
{
type Error = Error;
fn serialize<W: Write>(&self, root: &str, writer: &mut Writer<W>) -> Result<(), Self::Error> {
SerializeHelper::new(self, Some(root), writer)?.serialize_sync()
}
}
#[cfg(feature = "async")]
pub trait SerializeAsync: Sized {
type Future<'x>: std::future::Future<Output = Result<(), Self::Error>> + 'x
where
Self: 'x;
type Error;
fn serialize_async<'a, W: tokio::io::AsyncWrite + Unpin>(
&'a self,
root: &'a str,
writer: &'a mut Writer<W>,
) -> Self::Future<'a>;
}
#[cfg(feature = "async")]
impl<X> SerializeAsync for X
where
X: WithSerializer,
{
type Future<'x>
= std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), Self::Error>> + 'x>>
where
X: 'x;
type Error = Error;
fn serialize_async<'a, W: tokio::io::AsyncWrite + Unpin>(
&'a self,
root: &'a str,
writer: &'a mut Writer<W>,
) -> Self::Future<'a> {
Box::pin(async move {
SerializeHelper::new(self, Some(root), writer)?
.serialize_async()
.await
})
}
}
pub trait SerializeBytes: Sized {
fn serialize_bytes(&self) -> Result<Option<Cow<'_, str>>, Error>;
}
impl<X> SerializeBytes for X
where
X: ToString,
{
fn serialize_bytes(&self) -> Result<Option<Cow<'_, str>>, Error> {
Ok(Some(Cow::Owned(self.to_string())))
}
}
#[derive(Debug)]
#[allow(missing_docs)]
pub enum ContentSerializer<'ser, T> {
Begin {
name: &'ser str,
value: &'ser T,
},
Data {
name: &'ser str,
data: Cow<'ser, str>,
},
End {
name: &'ser str,
},
Done,
}
impl<'ser, T> Serializer<'ser, T> for ContentSerializer<'ser, T>
where
T: SerializeBytes + Debug,
{
fn init(value: &'ser T, name: Option<&'ser str>, is_root: bool) -> Result<Self, Error> {
let _is_root = is_root;
Ok(Self::Begin {
name: name.ok_or(ErrorKind::MissingName)?,
value,
})
}
}
impl<'ser, T> Iterator for ContentSerializer<'ser, T>
where
T: SerializeBytes + Debug,
{
type Item = Result<Event<'ser>, Error>;
fn next(&mut self) -> Option<Self::Item> {
match replace(self, Self::Done) {
Self::Begin { name, value } => match value.serialize_bytes() {
Ok(None) => Some(Ok(Event::Empty(BytesStart::new(name)))),
Ok(Some(data)) => {
if data.contains("]]>") {
return Some(Err(ErrorKind::InvalidData(RawByteStr::from_slice(
data.as_bytes(),
))
.into()));
}
*self = Self::Data { name, data };
Some(Ok(Event::Start(BytesStart::new(name))))
}
Err(error) => Some(Err(error)),
},
Self::Data { name, data } => {
*self = Self::End { name };
Some(Ok(Event::Text(BytesText::from_escaped(escape(data)))))
}
Self::End { name } => Some(Ok(Event::End(BytesEnd::new(name)))),
Self::Done => None,
}
}
}
#[derive(Debug)]
#[allow(missing_docs)]
pub enum IterSerializer<'ser, T, TItem>
where
&'ser T: IntoIterator<Item = &'ser TItem>,
<&'ser T as IntoIterator>::IntoIter: Debug,
TItem: WithSerializer + 'ser,
{
Pending {
name: Option<&'ser str>,
iter: <&'ser T as IntoIterator>::IntoIter,
},
Emitting {
name: Option<&'ser str>,
iter: <&'ser T as IntoIterator>::IntoIter,
serializer: TItem::Serializer<'ser>,
},
Done,
}
impl<'ser, T, TItem> Iterator for IterSerializer<'ser, T, TItem>
where
T: 'ser,
&'ser T: IntoIterator<Item = &'ser TItem>,
<&'ser T as IntoIterator>::IntoIter: Debug,
TItem: WithSerializer + 'ser,
{
type Item = Result<Event<'ser>, Error>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match replace(self, Self::Done) {
Self::Pending { name, mut iter } => {
let item = iter.next()?;
match Serializer::init(item, name, false) {
Ok(serializer) => {
*self = Self::Emitting {
name,
iter,
serializer,
}
}
Err(error) => return Some(Err(error)),
}
}
Self::Emitting {
name,
iter,
mut serializer,
} => {
if let Some(ret) = serializer.next() {
*self = Self::Emitting {
name,
iter,
serializer,
};
return Some(ret);
}
*self = Self::Pending { name, iter };
}
Self::Done => return None,
}
}
}
}
impl<'ser, T, TItem> Serializer<'ser, T> for IterSerializer<'ser, T, TItem>
where
T: Debug + 'ser,
&'ser T: IntoIterator<Item = &'ser TItem>,
<&'ser T as IntoIterator>::IntoIter: Debug,
TItem: WithSerializer + Debug + 'ser,
{
fn init(value: &'ser T, name: Option<&'ser str>, is_root: bool) -> Result<Self, Error> {
let _is_root = is_root;
Ok(Self::Pending {
name,
iter: value.into_iter(),
})
}
}
struct SerializeHelper<'a, T, W>
where
T: WithSerializer + 'a,
{
writer: &'a mut Writer<W>,
serializer: T::Serializer<'a>,
}
impl<'a, T, W> SerializeHelper<'a, T, W>
where
T: WithSerializer,
{
fn new(value: &'a T, name: Option<&'a str>, writer: &'a mut Writer<W>) -> Result<Self, Error> {
let serializer = Serializer::init(value, name, true)?;
Ok(Self { writer, serializer })
}
}
impl<T, W> SerializeHelper<'_, T, W>
where
T: WithSerializer,
W: Write,
{
fn serialize_sync(&mut self) -> Result<(), Error> {
for event in self.serializer.by_ref() {
self.writer
.write_event(event?)
.map_err(|error| ErrorKind::XmlError(error.into()))?;
}
Ok(())
}
}
#[cfg(feature = "async")]
impl<T, W> SerializeHelper<'_, T, W>
where
T: WithSerializer,
W: tokio::io::AsyncWrite + Unpin,
{
async fn serialize_async(&mut self) -> Result<(), Error> {
for event in self.serializer.by_ref() {
self.writer
.write_event_async(event?)
.await
.map_err(ErrorKind::XmlError)?;
}
Ok(())
}
}