1use std::any::Any;
16use std::fmt::{Debug, Formatter};
17use std::io::ErrorKind;
18use std::str::FromStr;
19use std::sync::Arc;
20
21use snafu::{FromString, Snafu};
22
23use crate::status_code::StatusCode;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum RetryHint {
32 Retryable,
34 NonRetryable,
38}
39
40const RETRY_HINT_RETRYABLE: &str = "retryable";
41const RETRY_HINT_NON_RETRYABLE: &str = "non_retryable";
42
43impl RetryHint {
44 pub fn is_retryable(self) -> bool {
45 matches!(self, RetryHint::Retryable)
46 }
47
48 pub fn as_str(self) -> &'static str {
49 match self {
50 RetryHint::Retryable => RETRY_HINT_RETRYABLE,
51 RetryHint::NonRetryable => RETRY_HINT_NON_RETRYABLE,
52 }
53 }
54}
55
56impl FromStr for RetryHint {
57 type Err = ();
58
59 fn from_str(s: &str) -> Result<Self, Self::Err> {
60 match s {
61 RETRY_HINT_RETRYABLE => Ok(RetryHint::Retryable),
62 RETRY_HINT_NON_RETRYABLE => Ok(RetryHint::NonRetryable),
63 _ => Err(()),
64 }
65 }
66}
67
68pub fn retry_hint_from_io_error(error: &std::io::Error) -> RetryHint {
75 match error.kind() {
76 ErrorKind::ConnectionRefused
77 | ErrorKind::ConnectionReset
78 | ErrorKind::HostUnreachable
79 | ErrorKind::NetworkUnreachable
80 | ErrorKind::ConnectionAborted
81 | ErrorKind::NotConnected
82 | ErrorKind::NetworkDown
83 | ErrorKind::BrokenPipe
84 | ErrorKind::WouldBlock
85 | ErrorKind::StaleNetworkFileHandle
86 | ErrorKind::TimedOut
87 | ErrorKind::ResourceBusy
88 | ErrorKind::Interrupted => RetryHint::Retryable,
89
90 _ => RetryHint::NonRetryable,
91 }
92}
93
94pub trait ErrorExt: StackError {
96 fn status_code(&self) -> StatusCode {
98 StatusCode::Unknown
99 }
100
101 fn retry_hint(&self) -> RetryHint {
107 RetryHint::NonRetryable
108 }
109
110 fn is_retryable(&self) -> bool {
115 self.retry_hint().is_retryable()
116 }
117
118 fn as_any(&self) -> &dyn Any;
121
122 fn output_msg(&self) -> String
123 where
124 Self: Sized,
125 {
126 match self.status_code() {
127 StatusCode::Unknown | StatusCode::Internal => {
128 format!("Internal error: {}", self.status_code() as u32)
130 }
131 _ => {
132 let error = self.last();
133 if let Some(external_error) = error.source() {
134 let external_root = external_error.sources().last().unwrap();
135
136 if error.transparent() {
137 format!("{external_root}")
138 } else {
139 format!("{error}: {external_root}")
140 }
141 } else {
142 format!("{error}")
143 }
144 }
145 }
146 }
147
148 fn root_cause(&self) -> Option<&dyn std::error::Error>
150 where
151 Self: Sized,
152 {
153 let error = self.last();
154 if let Some(external_error) = error.source() {
155 let external_root = external_error.sources().last().unwrap();
156 Some(external_root)
157 } else {
158 None
159 }
160 }
161}
162
163pub trait StackError: std::error::Error {
164 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>);
165
166 fn next(&self) -> Option<&dyn StackError>;
167
168 fn last(&self) -> &dyn StackError
169 where
170 Self: Sized,
171 {
172 let Some(mut result) = self.next() else {
173 return self;
174 };
175 while let Some(err) = result.next() {
176 result = err;
177 }
178 result
179 }
180
181 fn transparent(&self) -> bool {
186 false
187 }
188}
189
190impl<T: ?Sized + StackError> StackError for Arc<T> {
191 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
192 self.as_ref().debug_fmt(layer, buf)
193 }
194
195 fn next(&self) -> Option<&dyn StackError> {
196 self.as_ref().next()
197 }
198}
199
200impl<T: StackError> StackError for Box<T> {
201 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
202 self.as_ref().debug_fmt(layer, buf)
203 }
204
205 fn next(&self) -> Option<&dyn StackError> {
206 self.as_ref().next()
207 }
208}
209
210pub type WhateverResult<T> = Result<T, Whatever>;
214
215#[derive(Snafu)]
216#[snafu(display("{inner}"))]
217pub struct Whatever {
218 inner: snafu::Whatever,
219}
220
221impl Debug for Whatever {
222 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
223 write!(f, "{}", self.inner)
224 }
225}
226
227impl<E: ErrorExt> From<E> for Whatever {
228 fn from(e: E) -> Self {
229 Self {
230 inner: FromString::without_source(format!("{e:?}")),
231 }
232 }
233}
234
235impl From<String> for Whatever {
236 fn from(s: String) -> Self {
237 Self {
238 inner: FromString::without_source(s),
239 }
240 }
241}
242
243pub struct BoxedError {
245 inner: Box<dyn crate::ext::ErrorExt + Send + Sync>,
246}
247
248impl BoxedError {
249 pub fn new<E: crate::ext::ErrorExt + Send + Sync + 'static>(err: E) -> Self {
250 Self {
251 inner: Box::new(err),
252 }
253 }
254
255 pub fn into_inner(self) -> Box<dyn crate::ext::ErrorExt + Send + Sync> {
256 self.inner
257 }
258}
259
260impl std::fmt::Debug for BoxedError {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 let mut buf = vec![];
263 self.debug_fmt(0, &mut buf);
264 write!(f, "{}", buf.join("\n"))
265 }
266}
267
268impl std::fmt::Display for BoxedError {
269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270 write!(f, "{}", self.inner)
271 }
272}
273
274impl std::error::Error for BoxedError {
275 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
276 self.inner.source()
277 }
278}
279
280impl crate::ext::ErrorExt for BoxedError {
281 fn status_code(&self) -> crate::status_code::StatusCode {
282 self.inner.status_code()
283 }
284
285 fn retry_hint(&self) -> RetryHint {
286 self.inner.retry_hint()
287 }
288
289 fn as_any(&self) -> &dyn std::any::Any {
290 self.inner.as_any()
291 }
292}
293
294impl crate::snafu::ErrorCompat for BoxedError {
297 fn backtrace(&self) -> Option<&crate::snafu::Backtrace> {
298 None
299 }
300}
301
302impl StackError for BoxedError {
303 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
304 self.inner.debug_fmt(layer, buf)
305 }
306
307 fn next(&self) -> Option<&dyn StackError> {
308 self.inner.next()
309 }
310}
311
312#[derive(Debug)]
314pub struct PlainError {
315 msg: String,
316 status_code: StatusCode,
317}
318
319impl PlainError {
320 pub fn new(msg: String, status_code: StatusCode) -> Self {
321 Self { msg, status_code }
322 }
323}
324
325impl std::fmt::Display for PlainError {
326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327 write!(f, "{}", self.msg)
328 }
329}
330
331impl std::error::Error for PlainError {
332 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
333 None
334 }
335}
336
337impl crate::ext::ErrorExt for PlainError {
338 fn status_code(&self) -> crate::status_code::StatusCode {
339 self.status_code
340 }
341
342 fn as_any(&self) -> &dyn std::any::Any {
343 self as _
344 }
345}
346
347impl StackError for PlainError {
348 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
349 buf.push(format!("{}: {}", layer, self.msg))
350 }
351
352 fn next(&self) -> Option<&dyn StackError> {
353 None
354 }
355}