use crate::{ContentType, Message};
pub trait Serializer: Send + Sync {
type Error: std::error::Error + Send + Sync + 'static;
fn content_type(&self) -> &ContentType;
fn serialize<T: Message>(&self, body: &T) -> Result<bytes::Bytes, Self::Error>;
fn deserialize<T: Message>(&self, bytes: &[u8]) -> Result<T, Self::Error>;
}
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
mod json {
use core::fmt;
use bytes::Bytes;
use super::Serializer;
use crate::{ContentType, Message};
#[derive(Clone, Debug, Default)]
pub struct JsonSerializer;
impl Serializer for JsonSerializer {
type Error = JsonError;
fn content_type(&self) -> &ContentType {
&ContentType::JSON
}
fn serialize<T: Message>(&self, body: &T) -> Result<Bytes, Self::Error> {
serde_json::to_vec(body)
.map(Bytes::from)
.map_err(|source| JsonError::Serialize { source })
}
fn deserialize<T: Message>(&self, bytes: &[u8]) -> Result<T, Self::Error> {
serde_json::from_slice(bytes).map_err(|source| JsonError::Deserialize { source })
}
}
#[non_exhaustive]
pub enum JsonError {
Serialize {
source: serde_json::Error,
},
Deserialize {
source: serde_json::Error,
},
}
fn describe(source: &serde_json::Error) -> String {
let category = match source.classify() {
serde_json::error::Category::Io => "io",
serde_json::error::Category::Syntax => "syntax",
serde_json::error::Category::Data => "data",
serde_json::error::Category::Eof => "eof",
};
format!(
"{category} error at line {}, column {}",
source.line(),
source.column()
)
}
impl fmt::Display for JsonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Serialize { source } => {
write!(f, "failed to serialize to JSON: {}", describe(source))
}
Self::Deserialize { source } => {
write!(f, "failed to deserialize from JSON: {}", describe(source))
}
}
}
}
impl fmt::Debug for JsonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (variant, source) = match self {
Self::Serialize { source } => ("Serialize", source),
Self::Deserialize { source } => ("Deserialize", source),
};
f.debug_struct(variant)
.field("classification", &describe(source))
.finish()
}
}
impl std::error::Error for JsonError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Serialize { source } | Self::Deserialize { source } => Some(source),
}
}
}
}
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub use json::{JsonError, JsonSerializer};