use std::io::Read;
use serde::de::DeserializeOwned;
use crate::error::FormatError;
const POSTCARD_SCRATCH_BYTES: usize = 16 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecodeFormat {
Json,
MessagePack,
Postcard,
}
impl DecodeFormat {
#[must_use]
pub const fn media_type(self) -> &'static str {
match self {
Self::Json => "application/json",
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::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/vnd.msgpack" | "application/msgpack" | "application/x-msgpack" => {
Some(Self::MessagePack)
}
"application/x-postcard" => Some(Self::Postcard),
_ => None,
}
}
pub fn decode_slice<T: DeserializeOwned>(self, body: &[u8]) -> Result<T, FormatError> {
match self {
Self::Json => {
serde_json::from_slice(body).map_err(|error| FormatError::MalformedDocument {
format: self.name(),
detail: format!("line {} column {}: {error}", error.line(), error.column()),
})
}
Self::MessagePack => rmp_serde::from_slice(body).map_err(|error| self.malformed(error)),
Self::Postcard => postcard::from_bytes(body).map_err(|error| self.malformed(error)),
}
}
pub fn decode_reader<T, R>(self, reader: R) -> Result<T, FormatError>
where
T: DeserializeOwned,
R: Read,
{
match self {
Self::Json => serde_json::from_reader(reader).map_err(|error| {
if error.is_io() {
return FormatError::from_read_error(&error.into());
}
FormatError::MalformedDocument {
format: self.name(),
detail: error.to_string(),
}
}),
Self::MessagePack => rmp_serde::from_read(reader).map_err(|error| match error {
rmp_serde::decode::Error::InvalidMarkerRead(error)
| rmp_serde::decode::Error::InvalidDataRead(error) => {
FormatError::from_read_error(&error)
}
error => self.malformed(error),
}),
Self::Postcard => {
let mut scratch = [0_u8; POSTCARD_SCRATCH_BYTES];
postcard::from_io((reader, &mut scratch))
.map(|(value, _)| value)
.map_err(|error| self.malformed(error))
}
}
}
fn malformed(self, error: impl std::fmt::Display) -> FormatError {
FormatError::MalformedDocument {
format: self.name(),
detail: error.to_string(),
}
}
}