use std::error::Error as StdError;
use std::fmt::Display;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Phase {
Resolve,
Connect,
FirstByte,
BetweenBytes,
Total,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorKind {
Resolve,
Connect,
Tls,
Redirect,
Timeout(Phase),
Body,
Decode,
Status,
Unsupported,
Cancelled,
Other,
}
#[derive(Debug, Clone)]
pub struct Error {
kind: ErrorKind,
source: Arc<dyn StdError + Send + Sync + 'static>, }
impl Error {
pub fn new<E>(kind: ErrorKind, source: E) -> Self
where
E: StdError + Send + Sync + 'static, {
Self {
kind,
source: Arc::new(source),
}
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
pub fn is_timeout(&self) -> bool {
matches!(self.kind, ErrorKind::Timeout(_))
}
pub fn is_redirect(&self) -> bool {
matches!(self.kind, ErrorKind::Redirect)
}
pub fn is_connect(&self) -> bool {
matches!(self.kind, ErrorKind::Connect)
}
pub fn is_unsupported(&self) -> bool {
matches!(self.kind, ErrorKind::Unsupported)
}
pub fn is_cancelled(&self) -> bool {
matches!(self.kind, ErrorKind::Cancelled)
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}: {}", self.kind, self.source)
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(&*self.source)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error as StdError;
use std::fmt::Display;
#[derive(Debug)]
struct Src;
impl Display for Src {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "boom")
}
}
impl StdError for Src {}
#[test]
fn preserves_kind_and_source_without_stringifying() {
let e = Error::new(ErrorKind::Resolve, Src);
assert_eq!(e.kind(), &ErrorKind::Resolve);
let src = StdError::source(&e).unwrap();
assert!(src.downcast_ref::<Src>().is_some());
}
#[test]
fn is_clone_which_reqwest_error_is_not() {
let e = Error::new(ErrorKind::Connect, Src);
let c = e.clone();
assert_eq!(c.kind(), &ErrorKind::Connect);
let a = StdError::source(&e).unwrap() as *const dyn StdError;
let b = StdError::source(&c).unwrap() as *const dyn StdError;
assert!(std::ptr::eq(a, b));
}
#[test]
fn predicates_agree_with_kind() {
assert!(Error::new(ErrorKind::Timeout(Phase::Connect), Src).is_timeout());
assert!(Error::new(ErrorKind::Redirect, Src).is_redirect());
assert!(Error::new(ErrorKind::Connect, Src).is_connect());
assert!(!Error::new(ErrorKind::Body, Src).is_connect());
assert!(Error::new(ErrorKind::Unsupported, Src).is_unsupported());
assert!(!Error::new(ErrorKind::Body, Src).is_unsupported());
assert!(Error::new(ErrorKind::Cancelled, Src).is_cancelled());
assert!(!Error::new(ErrorKind::Resolve, Src).is_cancelled());
assert!(!Error::new(ErrorKind::Other, Src).is_cancelled());
}
}