httpclient/
error.rs

1use crate::{Body, InMemoryResponse, InMemoryResponseExt, Response};
2use http::StatusCode;
3use std::fmt::{Debug, Display, Formatter};
4use std::str::Utf8Error;
5use std::string::FromUtf8Error;
6
7pub type Result<T = Response, E = Error> = std::result::Result<T, E>;
8pub type InMemoryError = Error<InMemoryResponse>;
9pub type InMemoryResult<T> = Result<T, InMemoryError>;
10pub type ProtocolResult<T> = Result<T, ProtocolError>;
11
12#[derive(Debug)]
13pub enum ProtocolError {
14    ConnectionError(hyper::Error),
15    Utf8Error(Utf8Error),
16    JsonError(serde_json::Error),
17    IoError(std::io::Error),
18    TooManyRedirects,
19    TooManyRetries,
20}
21
22impl std::error::Error for ProtocolError {}
23
24impl Display for ProtocolError {
25    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26        match self {
27            ProtocolError::ConnectionError(e) => write!(f, "ConnectionError: {e}"),
28            ProtocolError::Utf8Error(e) => write!(f, "Utf8Error: {e}"),
29            ProtocolError::JsonError(e) => write!(f, "JsonError: {e}"),
30            ProtocolError::IoError(e) => write!(f, "IoError: {e}"),
31            ProtocolError::TooManyRedirects => write!(f, "TooManyRedirects"),
32            ProtocolError::TooManyRetries => write!(f, "TooManyRetries"),
33        }
34    }
35}
36
37#[derive(Debug)]
38pub enum Error<T = Response> {
39    Protocol(ProtocolError),
40    HttpError(T),
41}
42
43impl Error<InMemoryResponse> {
44    #[must_use]
45    pub fn status(&self) -> Option<StatusCode> {
46        match self {
47            Error::HttpError(r) => Some(r.status()),
48            Error::Protocol(_) => None,
49        }
50    }
51}
52
53impl Error {
54    /// Get the error status code.
55    pub fn status(&self) -> Option<StatusCode> {
56        match self {
57            Error::HttpError(r) => Some(r.status()),
58            Error::Protocol(_) => None,
59        }
60    }
61
62    pub async fn into_content(self) -> InMemoryError {
63        match self {
64            Error::HttpError(r) => {
65                let (parts, body) = r.into_parts();
66                let content_type = parts.headers.get(http::header::CONTENT_TYPE);
67                let body = match body.into_content_type(content_type).await {
68                    Ok(body) => body,
69                    Err(e) => return e.into(),
70                };
71                Error::HttpError(InMemoryResponse::from_parts(parts, body))
72            }
73            Error::Protocol(e) => Error::Protocol(e),
74        }
75    }
76}
77
78impl InMemoryError {
79    #[must_use]
80    pub fn transform_error<T>(self) -> Error<T>
81    where
82        T: TryFrom<InMemoryResponse>,
83        T::Error: Into<Error<T>>,
84    {
85        match self {
86            InMemoryError::Protocol(e) => Error::Protocol(e),
87            InMemoryError::HttpError(e) => match e.try_into() {
88                Ok(r) => Error::HttpError(r),
89                Err(e) => e.into(),
90            },
91        }
92    }
93
94    #[must_use]
95    pub fn into_text(self) -> String {
96        match self {
97            InMemoryError::Protocol(e) => e.to_string(),
98            InMemoryError::HttpError(r) => r.text().unwrap_or_else(|e| format!("Error reading body as text: {e}")),
99        }
100    }
101}
102
103impl From<InMemoryError> for Error {
104    fn from(value: InMemoryError) -> Self {
105        match value {
106            Error::HttpError(r) => {
107                let (parts, body) = r.into_parts();
108                let body: Body = body.into();
109                let r = crate::Response::from_parts(parts, body);
110                Error::HttpError(r)
111            }
112            Error::Protocol(e) => Error::Protocol(e),
113        }
114    }
115}
116
117impl Display for InMemoryError {
118    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
119        match self {
120            Error::HttpError(r) => {
121                let body = r.body().text().ok().unwrap_or_else(|| format!("{:?}", r).into());
122                write!(f, "HttpError: {body}")
123            }
124            Error::Protocol(p) => write!(f, "ProtocolError: {p}"),
125        }
126    }
127}
128
129impl std::error::Error for InMemoryError {}
130
131impl serde::de::Error for InMemoryError {
132    fn custom<T: Display>(msg: T) -> Self {
133        Error::Protocol(ProtocolError::JsonError(serde_json::Error::custom(msg.to_string())))
134    }
135}
136
137impl<T> From<serde_json::Error> for Error<T> {
138    fn from(value: serde_json::Error) -> Self {
139        Error::Protocol(ProtocolError::JsonError(value))
140    }
141}
142
143impl From<std::io::Error> for ProtocolError {
144    fn from(value: std::io::Error) -> Self {
145        ProtocolError::IoError(value)
146    }
147}
148
149impl<T> From<hyper::Error> for Error<T> {
150    fn from(value: hyper::Error) -> Self {
151        Error::Protocol(ProtocolError::ConnectionError(value))
152    }
153}
154
155impl<T> From<hyper_util::client::legacy::Error> for Error<T> {
156    fn from(value: hyper_util::client::legacy::Error) -> Self {
157        Error::Protocol(ProtocolError::IoError(std::io::Error::new(std::io::ErrorKind::Other, value.to_string())))
158    }
159}
160
161impl<T> From<FromUtf8Error> for Error<T> {
162    fn from(value: FromUtf8Error) -> Self {
163        Error::Protocol(ProtocolError::Utf8Error(value.utf8_error()))
164    }
165}
166impl<T> From<ProtocolError> for Error<T> {
167    fn from(value: ProtocolError) -> Self {
168        Error::Protocol(value)
169    }
170}
171impl<T> From<Utf8Error> for Error<T> {
172    fn from(value: Utf8Error) -> Self {
173        Error::Protocol(ProtocolError::Utf8Error(value))
174    }
175}
176
177impl From<hyper::Error> for ProtocolError {
178    fn from(value: hyper::Error) -> Self {
179        Self::ConnectionError(value)
180    }
181}
182
183impl From<hyper_util::client::legacy::Error> for ProtocolError {
184    fn from(value: hyper_util::client::legacy::Error) -> Self {
185        Self::IoError(std::io::Error::new(std::io::ErrorKind::Other, value.to_string()))
186    }
187}
188
189impl From<serde_json::Error> for ProtocolError {
190    fn from(value: serde_json::Error) -> Self {
191        Self::JsonError(value)
192    }
193}
194
195impl From<FromUtf8Error> for ProtocolError {
196    fn from(value: FromUtf8Error) -> Self {
197        Self::Utf8Error(value.utf8_error())
198    }
199}
200
201impl From<Utf8Error> for ProtocolError {
202    fn from(value: Utf8Error) -> Self {
203        Self::Utf8Error(value)
204    }
205}