use crate::error_code::ErrorCodeInfo;
use core::fmt::{Arguments, Display, Formatter};
use core::panic::Location;
pub trait ErrorImplFunctions: Clone {
type FrameIter<'a>: Iterator<Item = ErrorFrameImpl> + 'a
where Self: 'a;
fn new(source: ErrorOrigin, args: Option<&Arguments<'_>>) -> ErrorImpl;
fn push_context(&mut self, source: &'static ErrorInfoImpl, args: Option<&Arguments<'_>>);
fn code(&self) -> Option<&'static ErrorCodeInfo>;
fn iter<'a>(&'a self) -> Self::FrameIter<'a>;
}
#[derive(Copy, Clone)]
#[repr(align(4))]
pub struct ErrorInfoImpl {
pub error_code: Option<&'static ErrorCodeInfo>,
pub message_static: StaticMessageInfo,
pub location: Option<&'static DecodedLocation>,
}
impl ErrorInfoImpl {
pub fn is_code_only(&self) -> bool {
self.location.is_none()
}
}
#[derive(Copy, Clone)]
pub enum StaticMessageInfo {
Unformatted(&'static str),
NoFormat(&'static str),
None,
}
#[derive(Copy, Clone, Debug)]
pub struct DecodedLocation {
pub module: &'static str,
pub line: u32,
pub column: u32,
}
impl From<&'static Location<'static>> for DecodedLocation {
fn from(value: &'static Location<'static>) -> Self {
DecodedLocation { module: value.file(), line: value.line(), column: value.column() }
}
}
impl DecodedLocation {
fn is_same(&self, other: DecodedLocation) -> bool {
self.module == other.module && self.line == other.line
}
}
#[derive(Copy, Clone)]
pub enum ErrorOrigin {
StaticOrigin(&'static ErrorInfoImpl),
TypeOrigin(&'static str, Option<&'static ErrorInfoImpl>),
}
#[derive(Clone, Debug)]
pub struct ErrorFrameImpl {
data: ErrorFrameData,
location: Option<DecodedLocation>,
}
impl Display for ErrorFrameImpl {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match &self.data {
ErrorFrameData::InternalContext(ctx) => write!(f, "{}", ctx.message())?,
ErrorFrameData::TypeFrame(ty, info) => match info {
Some(info) if info.message.is_some() => write!(
f,
"{} ({}::{})",
info.message.unwrap(),
info.type_name,
info.variant_name
)?,
Some(info) => {
write!(f, "<from type: {}> ({}::{})", ty, info.type_name, info.variant_name)?
}
None => write!(f, "<from type: {}>", ty)?,
},
ErrorFrameData::NormalFrame(msg, info) => match info {
Some(info) if info.message.is_some() && msg.is_none() => write!(
f,
"{} ({}::{})",
info.message.unwrap(),
info.type_name,
info.variant_name
)?,
Some(info) if msg.is_some() => write!(
f,
"{} ({}::{})",
msg.as_ref().unwrap(),
info.type_name,
info.variant_name
)?,
Some(info) => write!(f, "{}::{}", info.type_name, info.variant_name)?,
None if msg.is_some() => write!(f, "{}", msg.as_ref().unwrap())?,
None => write!(f, "<internal error: no message or code given???>")?,
},
}
if let Some(location) = &self.location {
write!(f, " [at {}:{}:{}]", location.module, location.line, location.column)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
enum ErrorFrameData {
InternalContext(InternalContextType),
TypeFrame(&'static str, Option<&'static ErrorCodeInfo>),
NormalFrame(Option<MessageContainer>, Option<&'static ErrorCodeInfo>),
}
impl ErrorFrameData {
fn decode_static(
data: Option<&'static ErrorInfoImpl>,
formatted: Option<MessageContainer>,
) -> ErrorFrameData {
ErrorFrameData::NormalFrame(
formatted.or_else(|| match data.map(|x| x.message_static) {
Some(StaticMessageInfo::Unformatted(msg)) => {
Some(MessageContainer::IncompleteStatic(msg))
}
Some(StaticMessageInfo::NoFormat(msg)) => Some(MessageContainer::Static(msg)),
_ => None,
}),
data.and_then(|x| x.error_code),
)
}
}
#[derive(Clone, Debug)]
enum MessageContainer {
Static(&'static str),
IncompleteStatic(&'static str),
#[cfg(feature = "repr_full")]
Formatted(alloc::string::String),
}
impl MessageContainer {
fn as_str(&self) -> &str {
match self {
MessageContainer::Static(v) => v,
MessageContainer::IncompleteStatic(v) => v,
#[cfg(feature = "repr_full")]
MessageContainer::Formatted(v) => v.as_str(),
}
}
fn is_incomplete(&self) -> bool {
matches!(self, MessageContainer::IncompleteStatic(_))
}
}
impl Display for MessageContainer {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
if self.is_incomplete() {
write!(f, "<unformatted:> ")?;
}
write!(f, "{}", self.as_str())?;
Ok(())
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
enum InternalContextType {
ErrorTypeConstructed,
OriginalTypeLost,
FurtherFramesOmitted,
}
impl InternalContextType {
fn message(&self) -> &'static str {
match self {
InternalContextType::ErrorTypeConstructed => "<ErrorInfo constructed>",
InternalContextType::OriginalTypeLost => "<original error type lost>",
InternalContextType::FurtherFramesOmitted => "<some frames have been omitted>",
}
}
}
const _COMMON_CHECKS: () = {
const fn test<T: ErrorImplFunctions + Sync + Send>() {}
test::<ErrorImpl>();
};
#[cfg(feature = "repr_full")]
mod full;
#[cfg(feature = "repr_full")]
pub use full::ErrorImpl;
#[cfg(any(
feature = "repr_unboxed",
feature = "repr_unboxed_location",
not(any(feature = "repr_full"))
))]
mod unboxed;
#[cfg(any(
feature = "repr_unboxed",
feature = "repr_unboxed_location",
not(any(feature = "repr_full"))
))]
pub use unboxed::ErrorImpl;
#[cfg(any(
all(feature = "repr_full", feature = "repr_unboxed_location"),
all(feature = "repr_full", feature = "repr_unboxed"),
all(feature = "repr_unboxed_location", feature = "repr_unboxed"),
))]
const _: () = {
compile_error!(
"You may only use one of `repr_full`, `repr_unboxed` or `repr_unboxed_location`."
);
};