1#![warn(clippy::all)]
16pub use std::error::Error as ErrorTrait;
19use std::fmt;
20use std::fmt::Debug;
21use std::result::Result as StdResult;
22
23mod immut_str;
24pub use immut_str::ImmutStr;
25
26pub type BError = Box<Error>;
28pub type Result<T, E = BError> = StdResult<T, E>;
30
31#[derive(Debug)]
33pub struct Error {
34 pub etype: ErrorType,
36 pub esource: ErrorSource,
38 pub retry: RetryType,
40 pub cause: Option<Box<(dyn ErrorTrait + Send + Sync)>>,
42 pub context: Option<ImmutStr>,
44}
45
46#[derive(Debug, PartialEq, Eq, Clone)]
48pub enum ErrorSource {
49 Upstream,
51 Downstream,
53 Internal,
55 Unset,
57}
58
59#[derive(Debug, PartialEq, Eq, Clone, Copy)]
61pub enum RetryType {
62 Decided(bool),
63 ReusedOnly, }
65
66impl RetryType {
67 pub fn decide_reuse(&mut self, reused: bool) {
68 if matches!(self, RetryType::ReusedOnly) {
69 *self = RetryType::Decided(reused);
70 }
71 }
72
73 pub fn retry(&self) -> bool {
74 match self {
75 RetryType::Decided(b) => *b,
76 RetryType::ReusedOnly => {
77 panic!("Retry is not decided")
78 }
79 }
80 }
81}
82
83impl From<bool> for RetryType {
84 fn from(b: bool) -> Self {
85 RetryType::Decided(b)
86 }
87}
88
89impl ErrorSource {
90 pub fn as_str(&self) -> &str {
92 match self {
93 Self::Upstream => "Upstream",
94 Self::Downstream => "Downstream",
95 Self::Internal => "Internal",
96 Self::Unset => "",
97 }
98 }
99}
100
101#[derive(Debug, PartialEq, Eq, Clone)]
103pub enum ErrorType {
104 ConnectTimedout,
106 ConnectRefused,
107 ConnectNoRoute,
108 TLSWantX509Lookup,
109 TLSHandshakeFailure,
110 TLSHandshakeTimedout,
111 InvalidCert,
112 HandshakeError, ConnectError, BindError,
115 AcceptError,
116 SocketError,
117 ConnectProxyFailure,
118 InvalidHTTPHeader,
120 H1Error, H2Error, H2Downgrade, InvalidH2, ReadError,
126 WriteError,
127 ReadTimedout,
128 WriteTimedout,
129 ConnectionClosed,
130 HTTPStatus(u16),
132 FileOpenError,
134 FileCreateError,
135 FileReadError,
136 FileWriteError,
137 InternalError,
139 UnknownError,
141 Custom(&'static str),
145 CustomCode(&'static str, u16),
148}
149
150impl ErrorType {
151 pub const fn new(name: &'static str) -> Self {
153 ErrorType::Custom(name)
154 }
155
156 pub const fn new_code(name: &'static str, code: u16) -> Self {
158 ErrorType::CustomCode(name, code)
159 }
160
161 pub fn as_str(&self) -> &str {
163 match self {
164 ErrorType::ConnectTimedout => "ConnectTimedout",
165 ErrorType::ConnectRefused => "ConnectRefused",
166 ErrorType::ConnectNoRoute => "ConnectNoRoute",
167 ErrorType::ConnectProxyFailure => "ConnectProxyFailure",
168 ErrorType::TLSWantX509Lookup => "TLSWantX509Lookup",
169 ErrorType::TLSHandshakeFailure => "TLSHandshakeFailure",
170 ErrorType::TLSHandshakeTimedout => "TLSHandshakeTimedout",
171 ErrorType::InvalidCert => "InvalidCert",
172 ErrorType::HandshakeError => "HandshakeError",
173 ErrorType::ConnectError => "ConnectError",
174 ErrorType::BindError => "BindError",
175 ErrorType::AcceptError => "AcceptError",
176 ErrorType::SocketError => "SocketError",
177 ErrorType::InvalidHTTPHeader => "InvalidHTTPHeader",
178 ErrorType::H1Error => "H1Error",
179 ErrorType::H2Error => "H2Error",
180 ErrorType::InvalidH2 => "InvalidH2",
181 ErrorType::H2Downgrade => "H2Downgrade",
182 ErrorType::ReadError => "ReadError",
183 ErrorType::WriteError => "WriteError",
184 ErrorType::ReadTimedout => "ReadTimedout",
185 ErrorType::WriteTimedout => "WriteTimedout",
186 ErrorType::ConnectionClosed => "ConnectionClosed",
187 ErrorType::FileOpenError => "FileOpenError",
188 ErrorType::FileCreateError => "FileCreateError",
189 ErrorType::FileReadError => "FileReadError",
190 ErrorType::FileWriteError => "FileWriteError",
191 ErrorType::HTTPStatus(_) => "HTTPStatus",
192 ErrorType::InternalError => "InternalError",
193 ErrorType::UnknownError => "UnknownError",
194 ErrorType::Custom(s) => s,
195 ErrorType::CustomCode(s, _) => s,
196 }
197 }
198}
199
200impl Error {
201 #[inline]
203 pub fn create(
204 etype: ErrorType,
205 esource: ErrorSource,
206 context: Option<ImmutStr>,
207 cause: Option<Box<dyn ErrorTrait + Send + Sync>>,
208 ) -> BError {
209 let retry = if let Some(c) = cause.as_ref() {
210 if let Some(e) = c.downcast_ref::<BError>() {
211 e.retry
212 } else {
213 false.into()
214 }
215 } else {
216 false.into()
217 };
218 Box::new(Error {
219 etype,
220 esource,
221 retry,
222 cause,
223 context,
224 })
225 }
226
227 #[inline]
228 fn do_new(e: ErrorType, s: ErrorSource) -> BError {
229 Self::create(e, s, None, None)
230 }
231
232 #[inline]
234 pub fn new(e: ErrorType) -> BError {
235 Self::do_new(e, ErrorSource::Unset)
236 }
237
238 #[inline]
256 pub fn because<S: Into<ImmutStr>, E: Into<Box<dyn ErrorTrait + Send + Sync>>>(
257 e: ErrorType,
258 context: S,
259 cause: E,
260 ) -> BError {
261 Self::create(
262 e,
263 ErrorSource::Unset,
264 Some(context.into()),
265 Some(cause.into()),
266 )
267 }
268
269 #[inline]
271 pub fn e_because<T, S: Into<ImmutStr>, E: Into<Box<dyn ErrorTrait + Send + Sync>>>(
272 e: ErrorType,
273 context: S,
274 cause: E,
275 ) -> Result<T> {
276 Err(Self::because(e, context, cause))
277 }
278
279 #[inline]
281 pub fn explain<S: Into<ImmutStr>>(e: ErrorType, context: S) -> BError {
282 Self::create(e, ErrorSource::Unset, Some(context.into()), None)
283 }
284
285 #[inline]
287 pub fn e_explain<T, S: Into<ImmutStr>>(e: ErrorType, context: S) -> Result<T> {
288 Err(Self::explain(e, context))
289 }
290
291 #[inline]
294 pub fn new_up(e: ErrorType) -> BError {
295 Self::do_new(e, ErrorSource::Upstream)
296 }
297
298 #[inline]
299 pub fn new_down(e: ErrorType) -> BError {
300 Self::do_new(e, ErrorSource::Downstream)
301 }
302
303 #[inline]
304 pub fn new_in(e: ErrorType) -> BError {
305 Self::do_new(e, ErrorSource::Internal)
306 }
307
308 #[inline]
310 pub fn new_str(s: &'static str) -> BError {
311 Self::do_new(ErrorType::Custom(s), ErrorSource::Unset)
312 }
313
314 #[inline]
316 pub fn err<T>(e: ErrorType) -> Result<T> {
317 Err(Self::new(e))
318 }
319
320 #[inline]
321 pub fn err_up<T>(e: ErrorType) -> Result<T> {
322 Err(Self::new_up(e))
323 }
324
325 #[inline]
326 pub fn err_down<T>(e: ErrorType) -> Result<T> {
327 Err(Self::new_down(e))
328 }
329
330 #[inline]
331 pub fn err_in<T>(e: ErrorType) -> Result<T> {
332 Err(Self::new_in(e))
333 }
334
335 pub fn etype(&self) -> &ErrorType {
336 &self.etype
337 }
338
339 pub fn esource(&self) -> &ErrorSource {
340 &self.esource
341 }
342
343 pub fn retry(&self) -> bool {
344 self.retry.retry()
345 }
346
347 pub fn set_retry(&mut self, retry: bool) {
348 self.retry = retry.into();
349 }
350
351 pub fn reason_str(&self) -> &str {
352 self.etype.as_str()
353 }
354
355 pub fn source_str(&self) -> &str {
356 self.esource.as_str()
357 }
358
359 pub fn as_up(&mut self) {
362 self.esource = ErrorSource::Upstream;
363 }
364
365 pub fn as_down(&mut self) {
366 self.esource = ErrorSource::Downstream;
367 }
368
369 pub fn as_in(&mut self) {
370 self.esource = ErrorSource::Internal;
371 }
372
373 pub fn into_up(mut self: BError) -> BError {
375 self.as_up();
376 self
377 }
378
379 pub fn into_down(mut self: BError) -> BError {
380 self.as_down();
381 self
382 }
383
384 pub fn into_in(mut self: BError) -> BError {
385 self.as_in();
386 self
387 }
388
389 pub fn into_err<T>(self: BError) -> Result<T> {
390 Err(self)
391 }
392
393 pub fn set_cause<C: Into<Box<dyn ErrorTrait + Send + Sync>>>(&mut self, cause: C) {
394 self.cause = Some(cause.into());
395 }
396
397 pub fn set_context<T: Into<ImmutStr>>(&mut self, context: T) {
398 self.context = Some(context.into());
399 }
400
401 pub fn more_context<T: Into<ImmutStr>>(self: BError, context: T) -> BError {
418 let esource = self.esource.clone();
419 let retry = self.retry;
420 let mut e = Self::because(self.etype.clone(), context, self);
421 e.esource = esource;
422 e.retry = retry;
423 e
424 }
425
426 fn chain_display(&self, previous: Option<&Error>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 if previous.map(|p| p.esource != self.esource).unwrap_or(true) {
429 write!(f, "{}", self.esource.as_str())?
430 }
431 if previous.map(|p| p.etype != self.etype).unwrap_or(true) {
432 write!(f, " {}", self.etype.as_str())?
433 }
434
435 if let Some(c) = self.context.as_ref() {
436 write!(f, " context: {}", c)?;
437 }
438 if let Some(c) = self.cause.as_ref() {
439 if let Some(e) = c.downcast_ref::<BError>() {
440 write!(f, " cause: ")?;
441 e.chain_display(Some(self), f)
442 } else {
443 write!(f, " cause: {}", c)
444 }
445 } else {
446 Ok(())
447 }
448 }
449
450 pub fn root_etype(&self) -> &ErrorType {
452 self.cause.as_ref().map_or(&self.etype, |c| {
453 c.downcast_ref::<BError>()
455 .map_or(&self.etype, |e| e.root_etype())
456 })
457 }
458
459 pub fn root_cause(&self) -> &(dyn ErrorTrait + Send + Sync + 'static) {
460 self.cause.as_deref().map_or(self, |c| {
461 c.downcast_ref::<BError>().map_or(c, |e| e.root_cause())
462 })
463 }
464}
465
466impl fmt::Display for Error {
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 self.chain_display(None, f)
469 }
470}
471
472impl ErrorTrait for Error {}
473
474pub trait Context<T> {
476 fn err_context<C: Into<ImmutStr>, F: FnOnce() -> C>(self, context: F) -> Result<T, BError>;
480}
481
482impl<T> Context<T> for Result<T, BError> {
483 fn err_context<C: Into<ImmutStr>, F: FnOnce() -> C>(self, context: F) -> Result<T, BError> {
484 self.map_err(|e| e.more_context(context()))
485 }
486}
487
488pub trait OrErr<T, E> {
490 fn or_err(self, et: ErrorType, context: &'static str) -> Result<T, BError>
494 where
495 E: Into<Box<dyn ErrorTrait + Send + Sync>>;
496
497 fn or_err_with<C: Into<ImmutStr>, F: FnOnce() -> C>(
499 self,
500 et: ErrorType,
501 context: F,
502 ) -> Result<T, BError>
503 where
504 E: Into<Box<dyn ErrorTrait + Send + Sync>>;
505
506 fn explain_err<C: Into<ImmutStr>, F: FnOnce(E) -> C>(
510 self,
511 et: ErrorType,
512 context: F,
513 ) -> Result<T, BError>;
514
515 fn or_fail(self) -> Result<T>
519 where
520 E: Into<Box<dyn ErrorTrait + Send + Sync>>;
521}
522
523impl<T, E> OrErr<T, E> for Result<T, E> {
524 fn or_err(self, et: ErrorType, context: &'static str) -> Result<T, BError>
525 where
526 E: Into<Box<dyn ErrorTrait + Send + Sync>>,
527 {
528 self.map_err(|e| Error::because(et, context, e))
529 }
530
531 fn or_err_with<C: Into<ImmutStr>, F: FnOnce() -> C>(
532 self,
533 et: ErrorType,
534 context: F,
535 ) -> Result<T, BError>
536 where
537 E: Into<Box<dyn ErrorTrait + Send + Sync>>,
538 {
539 self.map_err(|e| Error::because(et, context(), e))
540 }
541
542 fn explain_err<C: Into<ImmutStr>, F: FnOnce(E) -> C>(
543 self,
544 et: ErrorType,
545 exp: F,
546 ) -> Result<T, BError> {
547 self.map_err(|e| Error::explain(et, exp(e)))
548 }
549
550 fn or_fail(self) -> Result<T, BError>
551 where
552 E: Into<Box<dyn ErrorTrait + Send + Sync>>,
553 {
554 self.map_err(|e| Error::because(ErrorType::InternalError, "", e))
555 }
556}
557
558pub trait OkOrErr<T> {
560 fn or_err(self, et: ErrorType, context: &'static str) -> Result<T, BError>;
561
562 fn or_err_with<C: Into<ImmutStr>, F: FnOnce() -> C>(
563 self,
564 et: ErrorType,
565 context: F,
566 ) -> Result<T, BError>;
567}
568
569impl<T> OkOrErr<T> for Option<T> {
570 fn or_err(self, et: ErrorType, context: &'static str) -> Result<T, BError> {
574 self.ok_or(Error::explain(et, context))
575 }
576
577 fn or_err_with<C: Into<ImmutStr>, F: FnOnce() -> C>(
579 self,
580 et: ErrorType,
581 context: F,
582 ) -> Result<T, BError> {
583 self.ok_or_else(|| Error::explain(et, context()))
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use super::*;
590
591 #[test]
592 fn test_chain_of_error() {
593 let e1 = Error::new(ErrorType::InternalError);
594 let mut e2 = Error::new(ErrorType::HTTPStatus(400));
595 e2.set_cause(e1);
596 assert_eq!(format!("{}", e2), " HTTPStatus cause: InternalError");
597 assert_eq!(e2.root_etype().as_str(), "InternalError");
598
599 let e3 = Error::new(ErrorType::InternalError);
600 let e4 = Error::because(ErrorType::HTTPStatus(400), "test", e3);
601 assert_eq!(
602 format!("{}", e4),
603 " HTTPStatus context: test cause: InternalError"
604 );
605 assert_eq!(e4.root_etype().as_str(), "InternalError");
606 }
607
608 #[test]
609 fn test_error_context() {
610 let mut e1 = Error::new(ErrorType::InternalError);
611 e1.set_context(format!("{} {}", "my", "context"));
612 assert_eq!(format!("{}", e1), " InternalError context: my context");
613 }
614
615 #[test]
616 fn test_context_trait() {
617 let e1: Result<(), BError> = Err(Error::new(ErrorType::InternalError));
618 let e2 = e1.err_context(|| "another");
619 assert_eq!(
620 format!("{}", e2.unwrap_err()),
621 " InternalError context: another cause: "
622 );
623 }
624
625 #[test]
626 fn test_cause_trait() {
627 let e1: Result<(), BError> = Err(Error::new(ErrorType::InternalError));
628 let e2 = e1.or_err(ErrorType::HTTPStatus(400), "another");
629 assert_eq!(
630 format!("{}", e2.unwrap_err()),
631 " HTTPStatus context: another cause: InternalError"
632 );
633 }
634
635 #[test]
636 fn test_option_some_ok() {
637 let m = Some(2);
638 let o = m.or_err(ErrorType::InternalError, "some is not an error!");
639 assert_eq!(2, o.unwrap());
640
641 let o = m.or_err_with(ErrorType::InternalError, || "some is not an error!");
642 assert_eq!(2, o.unwrap());
643 }
644
645 #[test]
646 fn test_option_none_err() {
647 let m: Option<i32> = None;
648 let e1 = m.or_err(ErrorType::InternalError, "none is an error!");
649 assert_eq!(
650 format!("{}", e1.unwrap_err()),
651 " InternalError context: none is an error!"
652 );
653
654 let e1 = m.or_err_with(ErrorType::InternalError, || "none is an error!");
655 assert_eq!(
656 format!("{}", e1.unwrap_err()),
657 " InternalError context: none is an error!"
658 );
659 }
660
661 #[test]
662 fn test_into() {
663 fn other_error() -> Result<(), &'static str> {
664 Err("oops")
665 }
666
667 fn surface_err() -> Result<()> {
668 other_error().or_fail()?; Ok(())
670 }
671
672 let e = surface_err().unwrap_err();
673 assert_eq!(format!("{}", e), " InternalError context: cause: oops");
674 }
675}