1use std::any::Any;
16use std::fmt::{Debug, Formatter};
17use std::io::ErrorKind;
18use std::str::FromStr;
19use std::sync::Arc;
20
21use serde::{Deserialize, Deserializer, Serializer};
22use snafu::{FromString, Snafu};
23
24fn error_chain_root(err: &dyn std::error::Error) -> &dyn std::error::Error {
31 let mut root = err;
32 while let Some(source) = root.source() {
33 root = source;
34 }
35 root
36}
37
38use crate::status_code::StatusCode;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum RetryHint {
47 Retryable,
49 NonRetryable,
53}
54
55const RETRY_HINT_RETRYABLE: &str = "retryable";
56const RETRY_HINT_NON_RETRYABLE: &str = "non_retryable";
57
58impl RetryHint {
59 pub fn is_retryable(self) -> bool {
60 matches!(self, RetryHint::Retryable)
61 }
62
63 pub fn as_str(self) -> &'static str {
64 match self {
65 RetryHint::Retryable => RETRY_HINT_RETRYABLE,
66 RetryHint::NonRetryable => RETRY_HINT_NON_RETRYABLE,
67 }
68 }
69
70 pub fn serialize_as_str<S>(hint: &Self, serializer: S) -> Result<S::Ok, S::Error>
71 where
72 S: Serializer,
73 {
74 serializer.serialize_str(hint.as_str())
75 }
76
77 pub fn deserialize_from_str<'de, D>(deserializer: D) -> Result<Self, D::Error>
78 where
79 D: Deserializer<'de>,
80 {
81 let hint = String::deserialize(deserializer)?;
82 hint.parse::<RetryHint>()
83 .map_err(|_| serde::de::Error::custom(format!("unknown retry hint: {hint}")))
84 }
85}
86
87impl FromStr for RetryHint {
88 type Err = ();
89
90 fn from_str(s: &str) -> Result<Self, Self::Err> {
91 match s {
92 RETRY_HINT_RETRYABLE => Ok(RetryHint::Retryable),
93 RETRY_HINT_NON_RETRYABLE => Ok(RetryHint::NonRetryable),
94 _ => Err(()),
95 }
96 }
97}
98
99pub fn retry_hint_from_io_error(error: &std::io::Error) -> RetryHint {
106 match error.kind() {
107 ErrorKind::ConnectionRefused
108 | ErrorKind::ConnectionReset
109 | ErrorKind::HostUnreachable
110 | ErrorKind::NetworkUnreachable
111 | ErrorKind::ConnectionAborted
112 | ErrorKind::NotConnected
113 | ErrorKind::NetworkDown
114 | ErrorKind::BrokenPipe
115 | ErrorKind::WouldBlock
116 | ErrorKind::StaleNetworkFileHandle
117 | ErrorKind::TimedOut
118 | ErrorKind::ResourceBusy
119 | ErrorKind::Interrupted => RetryHint::Retryable,
120
121 _ => RetryHint::NonRetryable,
122 }
123}
124
125pub trait ErrorExt: StackError {
127 fn status_code(&self) -> StatusCode {
129 StatusCode::Unknown
130 }
131
132 fn retry_hint(&self) -> RetryHint {
138 RetryHint::NonRetryable
139 }
140
141 fn is_retryable(&self) -> bool {
146 self.retry_hint().is_retryable()
147 }
148
149 fn as_any(&self) -> &dyn Any;
152
153 fn output_msg(&self) -> String
154 where
155 Self: Sized,
156 {
157 match self.status_code() {
158 StatusCode::Unknown | StatusCode::Internal => {
159 format!("Internal error: {}", self.status_code() as u32)
161 }
162 _ => {
163 let error = self.last();
164 if let Some(external_error) = error.source() {
165 let external_root = error_chain_root(external_error);
166
167 if error.transparent() {
168 format!("{external_root}")
169 } else {
170 format!("{error}: {external_root}")
171 }
172 } else {
173 format!("{error}")
174 }
175 }
176 }
177 }
178
179 fn root_cause(&self) -> Option<&dyn std::error::Error>
181 where
182 Self: Sized,
183 {
184 let error = self.last();
185 if let Some(external_error) = error.source() {
186 let external_root = error_chain_root(external_error);
187 Some(external_root)
188 } else {
189 None
190 }
191 }
192}
193
194pub trait StackError: std::error::Error {
195 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>);
196
197 fn next(&self) -> Option<&dyn StackError>;
198
199 fn last(&self) -> &dyn StackError
200 where
201 Self: Sized,
202 {
203 let Some(mut result) = self.next() else {
204 return self;
205 };
206 while let Some(err) = result.next() {
207 result = err;
208 }
209 result
210 }
211
212 fn transparent(&self) -> bool {
217 false
218 }
219}
220
221impl<T: ?Sized + StackError> StackError for Arc<T> {
222 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
223 self.as_ref().debug_fmt(layer, buf)
224 }
225
226 fn next(&self) -> Option<&dyn StackError> {
227 self.as_ref().next()
228 }
229}
230
231impl<T: StackError> StackError for Box<T> {
232 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
233 self.as_ref().debug_fmt(layer, buf)
234 }
235
236 fn next(&self) -> Option<&dyn StackError> {
237 self.as_ref().next()
238 }
239}
240
241pub type WhateverResult<T> = Result<T, Whatever>;
245
246#[derive(Snafu)]
247#[snafu(display("{inner}"))]
248pub struct Whatever {
249 inner: snafu::Whatever,
250}
251
252impl Debug for Whatever {
253 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
254 write!(f, "{}", self.inner)
255 }
256}
257
258impl<E: ErrorExt> From<E> for Whatever {
259 fn from(e: E) -> Self {
260 Self {
261 inner: FromString::without_source(format!("{e:?}")),
262 }
263 }
264}
265
266impl From<String> for Whatever {
267 fn from(s: String) -> Self {
268 Self {
269 inner: FromString::without_source(s),
270 }
271 }
272}
273
274pub struct BoxedError {
276 inner: Box<dyn crate::ext::ErrorExt + Send + Sync>,
277}
278
279impl BoxedError {
280 pub fn new<E: crate::ext::ErrorExt + Send + Sync + 'static>(err: E) -> Self {
281 Self {
282 inner: Box::new(err),
283 }
284 }
285
286 pub fn into_inner(self) -> Box<dyn crate::ext::ErrorExt + Send + Sync> {
287 self.inner
288 }
289}
290
291impl std::fmt::Debug for BoxedError {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 let mut buf = vec![];
294 self.debug_fmt(0, &mut buf);
295 write!(f, "{}", buf.join("\n"))
296 }
297}
298
299impl std::fmt::Display for BoxedError {
300 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301 write!(f, "{}", self.inner)
302 }
303}
304
305impl std::error::Error for BoxedError {
306 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
307 self.inner.source()
308 }
309}
310
311impl crate::ext::ErrorExt for BoxedError {
312 fn status_code(&self) -> crate::status_code::StatusCode {
313 self.inner.status_code()
314 }
315
316 fn retry_hint(&self) -> RetryHint {
317 self.inner.retry_hint()
318 }
319
320 fn as_any(&self) -> &dyn std::any::Any {
321 self.inner.as_any()
322 }
323}
324
325impl crate::snafu::ErrorCompat for BoxedError {
328 fn backtrace(&self) -> Option<&crate::snafu::Backtrace> {
329 None
330 }
331}
332
333impl StackError for BoxedError {
334 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
335 self.inner.debug_fmt(layer, buf)
336 }
337
338 fn next(&self) -> Option<&dyn StackError> {
339 self.inner.next()
340 }
341}
342
343#[derive(Debug)]
345pub struct PlainError {
346 msg: String,
347 status_code: StatusCode,
348}
349
350impl PlainError {
351 pub fn new(msg: String, status_code: StatusCode) -> Self {
352 Self { msg, status_code }
353 }
354}
355
356impl std::fmt::Display for PlainError {
357 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358 write!(f, "{}", self.msg)
359 }
360}
361
362impl std::error::Error for PlainError {
363 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
364 None
365 }
366}
367
368impl crate::ext::ErrorExt for PlainError {
369 fn status_code(&self) -> crate::status_code::StatusCode {
370 self.status_code
371 }
372
373 fn as_any(&self) -> &dyn std::any::Any {
374 self as _
375 }
376}
377
378impl StackError for PlainError {
379 fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
380 buf.push(format!("{}: {}", layer, self.msg))
381 }
382
383 fn next(&self) -> Option<&dyn StackError> {
384 None
385 }
386}