1#![no_std]
2#![warn(missing_docs)]
3#![doc = "README.md"]
4
5pub use pseudo_backtrace_derive::StackError;
6
7#[derive(Debug, Clone)]
9pub enum ErrorDetail<'a> {
10 Stacked(&'a dyn StackError),
12 End(&'a dyn core::error::Error),
14}
15
16impl<'a> ErrorDetail<'a> {
17 pub fn source(&'a self) -> &'a dyn core::error::Error {
19 match self {
20 ErrorDetail::Stacked(stack_error) => stack_error,
21 ErrorDetail::End(error) => error,
22 }
23 }
24
25 pub fn location(&self) -> Option<&'static core::panic::Location<'static>> {
27 match self {
28 ErrorDetail::Stacked(stack_error) => Some(stack_error.location()),
29 _ => None,
30 }
31 }
32}
33
34impl<'a, E> From<&'a E> for ErrorDetail<'a>
35where
36 E: StackError + Sized,
37{
38 fn from(stack_error: &'a E) -> Self {
39 ErrorDetail::Stacked(stack_error)
40 }
41}
42
43pub trait StackError: core::error::Error {
45 fn location(&self) -> &'static core::panic::Location<'static>;
47 fn next<'a>(&'a self) -> Option<ErrorDetail<'a>>;
49 fn iter<'a>(&'a self) -> Iter<'a>
51 where
52 Self: Sized,
53 {
54 Iter::new(self)
55 }
56}
57
58#[derive(Debug, Clone)]
60pub struct Iter<'a> {
61 source: Option<ErrorDetail<'a>>,
62}
63
64impl<'a> Iter<'a> {
65 const fn new<E>(source: &'a E) -> Self
66 where
67 E: StackError,
68 {
69 Iter {
70 source: Some(ErrorDetail::Stacked(source)),
71 }
72 }
73}
74
75impl<'a> Iterator for Iter<'a> {
76 type Item = ErrorDetail<'a>;
77
78 fn next(&mut self) -> Option<Self::Item> {
79 match self.source.take() {
80 Some(detail) => {
81 if let ErrorDetail::Stacked(stack_error) = &detail {
82 self.source = stack_error.next();
83 };
84 Some(detail)
85 }
86 None => None,
87 }
88 }
89}
90
91#[derive(Debug)]
105pub struct LocatedError<E> {
106 source: E,
107 location: &'static core::panic::Location<'static>,
108}
109
110impl<E> LocatedError<E> {
111 pub fn into_inner(self) -> E {
113 self.source
114 }
115}
116
117impl<E> core::fmt::Display for LocatedError<E>
118where
119 E: core::fmt::Display,
120{
121 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122 self.source.fmt(f)
123 }
124}
125
126impl<E> core::error::Error for LocatedError<E>
127where
128 E: core::error::Error,
129{
130 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
131 self.source.source()
132 }
133}
134
135impl<E> StackError for LocatedError<E>
136where
137 E: core::error::Error,
138{
139 fn location(&self) -> &'static core::panic::Location<'static> {
140 self.location
141 }
142
143 fn next<'a>(&'a self) -> Option<ErrorDetail<'a>> {
144 self.source.source().map(|e| ErrorDetail::End(e))
145 }
146}
147
148impl<E> From<E> for LocatedError<E> {
149 #[track_caller]
150 fn from(value: E) -> Self {
151 LocatedError {
152 source: value,
153 location: core::panic::Location::caller(),
154 }
155 }
156}
157
158#[derive(Debug, Clone)]
160pub struct StackWriter<'a> {
161 layer: usize,
162 source: ErrorDetail<'a>,
163}
164
165impl<'a> StackWriter<'a> {
166 pub fn layer(&self) -> usize {
168 self.layer
169 }
170 pub fn detail(&'a self) -> ErrorDetail<'a> {
172 self.source.clone()
173 }
174}
175
176impl<'a> core::fmt::Display for StackWriter<'a> {
177 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178 match self.source {
179 ErrorDetail::Stacked(stack_error) => {
180 write!(
181 f,
182 "{}: {}, at {}",
183 self.layer,
184 stack_error,
185 stack_error.location()
186 )
187 }
188 ErrorDetail::End(error) => {
189 write!(f, "{}: {}", self.layer, error,)
190 }
191 }
192 }
193}
194
195#[derive(Debug, Clone)]
197pub struct StackChain<'a> {
198 layer: usize,
199 source: ErrorDetail<'a>,
200}
201
202impl<'a> Iterator for StackChain<'a> {
203 type Item = StackWriter<'a>;
204 fn next(&mut self) -> Option<Self::Item> {
205 if self.layer == usize::MAX {
206 return None;
207 }
208
209 let out = StackWriter {
210 layer: self.layer,
211 source: self.source.clone(),
212 };
213
214 match self.source {
215 ErrorDetail::Stacked(stack_error) => {
216 if let Some(next) = stack_error.next() {
217 self.source = next;
218 self.layer += 1;
219 } else {
220 self.layer = usize::MAX;
221 }
222 }
223 ErrorDetail::End(_) => {
224 self.layer = usize::MAX;
225 }
226 }
227
228 Some(out)
229 }
230}
231
232impl<'a> core::fmt::Display for StackChain<'a> {
233 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
234 for w in self.clone() {
235 writeln!(f, "{}", w)?;
236 }
237
238 Ok(())
239 }
240}
241
242pub trait StackErrorExt: StackError {
244 fn to_chain<'a>(&'a self) -> StackChain<'a>
246 where
247 Self: Sized,
248 {
249 StackChain {
250 layer: 0,
251 source: ErrorDetail::from(self),
252 }
253 }
254
255 fn last<'a>(&'a self) -> ErrorDetail<'a>
257 where
258 Self: Sized,
259 {
260 let mut detail = ErrorDetail::from(self);
261 while let Some(next) = self.next() {
262 detail = next;
263 }
264 detail
265 }
266}
267
268impl<E: StackError> StackErrorExt for E {}