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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use reqwest::{StatusCode, Url};
use thiserror::Error;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Error, Debug)]
pub enum Error {
    /// There was an error running some middleware
    #[error("Middleware error: {0}")]
    Middleware(#[from] anyhow::Error),
    /// Error from the underlying reqwest client
    #[error("Request error: {0}")]
    Reqwest(#[from] reqwest::Error),
}

impl Error {
    pub fn middleware<E>(err: E) -> Self
    where
        E: 'static + Send + Sync + std::error::Error,
    {
        Error::Middleware(err.into())
    }

    /// Returns a possible URL related to this error.
    pub fn url(&self) -> Option<&Url> {
        match self {
            Error::Middleware(_) => None,
            Error::Reqwest(e) => e.url(),
        }
    }

    /// Returns a mutable reference to the URL related to this error.
    ///
    /// This is useful if you need to remove sensitive information from the URL
    /// (e.g. an API key in the query), but do not want to remove the URL
    /// entirely.
    pub fn url_mut(&mut self) -> Option<&mut Url> {
        match self {
            Error::Middleware(_) => None,
            Error::Reqwest(e) => e.url_mut(),
        }
    }

    /// Adds a url related to this error (overwriting any existing).
    pub fn with_url(self, url: Url) -> Self {
        match self {
            Error::Middleware(_) => self,
            Error::Reqwest(e) => e.with_url(url).into(),
        }
    }

    /// Strips the related URL from this error (if, for example, it contains
    /// sensitive information).
    pub fn without_url(self) -> Self {
        match self {
            Error::Middleware(_) => self,
            Error::Reqwest(e) => e.without_url().into(),
        }
    }

    /// Returns true if the error is from any middleware.
    pub fn is_middleware(&self) -> bool {
        match self {
            Error::Middleware(_) => true,
            Error::Reqwest(_) => false,
        }
    }

    /// Returns true if the error is from a type `Builder`.
    pub fn is_builder(&self) -> bool {
        match self {
            Error::Middleware(_) => false,
            Error::Reqwest(e) => e.is_builder(),
        }
    }

    /// Returns true if the error is from a `RedirectPolicy`.
    pub fn is_redirect(&self) -> bool {
        match self {
            Error::Middleware(_) => false,
            Error::Reqwest(e) => e.is_redirect(),
        }
    }

    /// Returns true if the error is from `Response::error_for_status`.
    pub fn is_status(&self) -> bool {
        match self {
            Error::Middleware(_) => false,
            Error::Reqwest(e) => e.is_status(),
        }
    }

    /// Returns true if the error is related to a timeout.
    pub fn is_timeout(&self) -> bool {
        match self {
            Error::Middleware(_) => false,
            Error::Reqwest(e) => e.is_timeout(),
        }
    }

    /// Returns true if the error is related to the request.
    pub fn is_request(&self) -> bool {
        match self {
            Error::Middleware(_) => false,
            Error::Reqwest(e) => e.is_request(),
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    /// Returns true if the error is related to connect.
    pub fn is_connect(&self) -> bool {
        match self {
            Error::Middleware(_) => false,
            Error::Reqwest(e) => e.is_connect(),
        }
    }

    /// Returns true if the error is related to the request or response body.
    pub fn is_body(&self) -> bool {
        match self {
            Error::Middleware(_) => false,
            Error::Reqwest(e) => e.is_body(),
        }
    }

    /// Returns true if the error is related to decoding the response's body.
    pub fn is_decode(&self) -> bool {
        match self {
            Error::Middleware(_) => false,
            Error::Reqwest(e) => e.is_decode(),
        }
    }

    /// Returns the status code, if the error was generated from a response.
    pub fn status(&self) -> Option<StatusCode> {
        match self {
            Error::Middleware(_) => None,
            Error::Reqwest(e) => e.status(),
        }
    }
}