1use std::panic::Location;
2use std::fmt::Display;
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 CannotEditMessage,
82 CannotJoinCall,
83 TooManyAttachments {
84 max: usize,
85 },
86 TooManyEmbeds {
87 max: usize,
88 },
89 TooManyReplies {
90 max: usize,
91 },
92 TooManyChannels {
93 max: usize,
94 },
95 EmptyMessage,
96 PayloadTooLarge,
97 CannotRemoveYourself,
98 GroupTooLarge {
99 max: usize,
100 },
101 AlreadyInGroup,
102 NotInGroup,
103 AlreadyPinned,
104 NotPinned,
105
106 UnknownServer,
108 InvalidRole,
109 Banned,
110 TooManyServers {
111 max: usize,
112 },
113 TooManyEmoji {
114 max: usize,
115 },
116 TooManyRoles {
117 max: usize,
118 },
119 AlreadyInServer,
120 CannotTimeoutYourself,
121
122 ReachedMaximumBots,
124 IsBot,
125 IsNotBot,
126 BotIsPrivate,
127
128 CannotReportYourself,
130
131 MissingPermission {
133 permission: String,
134 },
135 MissingUserPermission {
136 permission: String,
137 },
138 NotElevated,
139 NotPrivileged,
140 CannotGiveMissingPermissions,
141 NotOwner,
142 IsElevated,
143
144 DatabaseError {
146 operation: String,
147 collection: String,
148 },
149 InternalError,
150 InvalidOperation,
151 InvalidCredentials,
152 InvalidProperty,
153 InvalidSession,
154 InvalidFlagValue,
155 NotAuthenticated,
156 DuplicateNonce,
157 NotFound,
158 NoEffect,
159 FailedValidation {
160 error: String,
161 },
162
163 LiveKitUnavailable,
165 NotAVoiceChannel,
166 AlreadyConnected,
167 NotConnected,
168 UnknownNode,
169 ProxyError,
171 FileTooSmall,
172 FileTooLarge {
173 max: usize,
174 },
175 FileTypeNotAllowed,
176 ImageProcessingFailed,
177 NoEmbedData,
178
179 VosoUnavailable,
181
182 FeatureDisabled {
184 feature: String,
185 },
186}
187
188#[macro_export]
189macro_rules! create_error {
190 ( $error: ident $( $tt:tt )? ) => {
191 $crate::Error {
192 error_type: $crate::ErrorType::$error $( $tt )?,
193 location: format!("{}:{}:{}", file!(), line!(), column!()),
194 }
195 };
196}
197
198#[macro_export]
199macro_rules! create_database_error {
200 ( $operation: expr, $collection: expr ) => {
201 $crate::create_error!(DatabaseError {
202 operation: $operation.to_string(),
203 collection: $collection.to_string()
204 })
205 };
206}
207
208#[macro_export]
209#[cfg(debug_assertions)]
210macro_rules! query {
211 ( $self: ident, $type: ident, $collection: expr, $($rest:expr),+ ) => {
212 Ok($self.$type($collection, $($rest),+).await.unwrap())
213 };
214}
215
216#[macro_export]
217#[cfg(not(debug_assertions))]
218macro_rules! query {
219 ( $self: ident, $type: ident, $collection: expr, $($rest:expr),+ ) => {
220 $self.$type($collection, $($rest),+).await
221 .map_err(|_| create_database_error!(stringify!($type), $collection))
222 };
223}
224
225pub trait ToRevoltError<T> {
226 #[track_caller]
227 fn to_internal_error(self) -> Result<T, Error>;
228}
229
230impl<T, E: std::fmt::Debug + std::error::Error> ToRevoltError<T> for Result<T, E> {
231 #[track_caller]
232 fn to_internal_error(self) -> Result<T, Error> {
233 let loc = Location::caller();
234
235 self
236 .map_err(|e| {
237 log::error!("{e:?}");
238 #[cfg(feature = "sentry")]
239 sentry::capture_error(&e);
240
241 Error {
242 error_type: ErrorType::InternalError,
243 location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column())
244 }
245 })
246 }
247}
248
249impl<T> ToRevoltError<T> for Option<T> {
250 #[track_caller]
251 fn to_internal_error(self) -> Result<T, Error> {
252 let loc = Location::caller();
253
254 self.ok_or_else(|| {
255 Error {
256 error_type: ErrorType::InternalError,
257 location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column())
258 }
259 })
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use crate::ErrorType;
266
267 #[test]
268 fn use_macro_to_construct_error() {
269 let error = create_error!(LabelMe);
270 assert!(matches!(error.error_type, ErrorType::LabelMe));
271 }
272
273 #[test]
274 fn use_macro_to_construct_complex_error() {
275 let error = create_error!(LabelMe);
276 assert!(matches!(error.error_type, ErrorType::LabelMe));
277 }
278}