#[allow(unused)]
use super::code::{Kind, Origin};
#[cfg(feature = "std")]
use crate::compat::{borrow::ToOwned, vec::Vec};
use crate::compat::{boxed::Box, error::Error as ErrorTrait, string::String};
use crate::error::code::ErrorCode;
use serde::{Deserialize, Serialize};
mod formatting;
#[cfg(all(feature = "std", feature = "tracing-error"))]
mod trace_config;
type BoxDynErr = Box<dyn ErrorTrait + Send + Sync + 'static>;
#[derive(Serialize, Deserialize)]
pub(super) struct ErrorData {
pub(super) code: ErrorCode,
#[cfg(feature = "std")]
payload: Vec<PayloadEntry>,
#[cfg(feature = "std")]
pub(super) source_loc: Location,
#[cfg(feature = "std")]
#[serde(skip, default)]
cause: Option<BoxDynErr>,
#[cfg(feature = "std")]
#[serde(skip, default)]
local: Option<Vec<LocalPayloadEntry>>,
}
impl ErrorData {
#[cold]
#[track_caller]
#[cfg(feature = "std")]
pub(super) fn new<E>(code: ErrorCode, cause: E) -> Self
where
E: Into<Box<dyn ErrorTrait + Send + Sync>>,
{
Self::new_inner(code, Some(cause.into()), Some(core::any::type_name::<E>()))
}
#[cold]
#[track_caller]
#[cfg(feature = "std")]
pub(super) fn new_without_cause(origin: Origin, kind: Kind) -> Self {
Self::new_inner(ErrorCode::new(origin, kind), None, None)
}
#[cold]
#[track_caller]
#[cfg(not(feature = "std"))]
pub(super) fn new<E>(code: ErrorCode, _cause: E) -> Self {
Self { code }
}
#[cold]
#[track_caller]
#[cfg(not(feature = "std"))]
pub(super) fn new_without_cause(origin: Origin, kind: Kind) -> Self {
Self {
code: ErrorCode::new(origin, kind),
}
}
#[cold]
#[track_caller]
#[cfg(feature = "std")]
pub(super) fn new_inner(
code: ErrorCode,
cause: Option<Box<dyn ErrorTrait + Send + Sync>>,
type_name: Option<&'static str>,
) -> Self {
let location = core::panic::Location::caller();
#[allow(unused_mut)]
let mut local: Vec<LocalPayloadEntry> = vec![];
#[cfg(all(feature = "std", feature = "backtrace"))]
if trace_config::BACKTRACE_ENABLED.get() {
local.push(LocalPayloadEntry::Backtrace(backtrace::Backtrace::new()));
}
#[cfg(all(feature = "std", feature = "tracing-error"))]
if trace_config::SPANTRACE_ENABLED.get() {
local.push(LocalPayloadEntry::Spantrace(
tracing_error::SpanTrace::capture(),
));
}
let mut payload = vec![];
if let Some(cause) = cause.as_ref() {
let debug = crate::compat::format!("{:?}", cause);
let display = crate::compat::format!("{}", cause);
payload.push(PayloadEntry::Cause {
display,
debug,
type_name: type_name.unwrap_or("<unknown>").to_owned(),
});
}
Self {
code,
cause,
source_loc: location.into(),
local: Some(local),
payload,
}
}
#[cold]
pub fn add_context(&mut self, _key: &str, _val: &dyn core::fmt::Display) {
#[cfg(feature = "std")]
self.payload.push(PayloadEntry::Info(
_key.into(),
crate::compat::format!("{}", _val),
));
}
#[cfg(feature = "std")]
pub fn cause(&self) -> Option<&(dyn ErrorTrait + Send + Sync + 'static)> {
self.cause.as_deref()
}
#[cfg(not(feature = "std"))]
pub fn cause(&self) -> Option<&(dyn ErrorTrait + Send + Sync + 'static)> {
None
}
}
#[derive(Serialize, Deserialize, Debug)]
enum PayloadEntry {
Cause {
display: String,
debug: String,
type_name: String,
},
Info(String, String),
}
#[derive(Debug)]
enum LocalPayloadEntry {
#[cfg(all(feature = "std", feature = "backtrace"))]
Backtrace(backtrace::Backtrace),
#[cfg(all(feature = "std", feature = "tracing-error"))]
Spantrace(tracing_error::SpanTrace),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub(crate) struct Location {
file: String,
line: u32,
column: u32,
}
impl From<&core::panic::Location<'_>> for Location {
fn from(p: &core::panic::Location<'_>) -> Self {
Self {
file: p.file().into(),
line: p.line(),
column: p.column(),
}
}
}
impl core::fmt::Display for Location {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}:{}:{}", self.file, self.line, self.column)
}
}