use std::backtrace::Backtrace;
use std::backtrace::BacktraceStatus;
use std::error::Error as StdError;
use std::fmt;
use std::panic::Location;
use std::sync::Arc;
use crate::BacktracePolicy;
use crate::CanonicalCondition;
use crate::ComponentId;
use crate::DiagnosticView;
use crate::ErrorClass;
use crate::ErrorCode;
use crate::ErrorContext;
use crate::ErrorDescriptor;
use crate::ErrorSeverity;
use crate::Exposure;
use crate::FaultAttribution;
use crate::ProjectionSpec;
use crate::PublicErrorView;
use crate::RecoveryHint;
use crate::ViewContextViolation;
type BoxError = Box<dyn StdError + Send + Sync + 'static>;
pub struct Error {
inner: Box<ErrorInner>,
}
struct ErrorInner {
descriptor: &'static ErrorDescriptor,
context: ErrorContext,
source: Option<BoxError>,
location: &'static Location<'static>,
backtrace: Option<Backtrace>,
}
pub type Result<T> = std::result::Result<T, Error>;
pub type SharedError = Arc<Error>;
impl Error {
#[must_use]
#[track_caller]
pub fn new(descriptor: &'static ErrorDescriptor) -> Self {
Self::from_parts(descriptor, None)
}
#[must_use]
#[track_caller]
pub fn caused_by(descriptor: &'static ErrorDescriptor, source: impl StdError + Send + Sync + 'static) -> Self {
Self::from_parts(descriptor, Some(Box::new(source)))
}
#[track_caller]
fn from_parts(descriptor: &'static ErrorDescriptor, source: Option<BoxError>) -> Self {
Self {
inner: Box::new(ErrorInner {
descriptor,
context: ErrorContext::new(),
source,
location: Location::caller(),
backtrace: capture_backtrace(descriptor.backtrace_policy()),
}),
}
}
#[must_use]
pub fn with_context(mut self, context: ErrorContext) -> Self {
self.inner.context = context;
self
}
#[must_use]
pub fn with_source(mut self, source: impl StdError + Send + Sync + 'static) -> Self {
self.inner.source = Some(Box::new(source));
self
}
#[must_use]
pub fn with_boxed_source(mut self, source: Box<dyn StdError + Send + Sync + 'static>) -> Self {
self.inner.source = Some(source);
self
}
#[must_use]
pub const fn descriptor(&self) -> &'static ErrorDescriptor {
self.inner.descriptor
}
#[must_use]
pub const fn code(&self) -> ErrorCode {
self.descriptor().code()
}
#[must_use]
pub const fn class(&self) -> ErrorClass {
self.descriptor().class()
}
#[must_use]
pub const fn condition(&self) -> CanonicalCondition {
self.descriptor().condition()
}
#[must_use]
pub const fn fault(&self) -> FaultAttribution {
self.descriptor().fault()
}
#[must_use]
pub const fn component(&self) -> ComponentId {
self.descriptor().component()
}
#[must_use]
pub const fn severity(&self) -> ErrorSeverity {
self.descriptor().severity()
}
#[must_use]
pub const fn recovery_hint(&self) -> RecoveryHint {
self.descriptor().recovery_hint()
}
#[must_use]
pub const fn exposure(&self) -> Exposure {
self.descriptor().exposure()
}
#[must_use]
pub const fn backtrace_policy(&self) -> BacktracePolicy {
self.descriptor().backtrace_policy()
}
#[must_use]
pub const fn projection(&self) -> ProjectionSpec {
self.descriptor().projection()
}
#[must_use]
pub const fn context(&self) -> &ErrorContext {
&self.inner.context
}
pub fn public_view(&self) -> std::result::Result<PublicErrorView<'_>, ViewContextViolation> {
PublicErrorView::try_new(self.descriptor(), self.context())
}
pub fn diagnostic_view(&self) -> std::result::Result<DiagnosticView<'_>, ViewContextViolation> {
DiagnosticView::try_new(self.descriptor(), self.context())
}
#[must_use]
pub const fn location(&self) -> &'static Location<'static> {
self.inner.location
}
#[must_use]
pub const fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace.as_ref()
}
}
fn capture_backtrace(policy: BacktracePolicy) -> Option<Backtrace> {
match policy {
BacktracePolicy::Never => None,
BacktracePolicy::OnDemand => {
let backtrace = Backtrace::capture();
matches!(backtrace.status(), BacktraceStatus::Captured).then_some(backtrace)
}
}
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}: {}", self.code(), self.descriptor().public_message())
}
}
impl fmt::Debug for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Error")
.field("code", &self.code())
.field("class", &self.class())
.field("condition", &self.condition())
.field("fault", &self.fault())
.field("component", &self.component())
.field("severity", &self.severity())
.field("recovery_hint", &self.recovery_hint())
.field("exposure", &self.exposure())
.field("backtrace_policy", &self.backtrace_policy())
.field("context", &self.context())
.field("source_present", &self.inner.source.is_some())
.field("backtrace_captured", &self.inner.backtrace.is_some())
.finish()
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.inner
.source
.as_deref()
.map(|source| source as &(dyn StdError + 'static))
}
}