use core::fmt::{Debug, Display, Formatter};
#[macro_export]
macro_rules! err {
($variant:expr, $desc:expr) => {{
$crate::error::Error::new($variant, $desc, file!(), line!())
}};
}
#[derive(Debug, Clone)]
pub struct Error<Variant> {
pub variant: Variant,
#[cfg(feature = "backtrace")]
pub description: &'static str,
#[cfg(feature = "backtrace")]
pub file: &'static str,
#[cfg(feature = "backtrace")]
pub line: u32,
#[cfg(all(feature = "backtrace", feature = "std"))]
pub backtrace: std::sync::Arc<std::backtrace::Backtrace>,
}
impl<Variant> Error<Variant> {
#[doc(hidden)]
#[allow(unused, reason = "some args are unused, depending on feature flags")]
pub fn new(variant: Variant, description: &'static str, file: &'static str, line: u32) -> Self {
Self {
variant,
#[cfg(feature = "backtrace")]
description,
#[cfg(feature = "backtrace")]
file,
#[cfg(feature = "backtrace")]
line,
#[cfg(all(feature = "backtrace", feature = "std"))]
backtrace: std::sync::Arc::new(std::backtrace::Backtrace::capture()),
}
}
pub fn into_variant<NewVariant>(self, new_variant: NewVariant) -> Error<NewVariant> {
Error {
variant: new_variant,
#[cfg(feature = "backtrace")]
description: self.description,
#[cfg(feature = "backtrace")]
file: self.file,
#[cfg(feature = "backtrace")]
line: self.line,
#[cfg(all(feature = "backtrace", feature = "std"))]
backtrace: self.backtrace,
}
}
}
impl<T> Display for Error<T>
where
T: Debug,
{
#[cfg(not(feature = "backtrace"))]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
write!(f, "MQTT error: {:#?}", self.variant)
}
#[cfg(all(feature = "backtrace", not(feature = "std")))]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
write!(f, "MQTT error: {:#?} at {}:{}", self.variant, self.file, self.line)
}
#[cfg(all(feature = "backtrace", feature = "std"))]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
use std::backtrace::BacktraceStatus;
write!(f, "MQTT error: {:#?} at", self.variant)?;
if self.backtrace.status() == BacktraceStatus::Captured {
writeln!(f, "{}", self.backtrace)?;
}
Ok(())
}
}
#[cfg(feature = "std")]
impl<T> std::error::Error for Error<T>
where
T: Debug,
{
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Memory;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Data {
Truncated,
SpecViolation,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Decoding {
Truncated,
SpecViolation,
Memory,
}
impl From<Error<Memory>> for Error<Decoding> {
fn from(error: Error<Memory>) -> Self {
error.into_variant(Decoding::Memory)
}
}
impl From<Error<Data>> for Error<Decoding> {
fn from(error: Error<Data>) -> Self {
match error.variant {
Data::Truncated => error.into_variant(Decoding::Truncated),
Data::SpecViolation => error.into_variant(Decoding::SpecViolation),
}
}
}
pub type MemoryError = Error<Memory>;
pub type DataError = Error<Data>;
pub type DecoderError = Error<Decoding>;