1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//! When something went wrong.

pub use tower::BoxError;

/// This type represent all possible errors that can occurs during the request processing.
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// An error occurred in the underlying client.
    #[error("Client error: {0}")]
    Client(ClientError),
    /// An error occurred while processing a middleware.
    #[error("Middleware error: {0}")]
    Middleware(BoxError),
}

/// An error that can occur while handling HTTP requests.
#[derive(Debug, thiserror::Error)]
#[error("{inner}")]
pub struct ClientError {
    inner: BoxError,
    kind: ClientErrorKind,
}

impl ClientError {
    /// Returns true if the error was caused by a timeout.
    #[must_use]
    pub fn is_timeout(&self) -> bool {
        matches!(self.kind, ClientErrorKind::Timeout)
    }

    /// Returns true if the error is related to connect
    #[must_use]
    pub fn is_connection(&self) -> bool {
        matches!(self.kind, ClientErrorKind::Timeout)
    }

    /// Returns true if the error is related to the request or response body.
    #[must_use]
    pub fn is_body(&self) -> bool {
        matches!(self.kind, ClientErrorKind::Body)
    }
}

#[derive(Debug, Clone, Copy)]
enum ClientErrorKind {
    Timeout,
    Connection,
    Body,
    Other,
}

impl From<reqwest::Error> for ClientError {
    fn from(value: reqwest::Error) -> Self {
        let kind = if value.is_timeout() {
            ClientErrorKind::Timeout
        } else if value.is_connect() {
            ClientErrorKind::Connection
        } else if value.is_body() {
            ClientErrorKind::Body
        } else {
            ClientErrorKind::Other
        };

        Self {
            inner: Box::new(value),
            kind,
        }
    }
}

impl From<reqwest::Error> for Error {
    fn from(value: reqwest::Error) -> Self {
        Self::Client(value.into())
    }
}