use std::fmt;
use std::io::{
self,
Write as IoWrite,
};
use std::pin::Pin;
use std::task::{
Context,
Poll,
};
use bytes::Bytes;
use serde::Serialize;
use tokio::sync::mpsc;
use tokio_stream::Stream;
use tokio_stream::wrappers::ReceiverStream;
use crate::error::FormatError;
const SERIALIZER_CHANNEL_CAPACITY: usize = 8;
const MAX_SERIALIZER_CHUNK_BYTES: usize = 64 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EncodeFormat {
Json,
Yaml,
MessagePack,
Postcard,
}
impl EncodeFormat {
#[must_use]
pub const fn media_type(self) -> &'static str {
match self {
Self::Json => "application/json",
Self::Yaml => "application/yaml",
Self::MessagePack => "application/vnd.msgpack",
Self::Postcard => "application/x-postcard",
}
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::Json => "JSON",
Self::Yaml => "YAML",
Self::MessagePack => "MessagePack",
Self::Postcard => "Postcard",
}
}
#[must_use]
pub fn from_content_type(content_type: &str) -> Option<Self> {
let media_type = content_type
.split(';')
.next()
.unwrap_or_default()
.trim()
.to_ascii_lowercase();
match media_type.as_str() {
"application/json" => Some(Self::Json),
"application/yaml" | "application/x-yaml" | "text/yaml" => Some(Self::Yaml),
"application/vnd.msgpack" | "application/msgpack" | "application/x-msgpack" => {
Some(Self::MessagePack)
}
"application/x-postcard" => Some(Self::Postcard),
_ => None,
}
}
pub fn encode_vec<T: Serialize>(self, value: &T) -> Result<Vec<u8>, FormatError> {
match self {
Self::Json => serde_json::to_vec(value).map_err(|error| self.unencodable(error)),
Self::Yaml => serde_yaml2::to_string(value)
.map(String::into_bytes)
.map_err(|error| self.unencodable(error)),
Self::MessagePack => {
rmp_serde::to_vec_named(value).map_err(|error| self.unencodable(error))
}
Self::Postcard => postcard::to_allocvec(value).map_err(|error| self.unencodable(error)),
}
}
pub fn encode_stream<T>(self, value: T) -> EncodedStream
where
T: Serialize + Send + 'static,
{
let (sender, receiver) = mpsc::channel(SERIALIZER_CHANNEL_CAPACITY);
tokio::task::spawn_blocking(move || {
let mut writer = ChannelWriter { sender };
let result = match self {
Self::Json => {
serde_json::to_writer(&mut writer, &value).map_err(|error| error.to_string())
}
Self::Yaml => {
let mut formatter = ChannelFormatter {
writer: &mut writer,
};
let mut serializer = serde_yaml2::ser::YamlSerializer::new(&mut formatter);
serializer.write(value).map_err(|error| error.to_string())
}
Self::MessagePack => value
.serialize(&mut rmp_serde::Serializer::new(&mut writer).with_struct_map())
.map_err(|error| error.to_string()),
Self::Postcard => postcard::to_io(&value, &mut writer)
.map(|_| ())
.map_err(|error| error.to_string()),
};
if let Err(error) = result {
let format_error = self.unencodable(error);
let _ = writer.sender.blocking_send(Err(format_error));
}
});
EncodedStream {
inner: ReceiverStream::new(receiver),
}
}
fn unencodable(self, error: impl std::fmt::Display) -> FormatError {
FormatError::Encoding {
format: self.name(),
detail: error.to_string(),
}
}
}
#[derive(Debug)]
pub struct EncodedStream {
inner: ReceiverStream<Result<Bytes, FormatError>>,
}
impl Stream for EncodedStream {
type Item = Result<Bytes, FormatError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Pin::new(&mut self.inner).poll_next(cx)
}
}
struct ChannelWriter {
sender: mpsc::Sender<Result<Bytes, FormatError>>,
}
impl IoWrite for ChannelWriter {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
if bytes.is_empty() {
return Ok(0);
}
for chunk in bytes.chunks(MAX_SERIALIZER_CHUNK_BYTES) {
self.sender
.blocking_send(Ok(Bytes::copy_from_slice(chunk)))
.map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, "encoded stream dropped"))?;
}
Ok(bytes.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
struct ChannelFormatter<'a> {
writer: &'a mut ChannelWriter,
}
impl fmt::Write for ChannelFormatter<'_> {
fn write_str(&mut self, value: &str) -> fmt::Result {
self.writer
.write_all(value.as_bytes())
.map_err(|_| fmt::Error)
}
}