1use core::{
2 any::Any,
3 fmt::{Debug, Display},
4 panic::Location,
5 slice::{Iter, IterMut},
6};
7
8use smallbox::{smallbox, SmallBox};
9use thin_vec::{thin_vec, ThinVec};
10
11use crate::{ProbablyNotRootCauseError, TimeoutError, UnitError};
12
13pub trait StackableErrorTrait: Display + Any + Send + Sync + 'static {
20 #[doc(hidden)]
23 fn _as_any(&self) -> &(dyn Any + Send + Sync);
24 #[doc(hidden)]
25 fn _as_any_mut(&mut self) -> &mut (dyn Any + Send + Sync);
26 #[doc(hidden)]
27 fn _as_display(&self) -> &(dyn Display + Send + Sync);
28}
29
30impl<T: Display + Send + Sync + 'static> StackableErrorTrait for T {
31 fn _as_any(&self) -> &(dyn Any + Send + Sync) {
32 self
33 }
34
35 fn _as_any_mut(&mut self) -> &mut (dyn Any + Send + Sync) {
36 self
37 }
38
39 fn _as_display(&self) -> &(dyn Display + Send + Sync) {
40 self
41 }
42}
43
44pub trait StackedErrorDowncast: StackableErrorTrait + Sized {
45 fn get_err(&self) -> &(impl Display + Send + Sync + 'static);
46
47 fn get_location(&self) -> Option<&'static Location<'static>>;
48
49 fn downcast_ref<E>(&self) -> Option<&E>
56 where
57 E: Display + Send + Sync + 'static;
58
59 fn downcast_mut<E>(&mut self) -> Option<&mut E>
60 where
61 E: Display + Send + Sync + 'static;
62}
63
64pub struct ErrorItem {
71 b: SmallBox<dyn StackableErrorTrait, smallbox::space::S4>,
72 l: Option<&'static Location<'static>>,
73}
74
75#[cfg(target_pointer_width = "64")]
76#[test]
77fn error_kind_size() {
78 assert_eq!(core::mem::size_of::<ErrorItem>(), 56);
79}
80
81impl ErrorItem {
82 pub fn new<E: Display + Send + Sync + 'static>(
83 e: E,
84 l: Option<&'static Location<'static>>,
85 ) -> Self {
86 Self { b: smallbox!(e), l }
87 }
88}
89
90impl Debug for ErrorItem {
91 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
92 f.write_fmt(format_args!("{}", self.get_err()))?;
93 if let Some(location) = self.get_location() {
94 f.write_fmt(format_args!(" {location:?}"))?;
95 }
96 Ok(())
97 }
98}
99
100impl Display for ErrorItem {
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 core::fmt::Debug::fmt(self, f)
103 }
104}
105
106impl StackedErrorDowncast for ErrorItem {
107 fn get_err(&self) -> &(impl Display + Send + Sync + 'static) {
108 &self.b
109 }
110
111 fn get_location(&self) -> Option<&'static Location<'static>> {
112 self.l
113 }
114
115 #[allow(clippy::needless_borrow)]
121 fn downcast_ref<E>(&self) -> Option<&E>
122 where
123 E: Display + Send + Sync + 'static,
124 {
125 (&*self.b)._as_any().downcast_ref()
126 }
127
128 #[allow(clippy::needless_borrow)]
129 fn downcast_mut<E>(&mut self) -> Option<&mut E>
130 where
131 E: Display + Send + Sync + 'static,
132 {
133 (&mut self.b)._as_any_mut().downcast_mut()
134 }
135}
136
137pub struct StackedError {
149 stack: ThinVec<ErrorItem>,
154}
155
156pub type Error = StackedError;
157
158impl Error {
161 pub fn empty() -> Self {
163 Self {
164 stack: ThinVec::new(),
165 }
166 }
167
168 #[track_caller]
170 pub fn new() -> Self {
171 Self::from_err(UnitError {})
172 }
173
174 #[track_caller]
175 pub fn from_err<E: Display + Send + Sync + 'static>(e: E) -> Self {
176 Self {
177 stack: thin_vec![ErrorItem::new(e, Some(Location::caller()))],
178 }
179 }
180
181 pub fn from_err_locationless<E: Display + Send + Sync + 'static>(e: E) -> Self {
182 Self {
183 stack: thin_vec![ErrorItem::new(e, None)],
184 }
185 }
186
187 #[track_caller]
189 pub fn push(&mut self) {
190 self.push_err(UnitError {})
191 }
192
193 #[track_caller]
195 pub fn add(self) -> Self {
196 self.add_err(UnitError {})
197 }
198
199 #[track_caller]
201 pub fn push_err<E: Display + Send + Sync + 'static>(&mut self, e: E) {
202 self.stack.push(ErrorItem::new(e, Some(Location::caller())));
203 }
204
205 #[track_caller]
207 pub fn add_err<E: Display + Send + Sync + 'static>(mut self, e: E) -> Self {
208 self.push_err(e);
209 self
210 }
211
212 pub fn push_err_locationless<E: Display + Send + Sync + 'static>(&mut self, e: E) {
214 self.stack.push(ErrorItem::new(e, None));
215 }
216
217 pub fn add_err_locationless<E: Display + Send + Sync + 'static>(mut self, e: E) -> Self {
219 self.push_err_locationless(e);
220 self
221 }
222
223 pub fn chain_errors(mut self, mut other: Self) -> Self {
225 self.stack.append(&mut other.stack);
226 self
227 }
228
229 #[track_caller]
231 pub fn timeout() -> Self {
232 Self::from_err(TimeoutError {})
233 }
234
235 #[track_caller]
237 pub fn probably_not_root_cause() -> Self {
238 Self::from_err(ProbablyNotRootCauseError {})
239 }
240
241 pub fn is_timeout(&self) -> bool {
243 for e in &self.stack {
244 if e.downcast_ref::<TimeoutError>().is_some() {
245 return true
246 }
247 }
248 false
249 }
250
251 pub fn is_probably_not_root_cause(&self) -> bool {
253 for e in &self.stack {
254 if e.downcast_ref::<ProbablyNotRootCauseError>().is_some() {
255 return true
256 }
257 }
258 false
259 }
260
261 pub fn iter(&self) -> Iter<ErrorItem> {
263 self.stack.iter()
264 }
265
266 pub fn iter_mut(&mut self) -> IterMut<ErrorItem> {
268 self.stack.iter_mut()
269 }
270}
271
272impl<'a> IntoIterator for &'a Error {
273 type IntoIter = Iter<'a, ErrorItem>;
274 type Item = &'a ErrorItem;
275
276 fn into_iter(self) -> Self::IntoIter {
277 self.iter()
278 }
279}
280
281impl<'a> IntoIterator for &'a mut Error {
282 type IntoIter = IterMut<'a, ErrorItem>;
283 type Item = &'a mut ErrorItem;
284
285 fn into_iter(self) -> Self::IntoIter {
286 self.iter_mut()
287 }
288}
289
290impl Default for Error {
291 #[track_caller]
292 fn default() -> Self {
293 Error::new()
294 }
295}
296
297impl core::error::Error for Error {}
298
299