1use std::error::Error as StdError;
4use std::iter::FromIterator;
5use std::{fmt, time::Duration};
6
7use salvo::http::{Response, StatusCode, header};
8use salvo::writing::Scribe;
9use serde::{Deserialize, Serialize};
10use serde_json::{Map as JsonMap, Value as JsonValue, json};
11
12mod auth;
13pub use auth::*;
14mod kind;
15mod kind_serde;
18pub use kind::*;
19use crate::RoomVersionId;
20
21macro_rules! simple_kind_fns {
22 ($($fname:ident, $kind:ident;)+) => {
23 $(
24 pub fn $fname(body: impl Into<ErrorBody>) -> Self {
26 Self::new(ErrorKind::$kind, body)
27 }
28 )+
29 }
30}
31#[derive(Deserialize, Serialize, Debug, Clone)]
32pub struct ErrorBody(JsonMap<String, JsonValue>);
33
34impl From<String> for ErrorBody {
35 fn from(message: String) -> Self {
36 Self(JsonMap::from_iter(vec![("error".to_owned(), json!(message))]))
37 }
38}
39impl From<&str> for ErrorBody {
40 fn from(message: &str) -> Self {
41 Self(JsonMap::from_iter(vec![("error".to_owned(), json!(message))]))
42 }
43}
44impl From<JsonMap<String, JsonValue>> for ErrorBody {
45 fn from(inner: JsonMap<String, JsonValue>) -> Self {
46 Self(inner)
47 }
48}
49
50#[derive(Debug, Clone)]
52#[allow(clippy::exhaustive_structs)]
53pub struct MatrixError {
54 pub status_code: Option<http::StatusCode>,
56
57 pub authenticate: Option<AuthenticateError>,
59 pub kind: ErrorKind,
60
61 pub body: ErrorBody,
63}
64impl MatrixError {
65 pub fn new(kind: ErrorKind, body: impl Into<ErrorBody>) -> Self {
66 Self {
67 status_code: None,
68 authenticate: None,
69 kind,
70 body: body.into(),
71 }
72 }
73 simple_kind_fns! {
74 forbidden, Forbidden;
75 missing_token, MissingToken;
76 bad_json, BadJson;
77 not_json, NotJson;
78 not_found, NotFound;
79 unknown, Unknown;
80 unrecognized, Unrecognized;
81 unauthorized, Unauthorized;
82 user_deactivated, UserDeactivated;
83 user_in_use, UserInUse;
84 invalid_username, InvalidUsername;
85 room_in_use, RoomInUse;
86 invalid_room_state, InvalidRoomState;
87 threepid_in_use, ThreepidInUse;
88 threepid_not_found, ThreepidNotFound;
89 threepid_auth_failed, ThreepidAuthFailed;
90 threepid_denied, ThreepidDenied;
91 server_not_trusted, ServerNotTrusted;
92 unsupported_room_version, UnsupportedRoomVersion;
93 bad_state, BadState;
94 guest_access_forbidden, GuestAccessForbidden;
95 captcha_needed, CaptchaNeeded;
96 captcha_invalid, CaptchaInvalid;
97 missing_param, MissingParam;
98 invalid_param, InvalidParam;
99 too_large, TooLarge;
100 exclusive, Exclusive;
101 cannot_leave_server_notice_room, CannotLeaveServerNoticeRoom;
102 weak_password, WeakPassword;
103 unable_to_authorize_join, UnableToAuthorizeJoin;
104 unable_to_grant_join, UnableToGrantJoin;
105 bad_alias, BadAlias;
106 duplicate_annotation, DuplicateAnnotation;
107 not_yet_uploaded, NotYetUploaded;
108 cannot_overwrite_media, CannotOverwriteMedia;
109 unknown_pos, UnknownPos;
110 url_not_set, UrlNotSet;
111 bad_status, BadStatus;
112 connection_failed, ConnectionFailed;
113 connection_timeout, ConnectionTimeout;
114 }
115 pub fn unknown_token(soft_logout: bool, body: impl Into<ErrorBody>) -> Self {
116 Self::new(ErrorKind::UnknownToken { soft_logout }, body)
117 }
118 pub fn limit_exceeded(retry_after_ms: Option<Duration>, body: impl Into<ErrorBody>) -> Self {
119 Self::new(ErrorKind::LimitExceeded { retry_after_ms }, body)
120 }
121 pub fn incompatible_room_version(room_version: RoomVersionId, body: impl Into<ErrorBody>) -> Self {
122 Self::new(ErrorKind::IncompatibleRoomVersion { room_version }, body)
123 }
124 pub fn resource_limit_exceeded(admin_contact: String, body: impl Into<ErrorBody>) -> Self {
125 Self::new(ErrorKind::ResourceLimitExceeded { admin_contact }, body)
126 }
127 pub fn wrong_room_keys_version(current_version: Option<String>, body: impl Into<ErrorBody>) -> Self {
128 Self::new(ErrorKind::WrongRoomKeysVersion { current_version }, body)
129 }
130 pub fn is_not_found(&self) -> bool {
131 matches!(self.kind, ErrorKind::NotFound)
132 }
133}
134impl Serialize for MatrixError {
135 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
136 where
137 S: serde::Serializer,
138 {
139 self.body.serialize(serializer)
140 }
141}
142
143impl fmt::Display for MatrixError {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 let code = self.status_code.unwrap_or(StatusCode::BAD_REQUEST).as_u16();
146 write!(f, "[{code} / {}]", self.kind)
147 }
148}
149
150impl StdError for MatrixError {}
151
152impl Scribe for MatrixError {
153 fn render(self, res: &mut Response) {
154 println!("MatrixError {} {:?}", self.to_string(), self.body);
155 res.add_header(header::CONTENT_TYPE, "application/json", true).ok();
156
157 if res.status_code.map(|c| c.is_success()).unwrap_or(true) {
158 let code = self.status_code.unwrap_or_else(|| {
159 use ErrorKind::*;
160 match self.kind.clone() {
161 Forbidden | GuestAccessForbidden | ThreepidAuthFailed | ThreepidDenied => StatusCode::FORBIDDEN,
162 Unauthorized | UnknownToken { .. } | MissingToken => StatusCode::UNAUTHORIZED,
163 NotFound | Unrecognized => StatusCode::NOT_FOUND,
164 LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
165 UserDeactivated => StatusCode::FORBIDDEN,
166 TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
167 CannotOverwriteMedia => StatusCode::CONFLICT,
168 NotYetUploaded => StatusCode::GATEWAY_TIMEOUT,
169 _ => StatusCode::BAD_REQUEST,
170 }
171 });
172 res.status_code(code);
173 }
174
175 if let Some(auth_error) = &self.authenticate {
176 res.add_header(header::WWW_AUTHENTICATE, auth_error, true).ok();
177 };
178
179 let Self { kind, mut body, .. } = self;
180 body.0.insert("errcode".to_owned(), kind.to_string().into());
181
182 let bytes: Vec<u8> = crate::serde::json_to_buf(&body.0).unwrap();
183 res.write_body(bytes).ok();
184 }
185}
186
187#[derive(Debug)]
189pub struct UnknownVersionError;
190
191impl fmt::Display for UnknownVersionError {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 write!(f, "version string was unknown")
194 }
195}
196
197impl StdError for UnknownVersionError {}
198
199