1use std::{error::Error as StdError, fmt};
6
7use bytes::BufMut;
8use serde_json::{from_slice as from_json_slice, Value as JsonValue};
9use thiserror::Error;
10
11use crate::{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: JsonValue,
24}
25
26impl fmt::Display for MatrixError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(f, "[{}] ", self.status_code.as_u16())?;
29 fmt::Display::fmt(&self.body, f)
30 }
31}
32
33impl StdError for MatrixError {}
34
35impl OutgoingResponse for MatrixError {
36 fn try_into_http_response<T: Default + BufMut>(
37 self,
38 ) -> Result<http::Response<T>, IntoHttpError> {
39 http::Response::builder()
40 .header(http::header::CONTENT_TYPE, "application/json")
41 .status(self.status_code)
42 .body(ruma_serde::json_to_buf(&self.body)?)
43 .map_err(Into::into)
44 }
45}
46
47impl EndpointError for MatrixError {
48 fn try_from_http_response<T: AsRef<[u8]>>(
49 response: http::Response<T>,
50 ) -> Result<Self, DeserializationError> {
51 Ok(Self {
52 status_code: response.status(),
53 body: from_json_slice(response.body().as_ref())?,
54 })
55 }
56}
57
58#[derive(Debug, Error)]
61#[non_exhaustive]
62pub enum IntoHttpError {
63 #[error(
65 "This endpoint has to be converted to http::Request using \
66 try_into_authenticated_http_request"
67 )]
68 NeedsAuthentication,
69
70 #[error("Endpoint was not supported by server-reported versions, but no unstable path to fall back to was defined.")]
75 NoUnstablePath,
76
77 #[error("Could not create any path variant for endpoint, as it was removed in version {0}")]
80 EndpointRemoved(MatrixVersion),
81
82 #[error("JSON serialization failed: {0}")]
84 Json(#[from] serde_json::Error),
85
86 #[error("Query parameter serialization failed: {0}")]
88 Query(#[from] ruma_serde::urlencoded::ser::Error),
89
90 #[error("Header serialization failed: {0}")]
92 Header(#[from] http::header::InvalidHeaderValue),
93
94 #[error("HTTP request construction failed: {0}")]
96 Http(#[from] http::Error),
97}
98
99#[derive(Debug, Error)]
101#[non_exhaustive]
102pub enum FromHttpRequestError {
103 #[error("deserialization failed: {0}")]
105 Deserialization(DeserializationError),
106
107 #[error("http method mismatch: expected {expected}, received: {received}")]
109 MethodMismatch {
110 expected: http::method::Method,
112 received: http::method::Method,
114 },
115}
116
117impl<T> From<T> for FromHttpRequestError
118where
119 T: Into<DeserializationError>,
120{
121 fn from(err: T) -> Self {
122 Self::Deserialization(err.into())
123 }
124}
125
126#[derive(Debug)]
128#[non_exhaustive]
129pub enum FromHttpResponseError<E> {
130 Deserialization(DeserializationError),
132
133 Http(ServerError<E>),
135}
136
137impl<E: fmt::Display> fmt::Display for FromHttpResponseError<E> {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 match self {
140 Self::Deserialization(err) => write!(f, "deserialization failed: {}", err),
141 Self::Http(err) => write!(f, "the server returned an error: {}", err),
142 }
143 }
144}
145
146impl<E> From<ServerError<E>> for FromHttpResponseError<E> {
147 fn from(err: ServerError<E>) -> Self {
148 Self::Http(err)
149 }
150}
151
152impl<E, T> From<T> for FromHttpResponseError<E>
153where
154 T: Into<DeserializationError>,
155{
156 fn from(err: T) -> Self {
157 Self::Deserialization(err.into())
158 }
159}
160
161impl<E: StdError> StdError for FromHttpResponseError<E> {}
162
163#[derive(Debug)]
165#[allow(clippy::exhaustive_enums)]
166pub enum ServerError<E> {
167 Known(E),
170
171 Unknown(DeserializationError),
173}
174
175impl<E: fmt::Display> fmt::Display for ServerError<E> {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 match self {
178 ServerError::Known(e) => fmt::Display::fmt(e, f),
179 ServerError::Unknown(res_err) => fmt::Display::fmt(res_err, f),
180 }
181 }
182}
183
184impl<E: StdError> StdError for ServerError<E> {}
185
186#[derive(Debug, Error)]
189#[non_exhaustive]
190pub enum DeserializationError {
191 #[error("{0}")]
193 Utf8(#[from] std::str::Utf8Error),
194
195 #[error("{0}")]
197 Json(#[from] serde_json::Error),
198
199 #[error("{0}")]
201 Query(#[from] ruma_serde::urlencoded::de::Error),
202
203 #[error("{0}")]
205 Ident(#[from] ruma_identifiers::Error),
206
207 #[error("{0}")]
209 Header(#[from] HeaderDeserializationError),
210}
211
212impl From<std::convert::Infallible> for DeserializationError {
213 fn from(err: std::convert::Infallible) -> Self {
214 match err {}
215 }
216}
217
218impl From<http::header::ToStrError> for DeserializationError {
219 fn from(err: http::header::ToStrError) -> Self {
220 Self::Header(HeaderDeserializationError::ToStrError(err))
221 }
222}
223
224#[derive(Debug, Error)]
226#[non_exhaustive]
227pub enum HeaderDeserializationError {
228 #[error("{0}")]
230 ToStrError(http::header::ToStrError),
231
232 #[error("Missing header `{0}`")]
234 MissingHeader(String),
235}
236
237#[derive(Debug)]
239#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
240pub struct UnknownVersionError;
241
242impl fmt::Display for UnknownVersionError {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 write!(f, "Version string was unknown.")
245 }
246}
247
248impl StdError for UnknownVersionError {}