1use std::fmt::Display;
2use std::panic::Location;
3
4#[cfg(feature = "serde")]
5#[macro_use]
6extern crate serde;
7
8#[cfg(feature = "schemas")]
9#[macro_use]
10extern crate schemars;
11
12#[cfg(feature = "utoipa")]
13#[macro_use]
14extern crate utoipa;
15
16#[cfg(feature = "rocket")]
17pub mod rocket;
18
19#[cfg(feature = "axum")]
20pub mod axum;
21
22#[cfg(feature = "okapi")]
23pub mod okapi;
24
25pub type Result<T, E = Error> = std::result::Result<T, E>;
27
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30#[cfg_attr(feature = "schemas", derive(JsonSchema))]
31#[cfg_attr(feature = "utoipa", derive(ToSchema))]
32#[derive(Debug, Clone)]
33pub struct Error {
34 #[cfg_attr(feature = "serde", serde(flatten))]
36 pub error_type: ErrorType,
37
38 pub location: String,
40}
41
42impl Display for Error {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 write!(f, "{:?} occurred in {}", self.error_type, self.location)
45 }
46}
47
48impl std::error::Error for Error {}
49
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
52#[cfg_attr(feature = "serde", serde(tag = "type"))]
53#[cfg_attr(feature = "schemas", derive(JsonSchema))]
54#[cfg_attr(feature = "utoipa", derive(ToSchema))]
55#[derive(Debug, Clone)]
56pub enum ErrorType {
57 LabelMe,
59
60 AlreadyOnboarded,
62
63 UsernameTaken,
65 InvalidUsername,
66 DiscriminatorChangeRatelimited,
67 UnknownUser,
68 AlreadyFriends,
69 AlreadySentRequest,
70 Blocked,
71 BlockedByOther,
72 NotFriends,
73 TooManyPendingFriendRequests {
74 max: usize,
75 },
76
77 UnknownChannel,
79 UnknownAttachment,
80 UnknownMessage,
81 CannotDeleteMessage,
82 CannotEditMessage,
83 CannotJoinCall,
84 TooManyAttachments {
85 max: usize,
86 },
87 TooManyEmbeds {
88 max: usize,
89 },
90 TooManyReplies {
91 max: usize,
92 },
93 TooManyChannels {
94 max: usize,
95 },
96 EmptyMessage,
97 PayloadTooLarge,
98 CannotRemoveYourself,
99 GroupTooLarge {
100 max: usize,
101 },
102 AlreadyInGroup,
103 NotInGroup,
104 AlreadyPinned,
105 NotPinned,
106 InSlowmode {
107 retry_after: u64,
108 },
109
110 CantCreateServers,
112 UnknownServer,
113 InvalidRole,
114 Banned,
115 TooManyServers {
116 max: usize,
117 },
118 TooManyEmoji {
119 max: usize,
120 },
121 TooManyRoles {
122 max: usize,
123 },
124 AlreadyInServer,
125 CannotTimeoutYourself,
126
127 ReachedMaximumBots,
129 IsBot,
130 IsNotBot,
131 BotIsPrivate,
132
133 CannotReportYourself,
135
136 MissingPermission {
138 permission: String,
139 },
140 MissingUserPermission {
141 permission: String,
142 },
143 NotElevated,
144 NotPrivileged,
145 CannotGiveMissingPermissions,
146 NotOwner,
147 IsElevated,
148
149 DatabaseError {
151 operation: String,
152 collection: String,
153 },
154 InternalError,
155 InvalidOperation,
156 InvalidCredentials,
157 InvalidProperty,
158 InvalidSession,
159 InvalidFlagValue,
160 NotAuthenticated,
161 DuplicateNonce,
162 NotFound,
163 NoEffect,
164 FailedValidation {
165 error: String,
166 },
167
168 LiveKitUnavailable,
170 NotAVoiceChannel,
171 AlreadyConnected,
172 NotConnected,
173 UnknownNode,
174 ProxyError,
176 FileTooSmall,
177 FileTooLarge {
178 max: usize,
179 },
180 FileTypeNotAllowed,
181 ImageProcessingFailed,
182 NoEmbedData,
183
184 VosoUnavailable,
186
187 FeatureDisabled {
189 feature: String,
190 },
191}
192
193#[macro_export]
194macro_rules! create_error {
195 ( $error: ident $( $tt:tt )? ) => {
196 $crate::Error {
197 error_type: $crate::ErrorType::$error $( $tt )?,
198 location: format!("{}:{}:{}", file!(), line!(), column!()),
199 }
200 };
201}
202
203#[macro_export]
204macro_rules! create_database_error {
205 ( $operation: expr, $collection: expr ) => {
206 $crate::create_error!(DatabaseError {
207 operation: $operation.to_string(),
208 collection: $collection.to_string()
209 })
210 };
211}
212
213#[macro_export]
214#[cfg(debug_assertions)]
215macro_rules! query {
216 ( $self: ident, $type: ident, $collection: expr, $($rest:expr),+ ) => {
217 Ok($self.$type($collection, $($rest),+).await.unwrap())
218 };
219}
220
221#[macro_export]
222#[cfg(not(debug_assertions))]
223macro_rules! query {
224 ( $self: ident, $type: ident, $collection: expr, $($rest:expr),+ ) => {
225 $self.$type($collection, $($rest),+).await
226 .map_err(|_| create_database_error!(stringify!($type), $collection))
227 };
228}
229
230pub trait ToRevoltError<T> {
231 #[track_caller]
232 fn to_internal_error(self) -> Result<T, Error>;
233}
234
235impl<T, E: std::fmt::Debug + std::error::Error> ToRevoltError<T> for Result<T, E> {
236 #[track_caller]
237 fn to_internal_error(self) -> Result<T, Error> {
238 let loc = Location::caller();
239
240 self.map_err(|e| {
241 log::error!("{e:?}");
242 #[cfg(feature = "sentry")]
243 sentry::capture_error(&e);
244
245 Error {
246 error_type: ErrorType::InternalError,
247 location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()),
248 }
249 })
250 }
251}
252
253impl<T> ToRevoltError<T> for Option<T> {
254 #[track_caller]
255 fn to_internal_error(self) -> Result<T, Error> {
256 let loc = Location::caller();
257
258 self.ok_or_else(|| Error {
259 error_type: ErrorType::InternalError,
260 location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()),
261 })
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use crate::ErrorType;
268
269 #[test]
270 fn use_macro_to_construct_error() {
271 let error = create_error!(LabelMe);
272 assert!(matches!(error.error_type, ErrorType::LabelMe));
273 }
274
275 #[test]
276 fn use_macro_to_construct_complex_error() {
277 let error = create_error!(LabelMe);
278 assert!(matches!(error.error_type, ErrorType::LabelMe));
279 }
280}