1use std::backtrace::Backtrace;
16use std::backtrace::BacktraceStatus;
17use std::error::Error as StdError;
18use std::fmt;
19use std::panic::Location;
20use std::sync::Arc;
21
22use crate::BacktracePolicy;
23use crate::CanonicalCondition;
24use crate::ComponentId;
25use crate::DiagnosticView;
26use crate::ErrorClass;
27use crate::ErrorCode;
28use crate::ErrorContext;
29use crate::ErrorDescriptor;
30use crate::ErrorSeverity;
31use crate::Exposure;
32use crate::FaultAttribution;
33use crate::ProjectionSpec;
34use crate::PublicErrorView;
35use crate::RecoveryHint;
36use crate::ViewContextViolation;
37
38type BoxError = Box<dyn StdError + Send + Sync + 'static>;
39
40pub struct Error {
57 inner: Box<ErrorInner>,
58}
59
60struct ErrorInner {
61 descriptor: &'static ErrorDescriptor,
62 context: ErrorContext,
63 source: Option<BoxError>,
64 location: &'static Location<'static>,
65 backtrace: Option<Backtrace>,
66}
67
68pub type Result<T> = std::result::Result<T, Error>;
70
71pub type SharedError = Arc<Error>;
73
74impl Error {
75 #[must_use]
77 #[track_caller]
78 pub fn new(descriptor: &'static ErrorDescriptor) -> Self {
79 Self::from_parts(descriptor, None)
80 }
81
82 #[must_use]
84 #[track_caller]
85 pub fn caused_by(descriptor: &'static ErrorDescriptor, source: impl StdError + Send + Sync + 'static) -> Self {
86 Self::from_parts(descriptor, Some(Box::new(source)))
87 }
88
89 #[track_caller]
90 fn from_parts(descriptor: &'static ErrorDescriptor, source: Option<BoxError>) -> Self {
91 Self {
92 inner: Box::new(ErrorInner {
93 descriptor,
94 context: ErrorContext::new(),
95 source,
96 location: Location::caller(),
97 backtrace: capture_backtrace(descriptor.backtrace_policy()),
98 }),
99 }
100 }
101
102 #[must_use]
104 pub fn with_context(mut self, context: ErrorContext) -> Self {
105 self.inner.context = context;
106 self
107 }
108
109 #[must_use]
111 pub fn with_source(mut self, source: impl StdError + Send + Sync + 'static) -> Self {
112 self.inner.source = Some(Box::new(source));
113 self
114 }
115
116 #[must_use]
118 pub fn with_boxed_source(mut self, source: Box<dyn StdError + Send + Sync + 'static>) -> Self {
119 self.inner.source = Some(source);
120 self
121 }
122
123 #[must_use]
125 pub const fn descriptor(&self) -> &'static ErrorDescriptor {
126 self.inner.descriptor
127 }
128
129 #[must_use]
131 pub const fn code(&self) -> ErrorCode {
132 self.descriptor().code()
133 }
134
135 #[must_use]
137 pub const fn class(&self) -> ErrorClass {
138 self.descriptor().class()
139 }
140
141 #[must_use]
143 pub const fn condition(&self) -> CanonicalCondition {
144 self.descriptor().condition()
145 }
146
147 #[must_use]
149 pub const fn fault(&self) -> FaultAttribution {
150 self.descriptor().fault()
151 }
152
153 #[must_use]
155 pub const fn component(&self) -> ComponentId {
156 self.descriptor().component()
157 }
158
159 #[must_use]
161 pub const fn severity(&self) -> ErrorSeverity {
162 self.descriptor().severity()
163 }
164
165 #[must_use]
167 pub const fn recovery_hint(&self) -> RecoveryHint {
168 self.descriptor().recovery_hint()
169 }
170
171 #[must_use]
173 pub const fn exposure(&self) -> Exposure {
174 self.descriptor().exposure()
175 }
176
177 #[must_use]
179 pub const fn backtrace_policy(&self) -> BacktracePolicy {
180 self.descriptor().backtrace_policy()
181 }
182
183 #[must_use]
185 pub const fn projection(&self) -> ProjectionSpec {
186 self.descriptor().projection()
187 }
188
189 #[must_use]
191 pub const fn context(&self) -> &ErrorContext {
192 &self.inner.context
193 }
194
195 pub fn public_view(&self) -> std::result::Result<PublicErrorView<'_>, ViewContextViolation> {
202 PublicErrorView::try_new(self.descriptor(), self.context())
203 }
204
205 pub fn diagnostic_view(&self) -> std::result::Result<DiagnosticView<'_>, ViewContextViolation> {
214 DiagnosticView::try_new(self.descriptor(), self.context())
215 }
216
217 #[must_use]
219 pub const fn location(&self) -> &'static Location<'static> {
220 self.inner.location
221 }
222
223 #[must_use]
225 pub const fn backtrace(&self) -> Option<&Backtrace> {
226 self.inner.backtrace.as_ref()
227 }
228}
229
230fn capture_backtrace(policy: BacktracePolicy) -> Option<Backtrace> {
231 match policy {
232 BacktracePolicy::Never => None,
233 BacktracePolicy::OnDemand => {
234 let backtrace = Backtrace::capture();
235 matches!(backtrace.status(), BacktraceStatus::Captured).then_some(backtrace)
236 }
237 }
238}
239
240impl fmt::Display for Error {
241 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(formatter, "{}: {}", self.code(), self.descriptor().public_message())
243 }
244}
245
246impl fmt::Debug for Error {
247 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
248 formatter
249 .debug_struct("Error")
250 .field("code", &self.code())
251 .field("class", &self.class())
252 .field("condition", &self.condition())
253 .field("fault", &self.fault())
254 .field("component", &self.component())
255 .field("severity", &self.severity())
256 .field("recovery_hint", &self.recovery_hint())
257 .field("exposure", &self.exposure())
258 .field("backtrace_policy", &self.backtrace_policy())
259 .field("context", &self.context())
260 .field("source_present", &self.inner.source.is_some())
261 .field("backtrace_captured", &self.inner.backtrace.is_some())
262 .finish()
263 }
264}
265
266impl StdError for Error {
267 fn source(&self) -> Option<&(dyn StdError + 'static)> {
268 self.inner
269 .source
270 .as_deref()
271 .map(|source| source as &(dyn StdError + 'static))
272 }
273}