use super::*;
use std::{error::Error, fmt};
use crate::runtime::BacktraceSuffix;
use snafu::IntoError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SerErrorKind {
InvalidData,
InvalidType,
Buffer,
NoBuffersAvailable,
NoClone,
ThirdParty,
Unknown,
}
#[derive(Debug, snafu::Snafu)]
pub struct SerError(SerErrorInner);
#[derive(Debug, snafu::Snafu)]
#[snafu(module)]
enum SerErrorInner {
#[snafu(display(
"The provided data was not appropriate for (de-)serialisation: {message} ({location}){}",
BacktraceSuffix(backtrace.as_ref())
))]
InvalidData {
message: String,
#[snafu(implicit)]
location: snafu::Location,
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display(
"The provided type was not appropriate for (de-)serialisation: {message} ({location}){}",
BacktraceSuffix(backtrace.as_ref())
))]
InvalidType {
message: String,
#[snafu(implicit)]
location: snafu::Location,
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display(
"An issue occurred with the serialisation buffers: {message} ({location}){}",
BacktraceSuffix(backtrace.as_ref())
))]
Buffer {
message: String,
#[snafu(implicit)]
location: snafu::Location,
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display(
"Serialising into a BufferPool with no available buffers: {message} ({location}){}",
BacktraceSuffix(backtrace.as_ref())
))]
NoBuffersAvailable {
message: String,
#[snafu(implicit)]
location: snafu::Location,
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display(
"The provided type can not be cloned, but try_clone() was attempted ({location}){}",
BacktraceSuffix(backtrace.as_ref())
))]
NoClone {
#[snafu(implicit)]
location: snafu::Location,
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display(
"A serialisation error occurred in a third-party crate while {context}: {source} ({location}){}",
BacktraceSuffix(backtrace.as_ref())
))]
ThirdParty {
context: String,
source: Box<dyn Error + 'static>,
#[snafu(implicit)]
location: snafu::Location,
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display(
"A serialisation error occurred: {message} ({location}){}",
BacktraceSuffix(backtrace.as_ref())
))]
Unknown {
message: String,
#[snafu(implicit)]
location: snafu::Location,
backtrace: Option<snafu::Backtrace>,
},
}
impl SerError {
pub fn kind(&self) -> SerErrorKind {
match &self.0 {
SerErrorInner::InvalidData { .. } => SerErrorKind::InvalidData,
SerErrorInner::InvalidType { .. } => SerErrorKind::InvalidType,
SerErrorInner::Buffer { .. } => SerErrorKind::Buffer,
SerErrorInner::NoBuffersAvailable { .. } => SerErrorKind::NoBuffersAvailable,
SerErrorInner::NoClone { .. } => SerErrorKind::NoClone,
SerErrorInner::ThirdParty { .. } => SerErrorKind::ThirdParty,
SerErrorInner::Unknown { .. } => SerErrorKind::Unknown,
}
}
#[track_caller]
pub fn invalid_data(message: impl Into<String>) -> Self {
ser_error_inner::InvalidDataSnafu {
message: message.into(),
}
.build()
.into()
}
#[track_caller]
pub fn invalid_type(message: impl Into<String>) -> Self {
ser_error_inner::InvalidTypeSnafu {
message: message.into(),
}
.build()
.into()
}
#[track_caller]
pub fn buffer(message: impl Into<String>) -> Self {
ser_error_inner::BufferSnafu {
message: message.into(),
}
.build()
.into()
}
#[track_caller]
pub fn no_buffers_available(message: impl Into<String>) -> Self {
ser_error_inner::NoBuffersAvailableSnafu {
message: message.into(),
}
.build()
.into()
}
#[track_caller]
pub fn no_clone() -> Self {
ser_error_inner::NoCloneSnafu.build().into()
}
#[track_caller]
pub fn third_party(context: impl Into<String>, source: impl Error + 'static) -> Self {
let source: Box<dyn Error + 'static> = Box::new(source);
ser_error_inner::ThirdPartySnafu {
context: context.into(),
}
.into_error(source)
.into()
}
#[track_caller]
pub fn unknown(message: impl Into<String>) -> Self {
ser_error_inner::UnknownSnafu {
message: message.into(),
}
.build()
.into()
}
#[track_caller]
pub fn from_debug<E: Debug>(error: E) -> SerError {
let msg = format!("Wrapped error: {:?}", error);
SerError::unknown(msg)
}
}
pub trait SerialisationId {
const SER_ID: SerId;
}
pub trait Serialiser<T>: Send + TryClone {
fn ser_id(&self) -> SerId;
fn size_hint(&self) -> Option<usize> {
None
}
fn serialise(&self, v: &T, buf: &mut dyn BufMut) -> Result<(), SerError>;
fn try_clone_data(&self, _v: &T) -> Option<T> {
None
}
}
pub trait Serialisable: Send + Debug {
fn ser_id(&self) -> SerId;
fn size_hint(&self) -> Option<usize>;
fn serialise(&self, buf: &mut dyn BufMut) -> Result<(), SerError>;
fn local(self: Box<Self>) -> Result<Box<dyn Any + Send>, Box<dyn Serialisable>>;
fn serialised(&self) -> Result<crate::messaging::Serialised, SerError> {
crate::serialisation::ser_helpers::serialise_to_serialised(self)
}
fn cloned(&self) -> Option<Box<dyn Serialisable>> {
None
}
}
impl<T, S> From<(T, S)> for SerialisableValue<T, S>
where
T: Send + Debug + 'static,
S: Serialiser<T> + 'static,
{
fn from(t: (T, S)) -> Self {
SerialisableValue::from_tuple(t)
}
}
impl<T, S> From<(T, S)> for Box<dyn Serialisable>
where
T: Send + Debug + 'static,
S: Serialiser<T> + 'static,
{
fn from(t: (T, S)) -> Self {
let sv: SerialisableValue<T, S> = t.into();
Box::new(sv) as Box<dyn Serialisable>
}
}
impl<T> From<T> for Box<dyn Serialisable>
where
T: Send + Debug + Serialisable + Sized + 'static,
{
fn from(t: T) -> Self {
Box::new(t) as Box<dyn Serialisable>
}
}
pub struct SerialisableValue<T, S>
where
T: Send + Debug,
S: Serialiser<T>,
{
pub v: T,
pub ser: S,
}
impl<T, S> SerialisableValue<T, S>
where
T: Send + Debug + 'static,
S: Serialiser<T>,
{
pub fn new(v: T, ser: S) -> Self {
SerialisableValue { v, ser }
}
pub fn from_tuple(t: (T, S)) -> Self {
Self::new(t.0, t.1)
}
}
impl<T, S> Serialisable for SerialisableValue<T, S>
where
T: Send + Debug + 'static,
S: Serialiser<T> + 'static,
{
fn ser_id(&self) -> SerId {
self.ser.ser_id()
}
fn size_hint(&self) -> Option<usize> {
self.ser.size_hint()
}
fn serialise(&self, buf: &mut dyn BufMut) -> Result<(), SerError> {
self.ser.serialise(&self.v, buf)
}
fn local(self: Box<Self>) -> Result<Box<dyn Any + Send>, Box<dyn Serialisable>> {
let b: Box<dyn Any + Send> = Box::new(self.v);
Ok(b)
}
fn cloned(&self) -> Option<Box<dyn Serialisable>> {
self.ser.try_clone().ok().and_then(|ser| {
self.ser.try_clone_data(&self.v).map(|v| {
let b: Box<dyn Serialisable> = Box::new(SerialisableValue::new(v, ser));
b
})
})
}
}
impl<T, S> Debug for SerialisableValue<T, S>
where
T: Send + Debug,
S: Serialiser<T>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"Serialisable(id={:?},size={:?}, value={:?})",
self.ser.ser_id(),
self.ser.size_hint(),
self.v
)
}
}
pub trait Deserialiser<T>: Send {
const SER_ID: SerId;
fn deserialise(buf: &mut dyn Buf) -> Result<T, SerError>;
}
pub trait Deserialisable<T> {
fn ser_id(&self) -> SerId;
fn get_deserialised(self) -> Result<T, SerError>;
}
#[cfg(test)]
mod tests {
use super::*;
use std::{error::Error, io};
#[test]
fn ser_error_reports_stable_kind() {
let error = SerError::invalid_data("bad payload");
assert_eq!(SerErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("bad payload"));
assert!(error.to_string().contains("core/src/serialisation/core.rs"));
}
#[test]
fn ser_error_preserves_third_party_source() {
let source = io::Error::new(io::ErrorKind::InvalidData, "external failure");
let error = SerError::third_party("test", source);
assert_eq!(SerErrorKind::ThirdParty, error.kind());
assert_eq!(
"external failure",
error.source().expect("third-party source").to_string()
);
}
}