use std::borrow::Cow;
use crate::types::format_type_id;
use thiserror::Error;
const fn is_truthy_env(value: &str) -> bool {
let bytes = value.as_bytes();
(bytes.len() == 1 && bytes[0] == b'1')
|| (bytes.len() == 4
&& (bytes[0] | 0x20) == b't'
&& (bytes[1] | 0x20) == b'r'
&& (bytes[2] | 0x20) == b'u'
&& (bytes[3] | 0x20) == b'e')
}
pub const PANIC_ON_ERROR: bool = match option_env!("FORY_PANIC_ON_ERROR") {
Some(value) => is_truthy_env(value),
None => false,
};
#[inline(always)]
pub const fn should_panic_on_error() -> bool {
PANIC_ON_ERROR
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum Error {
#[error("{0}")]
TypeMismatch(Cow<'static, str>),
#[error("Buffer out of bound: {0} + {1} > {2}")]
BufferOutOfBound(usize, usize, usize),
#[error("{0}")]
EncodeError(Cow<'static, str>),
#[error("{0}")]
InvalidData(Cow<'static, str>),
#[error("{0}")]
InvalidRef(Cow<'static, str>),
#[error("{0}")]
UnknownEnum(Cow<'static, str>),
#[error("{0}")]
TypeError(Cow<'static, str>),
#[error("{0}")]
EncodingError(Cow<'static, str>),
#[error("{0}")]
DepthExceed(Cow<'static, str>),
#[error("{0}")]
Unsupported(Cow<'static, str>),
#[error("{0}")]
NotAllowed(Cow<'static, str>),
#[error("{0}")]
Unknown(Cow<'static, str>),
#[error("{0}")]
StructVersionMismatch(Cow<'static, str>),
}
impl Error {
#[inline(always)]
#[cold]
#[track_caller]
pub fn type_mismatch(type_a: u32, type_b: u32) -> Self {
let msg = format!(
"Type mismatch: local {} vs remote {}",
format_type_id(type_a),
format_type_id(type_b)
);
let err = Error::TypeMismatch(Cow::Owned(msg));
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn buffer_out_of_bound(offset: usize, length: usize, capacity: usize) -> Self {
let err = Error::BufferOutOfBound(offset, length, capacity);
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn encode_error<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::EncodeError(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn invalid_data<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::InvalidData(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn invalid_ref<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::InvalidRef(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn unknown_enum<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::UnknownEnum(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn type_error<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::TypeError(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn encoding_error<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::EncodingError(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn depth_exceed<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::DepthExceed(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn unsupported<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::Unsupported(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn not_allowed<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::NotAllowed(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn struct_version_mismatch<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::StructVersionMismatch(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(always)]
#[cold]
#[track_caller]
pub fn unknown<S: Into<Cow<'static, str>>>(s: S) -> Self {
let err = Error::Unknown(s.into());
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
err
}
#[inline(never)]
pub fn enhance_type_error<T: ?Sized + 'static>(err: Error) -> Error {
if let Error::TypeError(s) = err {
let mut msg = s.to_string();
msg.push_str(" (type: ");
msg.push_str(std::any::type_name::<T>());
msg.push(')');
Error::type_error(msg)
} else {
err
}
}
}
#[macro_export]
macro_rules! ensure {
($cond:expr, $msg:literal) => {
if !$cond {
return Err($crate::error::Error::unknown($msg));
}
};
($cond:expr, $err:expr) => {
if !$cond {
return Err($err);
}
};
($cond:expr, $fmt:expr, $($arg:tt)*) => {
if !$cond {
return Err($crate::error::Error::unknown(format!($fmt, $($arg)*)));
}
};
}
#[macro_export]
macro_rules! bail {
($err:expr) => {
return Err($crate::error::Error::unknown($err))
};
($fmt:expr, $($arg:tt)*) => {
return Err($crate::error::Error::unknown(format!($fmt, $($arg)*)))
};
}
#[macro_export]
macro_rules! not_allowed {
($err:expr) => {
return Err($crate::error::Error::not_allowed($err))
};
($fmt:expr, $($arg:tt)*) => {
return Err($crate::error::Error::not_allowed(format!($fmt, $($arg)*)))
};
}