1use std::{error::Error as StdError, fmt, num::ParseIntError, sync::Arc};
6
7use bytes::{BufMut, Bytes};
8use serde_json::{from_slice as from_json_slice, Value as JsonValue};
9use thiserror::Error;
10
11use super::{EndpointError, MatrixVersion, OutgoingResponse};
12
13#[allow(clippy::exhaustive_structs)]
17#[derive(Clone, Debug)]
18pub struct MatrixError {
19 pub status_code: http::StatusCode,
21
22 pub body: MatrixErrorBody,
24}
25
26impl fmt::Display for MatrixError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 let status_code = self.status_code.as_u16();
29 match &self.body {
30 MatrixErrorBody::Json(json) => write!(f, "[{status_code}] {json}"),
31 MatrixErrorBody::NotJson { .. } => write!(f, "[{status_code}] <non-json bytes>"),
32 }
33 }
34}
35
36impl StdError for MatrixError {}
37
38impl OutgoingResponse for MatrixError {
39 fn try_into_http_response<T: Default + BufMut>(
40 self,
41 ) -> Result<http::Response<T>, IntoHttpError> {
42 http::Response::builder()
43 .header(http::header::CONTENT_TYPE, "application/json")
44 .status(self.status_code)
45 .body(match self.body {
46 MatrixErrorBody::Json(json) => crate::serde::json_to_buf(&json)?,
47 MatrixErrorBody::NotJson { .. } => {
48 return Err(IntoHttpError::Json(serde::ser::Error::custom(
49 "attempted to serialize MatrixErrorBody::NotJson",
50 )));
51 }
52 })
53 .map_err(Into::into)
54 }
55}
56
57impl EndpointError for MatrixError {
58 fn from_http_response<T: AsRef<[u8]>>(response: http::Response<T>) -> Self {
59 let status_code = response.status();
60 let body = MatrixErrorBody::from_bytes(response.body().as_ref());
61 Self { status_code, body }
62 }
63}
64
65#[derive(Clone, Debug)]
67#[allow(clippy::exhaustive_enums)]
68pub enum MatrixErrorBody {
69 Json(JsonValue),
71
72 NotJson {
74 bytes: Bytes,
76
77 deserialization_error: Arc<serde_json::Error>,
79 },
80}
81
82impl MatrixErrorBody {
83 pub fn from_bytes(body_bytes: &[u8]) -> Self {
85 match from_json_slice(body_bytes) {
86 Ok(json) => MatrixErrorBody::Json(json),
87 Err(e) => MatrixErrorBody::NotJson {
88 bytes: Bytes::copy_from_slice(body_bytes),
89 deserialization_error: Arc::new(e),
90 },
91 }
92 }
93}
94
95#[derive(Debug, Error)]
98#[non_exhaustive]
99pub enum IntoHttpError {
100 #[error("no access token given, but this endpoint requires one")]
102 NeedsAuthentication,
103
104 #[error(
109 "endpoint was not supported by server-reported versions, \
110 but no unstable path to fall back to was defined"
111 )]
112 NoUnstablePath,
113
114 #[error("could not create any path variant for endpoint, as it was removed in version {0}")]
117 EndpointRemoved(MatrixVersion),
118
119 #[error("JSON serialization failed: {0}")]
121 Json(#[from] serde_json::Error),
122
123 #[error("query parameter serialization failed: {0}")]
125 Query(#[from] serde_html_form::ser::Error),
126
127 #[error("header serialization failed: {0}")]
129 Header(#[from] HeaderSerializationError),
130
131 #[error("HTTP request construction failed: {0}")]
133 Http(#[from] http::Error),
134}
135
136impl From<http::header::InvalidHeaderValue> for IntoHttpError {
137 fn from(value: http::header::InvalidHeaderValue) -> Self {
138 Self::Header(value.into())
139 }
140}
141
142#[derive(Debug, Error)]
144#[non_exhaustive]
145pub enum FromHttpRequestError {
146 #[error("deserialization failed: {0}")]
148 Deserialization(DeserializationError),
149
150 #[error("http method mismatch: expected {expected}, received: {received}")]
152 MethodMismatch {
153 expected: http::method::Method,
155 received: http::method::Method,
157 },
158}
159
160impl<T> From<T> for FromHttpRequestError
161where
162 T: Into<DeserializationError>,
163{
164 fn from(err: T) -> Self {
165 Self::Deserialization(err.into())
166 }
167}
168
169#[derive(Debug)]
171#[non_exhaustive]
172pub enum FromHttpResponseError<E> {
173 Deserialization(DeserializationError),
175
176 Server(E),
178}
179
180impl<E> FromHttpResponseError<E> {
181 pub fn map<F>(self, f: impl FnOnce(E) -> F) -> FromHttpResponseError<F> {
184 match self {
185 Self::Deserialization(d) => FromHttpResponseError::Deserialization(d),
186 Self::Server(s) => FromHttpResponseError::Server(f(s)),
187 }
188 }
189}
190
191impl<E, F> FromHttpResponseError<Result<E, F>> {
192 pub fn transpose(self) -> Result<FromHttpResponseError<E>, F> {
194 match self {
195 Self::Deserialization(d) => Ok(FromHttpResponseError::Deserialization(d)),
196 Self::Server(s) => s.map(FromHttpResponseError::Server),
197 }
198 }
199}
200
201impl<E: fmt::Display> fmt::Display for FromHttpResponseError<E> {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match self {
204 Self::Deserialization(err) => write!(f, "deserialization failed: {err}"),
205 Self::Server(err) => write!(f, "the server returned an error: {err}"),
206 }
207 }
208}
209
210impl<E, T> From<T> for FromHttpResponseError<E>
211where
212 T: Into<DeserializationError>,
213{
214 fn from(err: T) -> Self {
215 Self::Deserialization(err.into())
216 }
217}
218
219impl<E: StdError> StdError for FromHttpResponseError<E> {}
220
221#[derive(Debug, Error)]
224#[non_exhaustive]
225pub enum DeserializationError {
226 #[error(transparent)]
228 Utf8(#[from] std::str::Utf8Error),
229
230 #[error(transparent)]
232 Json(#[from] serde_json::Error),
233
234 #[error(transparent)]
236 Query(#[from] serde_html_form::de::Error),
237
238 #[error(transparent)]
240 Ident(#[from] crate::IdParseError),
241
242 #[error(transparent)]
244 Header(#[from] HeaderDeserializationError),
245
246 #[error(transparent)]
248 MultipartMixed(#[from] MultipartMixedDeserializationError),
249}
250
251impl From<std::convert::Infallible> for DeserializationError {
252 fn from(err: std::convert::Infallible) -> Self {
253 match err {}
254 }
255}
256
257impl From<http::header::ToStrError> for DeserializationError {
258 fn from(err: http::header::ToStrError) -> Self {
259 Self::Header(HeaderDeserializationError::ToStrError(err))
260 }
261}
262
263#[derive(Debug, Error)]
265#[non_exhaustive]
266pub enum HeaderDeserializationError {
267 #[error("{0}")]
269 ToStrError(#[from] http::header::ToStrError),
270
271 #[error("{0}")]
273 ParseIntError(#[from] ParseIntError),
274
275 #[error("failed to parse HTTP date")]
277 InvalidHttpDate,
278
279 #[error("missing header `{0}`")]
281 MissingHeader(String),
282
283 #[error("invalid header: {0}")]
285 InvalidHeader(Box<dyn std::error::Error + Send + Sync + 'static>),
286
287 #[error(
289 "The {header} header was received with an unexpected value, \
290 expected {expected}, received {unexpected}"
291 )]
292 InvalidHeaderValue {
293 header: String,
295 expected: String,
297 unexpected: String,
299 },
300
301 #[error(
304 "The `Content-Type` header for a `multipart/mixed` response is missing the `boundary` attribute"
305 )]
306 MissingMultipartBoundary,
307}
308
309#[derive(Debug, Error)]
311#[non_exhaustive]
312pub enum MultipartMixedDeserializationError {
313 #[error(
315 "multipart/mixed response does not have enough body parts, \
316 expected {expected}, found {found}"
317 )]
318 MissingBodyParts {
319 expected: usize,
321 found: usize,
323 },
324
325 #[error("multipart/mixed body part is missing separator between headers and content")]
327 MissingBodyPartInnerSeparator,
328
329 #[error("multipart/mixed body part header is missing separator between name and value")]
331 MissingHeaderSeparator,
332
333 #[error("invalid multipart/mixed header: {0}")]
335 InvalidHeader(Box<dyn std::error::Error + Send + Sync + 'static>),
336}
337
338#[derive(Debug)]
340#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
341pub struct UnknownVersionError;
342
343impl fmt::Display for UnknownVersionError {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 write!(f, "version string was unknown")
346 }
347}
348
349impl StdError for UnknownVersionError {}
350
351#[derive(Debug)]
354#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
355pub struct IncorrectArgumentCount {
356 pub expected: usize,
358
359 pub got: usize,
361}
362
363impl fmt::Display for IncorrectArgumentCount {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 write!(f, "incorrect path argument count, expected {}, got {}", self.expected, self.got)
366 }
367}
368
369impl StdError for IncorrectArgumentCount {}
370
371#[derive(Debug, Error)]
373#[non_exhaustive]
374pub enum HeaderSerializationError {
375 #[error(transparent)]
377 ToHeaderValue(#[from] http::header::InvalidHeaderValue),
378
379 #[error("invalid HTTP date")]
384 InvalidHttpDate,
385}