1use std::fmt::{self, Formatter};
2
3use crate::{AnyError, Location, Meta, backtrace_enabled};
4
5#[derive(Debug, Copy, Clone)]
7pub enum SourceFormat {
8 OneLine,
10 MultiLine {
12 location: bool,
14 },
15}
16
17pub trait StackError: fmt::Display + fmt::Debug + Send + Sync {
22 fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static);
24
25 fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync>;
27
28 fn as_dyn(&self) -> &dyn StackError;
30
31 fn meta(&self) -> Option<&Meta>;
33
34 fn source(&self) -> Option<ErrorRef<'_>>;
36
37 fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
39
40 fn is_transparent(&self) -> bool;
42
43 fn as_ref(&self) -> ErrorRef<'_> {
47 ErrorRef::Stack(self.as_dyn())
48 }
49
50 fn stack(&self) -> Chain<'_> {
52 Chain::stack(self.as_ref())
53 }
54
55 fn sources(&self) -> Chain<'_> {
57 Chain::sources(self.as_ref())
58 }
59
60 fn report(&self) -> Report<'_> {
62 Report::new(self.as_dyn())
63 }
64}
65
66pub trait StackErrorExt: StackError + Sized + 'static {
68 #[track_caller]
72 fn into_any(self) -> AnyError {
73 AnyError::from_stack(self)
74 }
75
76 #[track_caller]
78 fn context(self, context: impl fmt::Display) -> AnyError {
79 self.into_any().context(context)
80 }
81}
82
83impl<T: StackError + Sized + 'static> StackErrorExt for T {}
84
85#[derive(Copy, Clone, Debug)]
90pub enum ErrorRef<'a> {
91 Std(&'a (dyn std::error::Error + 'static), Option<&'a Meta>),
93 Stack(&'a dyn StackError),
95}
96
97impl<'a> ErrorRef<'a> {
98 pub fn std(err: &'a (dyn std::error::Error + 'static)) -> ErrorRef<'a> {
100 ErrorRef::Std(err, None)
101 }
102
103 pub(crate) fn std_with_meta(
104 err: &'a (dyn std::error::Error + 'static),
105 meta: &'a Meta,
106 ) -> ErrorRef<'a> {
107 ErrorRef::Std(err, Some(meta))
108 }
109
110 pub fn stack(err: &dyn StackError) -> ErrorRef<'_> {
112 ErrorRef::Stack(err)
113 }
114
115 pub fn is_transparent(&self) -> bool {
117 match self {
118 ErrorRef::Std(_, _) => false,
119 ErrorRef::Stack(error) => error.is_transparent(),
120 }
121 }
122
123 pub fn as_std(self) -> &'a dyn std::error::Error {
125 match self {
126 ErrorRef::Std(error, _) => error,
127 ErrorRef::Stack(error) => error.as_std(),
128 }
129 }
130
131 pub fn source(self) -> Option<ErrorRef<'a>> {
133 match self {
134 Self::Std(error, _) => error.source().map(ErrorRef::std),
135 Self::Stack(error) => StackError::source(error),
136 }
137 }
138
139 pub fn meta(&self) -> Option<&Meta> {
141 match self {
142 ErrorRef::Std(_, meta) => *meta,
143 ErrorRef::Stack(error) => error.meta(),
144 }
145 }
146
147 pub(crate) fn fmt_location(&self, f: &mut Formatter) -> fmt::Result {
149 fmt_location(self.meta().and_then(|m| m.location()), f)
150 }
151
152 pub fn fmt_message(&self, f: &mut Formatter) -> fmt::Result {
154 match self {
155 ErrorRef::Std(error, _) => fmt::Display::fmt(error, f),
156 ErrorRef::Stack(error) => error.fmt_message(f),
157 }
158 }
159
160 pub fn downcast_ref<T: std::error::Error + 'static>(self) -> Option<&'a T> {
162 match self {
163 ErrorRef::Std(error, _) => error.downcast_ref(),
164 ErrorRef::Stack(error) => error.as_std().downcast_ref(),
165 }
166 }
167}
168
169impl<'a> fmt::Display for ErrorRef<'a> {
170 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
171 match self {
172 Self::Std(error, _) => write!(f, "{error}"),
173 Self::Stack(error) => write!(f, "{error}"),
174 }
175 }
176}
177
178#[derive(Clone, Copy)]
180pub struct Report<'a> {
181 inner: &'a dyn StackError,
182 location: bool,
183 sources: Option<SourceFormat>,
184}
185
186impl<'a> Report<'a> {
187 pub(crate) fn new(inner: &'a dyn StackError) -> Self {
188 let location = backtrace_enabled();
189 Self {
190 inner,
191 location,
192 sources: None,
193 }
194 }
195
196 pub fn full(mut self) -> Self {
198 let location = backtrace_enabled();
199 self.location = location;
200 self.sources = Some(SourceFormat::MultiLine { location });
201 self
202 }
203
204 pub fn location(mut self, value: bool) -> Self {
209 self.location = value;
210 self
211 }
212
213 pub fn sources(mut self, format: Option<SourceFormat>) -> Self {
215 self.sources = format;
216 self
217 }
218}
219
220impl<'a> fmt::Display for Report<'a> {
221 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
222 match self.sources {
223 None => {
224 let item = self
225 .inner
226 .stack()
227 .find(|item| !item.is_transparent())
228 .unwrap_or_else(|| self.inner.as_ref());
229 item.fmt_message(f)?;
230 }
231 Some(format) => {
232 let mut stack = self
233 .inner
234 .stack()
235 .filter(|s| self.location || !s.is_transparent())
236 .peekable();
237 let mut is_first = true;
238 while let Some(item) = stack.next() {
239 match format {
240 SourceFormat::OneLine => {
241 if !is_first {
242 write!(f, ": ")?;
243 }
244 item.fmt_message(f)?;
245 }
246 SourceFormat::MultiLine { location } => {
247 if !is_first {
248 write!(f, " ")?;
249 }
250 item.fmt_message(f)?;
251 if location {
252 item.fmt_location(f)?;
253 }
254 if stack.peek().is_some() {
255 writeln!(f)?;
256 if is_first {
257 writeln!(f, "Caused by:")?;
258 }
259 }
260 }
261 }
262 is_first = false;
263 }
264 }
265 }
266 Ok(())
267 }
268}
269
270fn fmt_location(location: Option<&Location>, f: &mut Formatter) -> fmt::Result {
271 if let Some(location) = location {
272 write!(f, " ({})", location)?;
273 }
274 Ok(())
275}
276
277pub struct Chain<'a> {
279 item: Option<ErrorRef<'a>>,
280 skip: bool,
281}
282
283impl<'a> Chain<'a> {
284 fn stack(item: ErrorRef<'a>) -> Self {
286 Self {
287 item: Some(item),
288 skip: false,
289 }
290 }
291
292 fn sources(item: ErrorRef<'a>) -> Self {
294 Self {
295 item: Some(item),
296 skip: true,
297 }
298 }
299}
300
301impl<'a> Iterator for Chain<'a> {
302 type Item = ErrorRef<'a>;
303 fn next(&mut self) -> Option<Self::Item> {
304 loop {
305 let item = self.item?;
306 self.item = item.source();
307 if self.skip {
308 self.skip = false;
309 } else {
310 return Some(item);
311 }
312 }
313 }
314}
315
316macro_rules! impl_stack_error_for_std_error {
317 ($ty:ty) => {
318 impl StackError for $ty {
319 fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
320 self
321 }
322
323 fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync> {
324 self
325 }
326
327 fn as_dyn(&self) -> &dyn StackError {
328 self
329 }
330
331 fn meta(&self) -> Option<&Meta> {
332 None
333 }
334
335 fn source(&self) -> Option<ErrorRef<'_>> {
336 std::error::Error::source(self).map(ErrorRef::std)
337 }
338
339 fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 fmt::Display::fmt(self, f)
341 }
342
343 fn is_transparent(&self) -> bool {
344 false
345 }
346 }
347 };
348}
349
350impl_stack_error_for_std_error!(std::io::Error);
351impl_stack_error_for_std_error!(std::fmt::Error);
352impl_stack_error_for_std_error!(std::str::Utf8Error);
353impl_stack_error_for_std_error!(std::string::FromUtf8Error);
354impl_stack_error_for_std_error!(std::net::AddrParseError);
355impl_stack_error_for_std_error!(std::array::TryFromSliceError);
356
357