1mod err;
2mod log;
3mod panic;
4mod response;
5mod serde;
6
7use std::{
8 any::Any,
9 borrow::Cow,
10 convert::Infallible,
11 sync::{Mutex, PoisonError},
12};
13
14pub use self::{err::visit, log::*};
15use crate::utils::{assert_ref_unwind_safe, assert_send, assert_sync, assert_unwind_safe};
16
17#[derive(thiserror::Error)]
18pub enum Error {
19 #[error("PANIC!")]
20 PanicAny(Mutex<Box<dyn Any + Send>>),
21 #[error("PANIC! {0}")]
22 Panic(&'static str, Mutex<Box<dyn Any + Send + 'static>>),
23
24 #[error(transparent)]
26 Fmt(#[from] std::fmt::Error),
27 #[error(transparent)]
28 FromUtf8(#[from] std::string::FromUtf8Error),
29 #[error("I/O error: {0}")]
30 Io(#[from] std::io::Error),
31 #[error(transparent)]
32 ParseFloat(#[from] std::num::ParseFloatError),
33 #[error(transparent)]
34 ParseInt(#[from] std::num::ParseIntError),
35 #[error(transparent)]
36 Std(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
37 #[error(transparent)]
38 SystemTime(#[from] std::time::SystemTimeError),
39 #[error(transparent)]
40 ThreadAccessError(#[from] std::thread::AccessError),
41 #[error(transparent)]
42 TryFromInt(#[from] std::num::TryFromIntError),
43 #[error(transparent)]
44 TryFromSlice(#[from] std::array::TryFromSliceError),
45 #[error(transparent)]
46 Utf8(#[from] std::str::Utf8Error),
47
48 #[error(transparent)]
50 CapacityError(#[from] arrayvec::CapacityError),
51 #[error(transparent)]
52 CargoToml(#[from] cargo_toml::Error),
53 #[error(transparent)]
54 Clap(#[from] clap::error::Error),
55 #[cfg(unix)]
56 #[error(transparent)]
57 Errno(#[from] nix::errno::Errno),
58 #[error(transparent)]
59 Extension(#[from] axum::extract::rejection::ExtensionRejection),
60 #[error(transparent)]
61 Figment(#[from] figment::error::Error),
62 #[error(transparent)]
63 HtmlFormDe(#[from] serde_html_form::de::Error),
64 #[error(transparent)]
65 HtmlFormSer(#[from] serde_html_form::ser::Error),
66 #[error(transparent)]
67 Http(#[from] http::Error),
68 #[error(transparent)]
69 HttpHeader(#[from] http::header::InvalidHeaderValue),
70 #[error("Join error: {0}")]
71 JoinError(#[from] tokio::task::JoinError),
72 #[error(transparent)]
73 Json(#[from] serde_json::Error),
74 #[error(transparent)]
75 JsParseInt(#[from] ruma::JsParseIntError), #[error(transparent)]
77 JsTryFromInt(#[from] ruma::JsTryFromIntError), #[error(transparent)]
79 ObjectStore(#[from] object_store::Error),
80 #[error(transparent)]
81 Path(#[from] axum::extract::rejection::PathRejection),
82 #[error("Mutex poisoned: {0}")]
83 Poison(Cow<'static, str>),
84 #[error("Regex error: {0}")]
85 Regex(#[from] regex::Error),
86 #[error("Request error: {0}")]
87 Reqwest(#[from] reqwest::Error),
88 #[error("{0}")]
89 SerdeDe(Cow<'static, str>),
90 #[error("{0}")]
91 SerdeSer(Cow<'static, str>),
92 #[error(transparent)]
93 TomlDe(#[from] toml::de::Error),
94 #[error(transparent)]
95 TomlSer(#[from] toml::ser::Error),
96 #[error("Tracing filter error: {0}")]
97 TracingFilter(#[from] tracing_subscriber::filter::ParseError),
98 #[error("Tracing reload error: {0}")]
99 TracingReload(#[from] tracing_subscriber::reload::Error),
100 #[error(transparent)]
101 TypedHeader(#[from] axum_extra::typed_header::TypedHeaderRejection),
102 #[error(transparent)]
103 UrlParse(#[from] url::ParseError),
104 #[error(transparent)]
105 Yaml(#[from] serde_yaml::Error),
106
107 #[error("Arithmetic operation failed: {0}")]
109 Arithmetic(Cow<'static, str>),
110 #[error("{0}: {1}")]
111 BadRequest(ruma::api::error::ErrorKind, &'static str), #[error("{0}")]
113 BadServerResponse(Cow<'static, str>),
114 #[error(transparent)]
115 CanonicalJson(#[from] ruma::CanonicalJsonError),
116 #[error("There was a problem with the '{0}' directive in your configuration: {1}")]
117 Config(&'static str, Cow<'static, str>),
118 #[error("{0}")]
119 Conflict(Cow<'static, str>), #[error(transparent)]
121 ContentDisposition(#[from] ruma::http_headers::ContentDispositionParseError),
122 #[error("{0}")]
123 Database(Cow<'static, str>),
124 #[error("Feature '{0}' is not available on this server.")]
125 FeatureDisabled(Cow<'static, str>),
126 #[error("Remote server {0} responded with: {1}")]
127 Federation(ruma::OwnedServerName, ruma::api::error::Error),
128 #[error("{0}: {1:#?}")]
129 HttpJson(http::StatusCode, axum::Json<serde_json::Value>),
130 #[error("{0} in {1}")]
131 InconsistentRoomState(&'static str, ruma::OwnedRoomId),
132 #[error(transparent)]
133 IntoHttp(#[from] ruma::api::error::IntoHttpError),
134 #[error("{0}")]
135 Ldap(Cow<'static, str>),
136 #[error(transparent)]
137 Mxc(#[from] ruma::MxcUriError),
138 #[error(transparent)]
139 Mxid(#[from] ruma::IdParseError),
140 #[error(transparent)]
141 PowerLevels(#[from] ruma::events::room::power_levels::PowerLevelsError),
142 #[error("from {0}: {1}")]
143 Redaction(ruma::OwnedServerName, ruma::canonical_json::CanonicalJsonFieldError),
144 #[error("{0}: {1}")]
145 Request(ruma::api::error::ErrorKind, Cow<'static, str>, http::StatusCode),
146 #[error(transparent)]
147 Ruma(#[from] ruma::api::error::Error),
148 #[error(transparent)]
149 Signatures(#[from] ruma::signatures::VerificationError),
150 #[error(transparent)]
151 SignaturesJson(#[from] ruma::signatures::JsonError),
152 #[error("uiaa")]
153 Uiaa(ruma::api::client::uiaa::UiaaInfo),
154
155 #[error("{0}")]
157 Err(Cow<'static, str>),
158}
159
160static _IS_SEND: () = assert_send::<Error>();
161static _IS_SYNC: () = assert_sync::<Error>();
162static _IS_UNWIND_SAFE: () = assert_unwind_safe::<Error>();
163static _IS_REF_UNWIND_SAFE: () = assert_ref_unwind_safe::<Error>();
164
165impl Error {
166 #[inline]
167 #[must_use]
168 pub fn from_errno() -> Self { Self::Io(std::io::Error::last_os_error()) }
169
170 pub fn bad_database(message: &'static str) -> Self {
172 crate::err!(Database(error!("{message}")))
173 }
174
175 pub fn sanitized_message(&self) -> String {
177 match self {
178 | Self::Database(..) => String::from("Database error occurred."),
179 | Self::Io(..) => String::from("I/O error occurred."),
180 | _ => self.message(),
181 }
182 }
183
184 pub fn message(&self) -> String {
186 match self {
187 | Self::Federation(origin, error) => format!("Answer from {origin}: {error}"),
188 | Self::Ruma(error) => response::ruma_error_message(error),
189 | _ => format!("{self}"),
190 }
191 }
192
193 #[inline]
195 pub fn kind(&self) -> ruma::api::error::ErrorKind {
196 use ruma::api::error::ErrorKind::{FeatureDisabled, NotJson, Unknown};
197
198 match self {
199 | Self::FeatureDisabled(..) => FeatureDisabled,
200 | Self::CanonicalJson(..) | Self::Json(..) => NotJson,
201 | Self::BadRequest(kind, ..) | Self::Request(kind, ..) => kind.clone(),
202 | Self::Federation(_, error) | Self::Ruma(error) =>
203 response::ruma_error_kind(error).clone(),
204 | _ => Unknown,
205 }
206 }
207
208 pub fn status_code(&self) -> http::StatusCode {
211 use http::StatusCode;
212
213 match self {
214 | Self::Conflict(_) => StatusCode::CONFLICT, | Self::Federation(_, error) | Self::Ruma(error) => error.status_code,
216 | Self::FeatureDisabled(..)
217 | Self::CanonicalJson(..)
218 | Self::Json(..)
219 | Self::JsParseInt(..)
220 | Self::JsTryFromInt(..) => response::bad_request_code(&self.kind()),
221 | Self::BadRequest(kind, ..) => response::bad_request_code(kind),
222 | Self::Request(kind, _, code) => response::status_code(kind, *code),
223 | Self::Io(error) => response::io_error_code(error.kind()),
224 | Self::HttpJson(code, ..) => *code,
225 | Self::Reqwest(error) => error
226 .status()
227 .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
228 | _ => StatusCode::INTERNAL_SERVER_ERROR,
229 }
230 }
231
232 #[inline]
237 pub fn is_not_found(&self) -> bool { self.status_code() == http::StatusCode::NOT_FOUND }
238}
239
240impl std::fmt::Debug for Error {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 write!(f, "{}", self.message())
243 }
244}
245
246impl<T> From<PoisonError<T>> for Error {
247 #[cold]
248 #[inline(never)]
249 fn from(e: PoisonError<T>) -> Self { Self::Poison(e.to_string().into()) }
250}
251
252#[expect(clippy::fallible_impl_from)]
253impl From<Infallible> for Error {
254 #[cold]
255 #[inline(never)]
256 fn from(_e: Infallible) -> Self {
257 panic!("infallible error should never exist");
258 }
259}
260
261#[cold]
262#[inline(never)]
263pub fn infallible(_e: &Infallible) {
264 panic!("infallible error should never exist");
265}
266
267#[inline]
269#[must_use]
270#[expect(clippy::needless_pass_by_value)]
271pub fn sanitized_message(e: Error) -> String { e.sanitized_message() }