1use std::borrow::Cow;
2use std::mem::Discriminant;
3use std::num::NonZeroU64;
4
5use crate::model::id::*;
6
7#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
9pub struct RatelimitingBucket(Option<(std::mem::Discriminant<Route<'static>>, Option<NonZeroU64>)>);
10
11impl RatelimitingBucket {
12 #[must_use]
13 pub fn is_none(&self) -> bool {
14 self.0.is_none()
15 }
16}
17
18enum RatelimitingKind {
19 PathAndId(NonZeroU64),
22 Path,
24}
25
26macro_rules! routes {
30 ($lt:lifetime, {
31 $(
32 $name:ident $({ $($field_name:ident: $field_type:ty),* })?,
33 $path:expr,
34 $ratelimiting_kind:expr;
35 )+
36 }) => {
37 #[derive(Clone, Copy, Debug)]
38 pub enum Route<$lt> {
39 $(
40 $name $({ $($field_name: $field_type),* })?,
41 )+
42 }
43
44 impl<$lt> Route<$lt> {
45 #[must_use]
46 pub fn path(self) -> Cow<'static, str> {
47 match self {
48 $(
49 Self::$name $({ $($field_name),* })? => $path.into(),
50 )+
51 }
52 }
53
54 #[must_use]
55 pub fn ratelimiting_bucket(&self) -> RatelimitingBucket {
56 #[allow(unused_variables)]
57 let ratelimiting_kind = match *self {
58 $(
59 Self::$name $({ $($field_name),* })? => $ratelimiting_kind,
60 )+
61 };
62
63 let discriminant = unsafe {
66 std::mem::transmute::<Discriminant<Route<'a>>, Discriminant<Route<'static>>>(
67 std::mem::discriminant(self),
68 )
69 };
70
71 RatelimitingBucket(ratelimiting_kind.map(|r| {
72 let id = match r {
73 RatelimitingKind::PathAndId(id) => Some(id),
74 RatelimitingKind::Path => None,
75 };
76 (discriminant, id)
77 }))
78 }
79
80 }
81 };
82}
83
84routes! ('a, {
89 Channel { channel_id: ChannelId },
90 api!("/channels/{}", channel_id),
91 Some(RatelimitingKind::PathAndId(channel_id.into()));
92
93 ChannelInvites { channel_id: ChannelId },
94 api!("/channels/{}/invites", channel_id),
95 Some(RatelimitingKind::PathAndId(channel_id.into()));
96
97 ChannelMessage { channel_id: ChannelId, message_id: MessageId },
98 api!("/channels/{}/messages/{}", channel_id, message_id),
99 Some(RatelimitingKind::PathAndId(channel_id.into()));
100
101 ChannelMessageCrosspost { channel_id: ChannelId, message_id: MessageId },
102 api!("/channels/{}/messages/{}/crosspost", channel_id, message_id),
103 Some(RatelimitingKind::PathAndId(channel_id.into()));
104
105 ChannelMessageReaction { channel_id: ChannelId, message_id: MessageId, user_id: UserId, reaction: &'a str },
106 api!("/channels/{}/messages/{}/reactions/{}/{}", channel_id, message_id, reaction, user_id),
107 Some(RatelimitingKind::PathAndId(channel_id.into()));
108
109 ChannelMessageReactionMe { channel_id: ChannelId, message_id: MessageId, reaction: &'a str },
110 api!("/channels/{}/messages/{}/reactions/{}/@me", channel_id, message_id, reaction),
111 Some(RatelimitingKind::PathAndId(channel_id.into()));
112
113 ChannelMessageReactionEmoji { channel_id: ChannelId, message_id: MessageId, reaction: &'a str },
114 api!("/channels/{}/messages/{}/reactions/{}", channel_id, message_id, reaction),
115 Some(RatelimitingKind::PathAndId(channel_id.into()));
116
117 ChannelMessageReactions { channel_id: ChannelId, message_id: MessageId },
118 api!("/channels/{}/messages/{}/reactions", channel_id, message_id),
119 Some(RatelimitingKind::PathAndId(channel_id.into()));
120
121 ChannelMessages { channel_id: ChannelId },
122 api!("/channels/{}/messages", channel_id),
123 Some(RatelimitingKind::PathAndId(channel_id.into()));
124
125 ChannelMessagesBulkDelete { channel_id: ChannelId },
126 api!("/channels/{}/messages/bulk-delete", channel_id),
127 Some(RatelimitingKind::PathAndId(channel_id.into()));
128
129 ChannelFollowNews { channel_id: ChannelId },
130 api!("/channels/{}/followers", channel_id),
131 Some(RatelimitingKind::PathAndId(channel_id.into()));
132
133 ChannelPermission { channel_id: ChannelId, target_id: TargetId },
134 api!("/channels/{}/permissions/{}", channel_id, target_id),
135 Some(RatelimitingKind::PathAndId(channel_id.into()));
136
137 ChannelPin { channel_id: ChannelId, message_id: MessageId },
138 api!("/channels/{}/pins/{}", channel_id, message_id),
139 Some(RatelimitingKind::PathAndId(channel_id.into()));
140
141 ChannelPins { channel_id: ChannelId },
142 api!("/channels/{}/pins", channel_id),
143 Some(RatelimitingKind::PathAndId(channel_id.into()));
144
145 ChannelTyping { channel_id: ChannelId },
146 api!("/channels/{}/typing", channel_id),
147 Some(RatelimitingKind::PathAndId(channel_id.into()));
148
149 ChannelWebhooks { channel_id: ChannelId },
150 api!("/channels/{}/webhooks", channel_id),
151 Some(RatelimitingKind::PathAndId(channel_id.into()));
152
153 ChannelMessageThreads { channel_id: ChannelId, message_id: MessageId },
154 api!("/channels/{}/messages/{}/threads", channel_id, message_id),
155 Some(RatelimitingKind::PathAndId(channel_id.into()));
156
157 ChannelThreads { channel_id: ChannelId },
158 api!("/channels/{}/threads", channel_id),
159 Some(RatelimitingKind::PathAndId(channel_id.into()));
160
161 ChannelForumPosts { channel_id: ChannelId },
162 api!("/channels/{}/threads", channel_id),
163 Some(RatelimitingKind::PathAndId(channel_id.into()));
164
165 ChannelThreadMember { channel_id: ChannelId, user_id: UserId },
166 api!("/channels/{}/thread-members/{}", channel_id, user_id),
167 Some(RatelimitingKind::PathAndId(channel_id.into()));
168
169 ChannelThreadMemberMe { channel_id: ChannelId },
170 api!("/channels/{}/thread-members/@me", channel_id),
171 Some(RatelimitingKind::PathAndId(channel_id.into()));
172
173 ChannelThreadMembers { channel_id: ChannelId },
174 api!("/channels/{}/thread-members", channel_id),
175 Some(RatelimitingKind::PathAndId(channel_id.into()));
176
177 ChannelArchivedPublicThreads { channel_id: ChannelId },
178 api!("/channels/{}/threads/archived/public", channel_id),
179 Some(RatelimitingKind::PathAndId(channel_id.into()));
180
181 ChannelArchivedPrivateThreads { channel_id: ChannelId },
182 api!("/channels/{}/threads/archived/private", channel_id),
183 Some(RatelimitingKind::PathAndId(channel_id.into()));
184
185 ChannelJoinedPrivateThreads { channel_id: ChannelId },
186 api!("/channels/{}/users/@me/threads/archived/private", channel_id),
187 Some(RatelimitingKind::PathAndId(channel_id.into()));
188
189 ChannelPollGetAnswerVoters { channel_id: ChannelId, message_id: MessageId, answer_id: AnswerId },
190 api!("/channels/{}/polls/{}/answers/{}", channel_id, message_id, answer_id),
191 Some(RatelimitingKind::PathAndId(channel_id.into()));
192
193 ChannelPollExpire { channel_id: ChannelId, message_id: MessageId },
194 api!("/channels/{}/polls/{}/expire", channel_id, message_id),
195 Some(RatelimitingKind::PathAndId(channel_id.into()));
196
197 ChannelVoiceStatus { channel_id: ChannelId },
198 api!("/channels/{}/voice-status", channel_id),
199 Some(RatelimitingKind::PathAndId(channel_id.into()));
200
201 Gateway,
202 api!("/gateway"),
203 Some(RatelimitingKind::Path);
204
205 GatewayBot,
206 api!("/gateway/bot"),
207 Some(RatelimitingKind::Path);
208
209 Guild { guild_id: GuildId },
210 api!("/guilds/{}", guild_id),
211 Some(RatelimitingKind::PathAndId(guild_id.into()));
212
213 GuildAuditLogs { guild_id: GuildId },
214 api!("/guilds/{}/audit-logs", guild_id),
215 Some(RatelimitingKind::PathAndId(guild_id.into()));
216
217 GuildAutomodRule { guild_id: GuildId, rule_id: RuleId },
218 api!("/guilds/{}/auto-moderation/rules/{}", guild_id, rule_id),
219 Some(RatelimitingKind::PathAndId(guild_id.into()));
220
221 GuildAutomodRules { guild_id: GuildId },
222 api!("/guilds/{}/auto-moderation/rules", guild_id),
223 Some(RatelimitingKind::PathAndId(guild_id.into()));
224
225 GuildBan { guild_id: GuildId, user_id: UserId },
226 api!("/guilds/{}/bans/{}", guild_id, user_id),
227 Some(RatelimitingKind::PathAndId(guild_id.into()));
228
229 GuildBulkBan { guild_id: GuildId },
230 api!("/guilds/{}/bulk-ban", guild_id),
231 Some(RatelimitingKind::PathAndId(guild_id.into()));
232
233 GuildBans { guild_id: GuildId },
234 api!("/guilds/{}/bans", guild_id),
235 Some(RatelimitingKind::PathAndId(guild_id.into()));
236
237 GuildChannels { guild_id: GuildId },
238 api!("/guilds/{}/channels", guild_id),
239 Some(RatelimitingKind::PathAndId(guild_id.into()));
240
241 GuildWidget { guild_id: GuildId },
242 api!("/guilds/{}/widget", guild_id),
243 Some(RatelimitingKind::PathAndId(guild_id.into()));
244
245 GuildPreview { guild_id: GuildId },
246 api!("/guilds/{}/preview", guild_id),
247 Some(RatelimitingKind::PathAndId(guild_id.into()));
248
249 GuildEmojis { guild_id: GuildId },
250 api!("/guilds/{}/emojis", guild_id),
251 Some(RatelimitingKind::PathAndId(guild_id.into()));
252
253 GuildEmoji { guild_id: GuildId, emoji_id: EmojiId },
254 api!("/guilds/{}/emojis/{}", guild_id, emoji_id),
255 Some(RatelimitingKind::PathAndId(guild_id.into()));
256
257 GuildIntegration { guild_id: GuildId, integration_id: IntegrationId },
258 api!("/guilds/{}/integrations/{}", guild_id, integration_id),
259 Some(RatelimitingKind::PathAndId(guild_id.into()));
260
261 GuildIntegrationSync { guild_id: GuildId, integration_id: IntegrationId },
262 api!("/guilds/{}/integrations/{}/sync", guild_id, integration_id),
263 Some(RatelimitingKind::PathAndId(guild_id.into()));
264
265 GuildIntegrations { guild_id: GuildId },
266 api!("/guilds/{}/integrations", guild_id),
267 Some(RatelimitingKind::PathAndId(guild_id.into()));
268
269 GuildInvites { guild_id: GuildId },
270 api!("/guilds/{}/invites", guild_id),
271 Some(RatelimitingKind::PathAndId(guild_id.into()));
272
273 GuildMember { guild_id: GuildId, user_id: UserId },
274 api!("/guilds/{}/members/{}", guild_id, user_id),
275 Some(RatelimitingKind::PathAndId(guild_id.into()));
276
277 GuildMemberRole { guild_id: GuildId, user_id: UserId, role_id: RoleId },
278 api!("/guilds/{}/members/{}/roles/{}", guild_id, user_id, role_id),
279 Some(RatelimitingKind::PathAndId(guild_id.into()));
280
281 GuildMembers { guild_id: GuildId },
282 api!("/guilds/{}/members", guild_id),
283 Some(RatelimitingKind::PathAndId(guild_id.into()));
284
285 GuildMembersSearch { guild_id: GuildId },
286 api!("/guilds/{}/members/search", guild_id),
287 Some(RatelimitingKind::PathAndId(guild_id.into()));
288
289 GuildMemberMe { guild_id: GuildId },
290 api!("/guilds/{}/members/@me", guild_id),
291 Some(RatelimitingKind::PathAndId(guild_id.into()));
292
293 GuildMfa { guild_id: GuildId },
294 api!("/guilds/{}/mfa", guild_id),
295 Some(RatelimitingKind::PathAndId(guild_id.into()));
296
297 GuildPrune { guild_id: GuildId },
298 api!("/guilds/{}/prune", guild_id),
299 Some(RatelimitingKind::PathAndId(guild_id.into()));
300
301 GuildRegions { guild_id: GuildId },
302 api!("/guilds/{}/regions", guild_id),
303 Some(RatelimitingKind::PathAndId(guild_id.into()));
304
305 GuildRole { guild_id: GuildId, role_id: RoleId },
306 api!("/guilds/{}/roles/{}", guild_id, role_id),
307 Some(RatelimitingKind::PathAndId(guild_id.into()));
308
309 GuildRoles { guild_id: GuildId },
310 api!("/guilds/{}/roles", guild_id),
311 Some(RatelimitingKind::PathAndId(guild_id.into()));
312
313 GuildScheduledEvent { guild_id: GuildId, event_id: ScheduledEventId },
314 api!("/guilds/{}/scheduled-events/{}", guild_id, event_id),
315 Some(RatelimitingKind::PathAndId(guild_id.into()));
316
317 GuildScheduledEvents { guild_id: GuildId },
318 api!("/guilds/{}/scheduled-events", guild_id),
319 Some(RatelimitingKind::PathAndId(guild_id.into()));
320
321 GuildScheduledEventUsers { guild_id: GuildId, event_id: ScheduledEventId },
322 api!("/guilds/{}/scheduled-events/{}/users", guild_id, event_id),
323 Some(RatelimitingKind::PathAndId(guild_id.into()));
324
325 GuildSticker { guild_id: GuildId, sticker_id: StickerId },
326 api!("/guilds/{}/stickers/{}", guild_id, sticker_id),
327 Some(RatelimitingKind::PathAndId(guild_id.into()));
328
329 GuildStickers { guild_id: GuildId },
330 api!("/guilds/{}/stickers", guild_id),
331 Some(RatelimitingKind::PathAndId(guild_id.into()));
332
333 GuildVanityUrl { guild_id: GuildId },
334 api!("/guilds/{}/vanity-url", guild_id),
335 Some(RatelimitingKind::PathAndId(guild_id.into()));
336
337 GuildVoiceStates { guild_id: GuildId, user_id: UserId },
338 api!("/guilds/{}/voice-states/{}", guild_id, user_id),
339 Some(RatelimitingKind::PathAndId(guild_id.into()));
340
341 GuildVoiceStateMe { guild_id: GuildId },
342 api!("/guilds/{}/voice-states/@me", guild_id),
343 Some(RatelimitingKind::PathAndId(guild_id.into()));
344
345 GuildWebhooks { guild_id: GuildId },
346 api!("/guilds/{}/webhooks", guild_id),
347 Some(RatelimitingKind::PathAndId(guild_id.into()));
348
349 GuildWelcomeScreen { guild_id: GuildId },
350 api!("/guilds/{}/welcome-screen", guild_id),
351 Some(RatelimitingKind::PathAndId(guild_id.into()));
352
353 GuildThreadsActive { guild_id: GuildId },
354 api!("/guilds/{}/threads/active", guild_id),
355 Some(RatelimitingKind::PathAndId(guild_id.into()));
356
357 Guilds,
358 api!("/guilds"),
359 Some(RatelimitingKind::Path);
360
361 Invite { code: &'a str },
362 api!("/invites/{}", code),
363 Some(RatelimitingKind::Path);
364
365 Oauth2ApplicationCurrent,
366 api!("/oauth2/applications/@me"),
367 None;
368
369 StatusIncidentsUnresolved,
370 status!("/incidents/unresolved.json"),
371 None;
372
373 StatusMaintenancesActive,
374 status!("/scheduled-maintenances/active.json"),
375 None;
376
377 StatusMaintenancesUpcoming,
378 status!("/scheduled-maintenances/upcoming.json"),
379 None;
380
381 Sticker { sticker_id: StickerId },
382 api!("/stickers/{}", sticker_id),
383 Some(RatelimitingKind::Path);
384
385 StickerPacks,
386 api!("/sticker-packs"),
387 Some(RatelimitingKind::Path);
388
389 StickerPack { sticker_pack_id: StickerPackId },
390 api!("/sticker-packs/{}", sticker_pack_id),
391 Some(RatelimitingKind::Path);
392
393 User { user_id: UserId },
394 api!("/users/{}", user_id),
395 Some(RatelimitingKind::Path);
396
397 UserMe,
398 api!("/users/@me"),
399 Some(RatelimitingKind::Path);
400
401 UserMeConnections,
402 api!("/users/@me/connections"),
403 Some(RatelimitingKind::Path);
404
405 UserMeDmChannels,
406 api!("/users/@me/channels"),
407 Some(RatelimitingKind::Path);
408
409 UserMeGuild { guild_id: GuildId },
410 api!("/users/@me/guilds/{}", guild_id),
411 Some(RatelimitingKind::Path);
412
413 UserMeGuildMember { guild_id: GuildId },
414 api!("/users/@me/guilds/{}/member", guild_id),
415 Some(RatelimitingKind::Path);
416
417 UserMeGuilds,
418 api!("/users/@me/guilds"),
419 Some(RatelimitingKind::Path);
420
421 VoiceRegions,
422 api!("/voice/regions"),
423 Some(RatelimitingKind::Path);
424
425 Webhook { webhook_id: WebhookId },
426 api!("/webhooks/{}", webhook_id),
427 Some(RatelimitingKind::PathAndId(webhook_id.into()));
428
429 WebhookWithToken { webhook_id: WebhookId, token: &'a str },
430 api!("/webhooks/{}/{}", webhook_id, token),
431 Some(RatelimitingKind::PathAndId(webhook_id.into()));
432
433 WebhookMessage { webhook_id: WebhookId, token: &'a str, message_id: MessageId },
434 api!("/webhooks/{}/{}/messages/{}", webhook_id, token, message_id),
435 Some(RatelimitingKind::PathAndId(webhook_id.into()));
436
437 WebhookOriginalInteractionResponse { application_id: ApplicationId, token: &'a str },
438 api!("/webhooks/{}/{}/messages/@original", application_id, token),
439 Some(RatelimitingKind::PathAndId(application_id.into()));
440
441 WebhookFollowupMessage { application_id: ApplicationId, token: &'a str, message_id: MessageId },
442 api!("/webhooks/{}/{}/messages/{}", application_id, token, message_id),
443 Some(RatelimitingKind::PathAndId(application_id.into()));
444
445 WebhookFollowupMessages { application_id: ApplicationId, token: &'a str },
446 api!("/webhooks/{}/{}", application_id, token),
447 Some(RatelimitingKind::PathAndId(application_id.into()));
448
449 InteractionResponse { interaction_id: InteractionId, token: &'a str },
450 api!("/interactions/{}/{}/callback", interaction_id, token),
451 Some(RatelimitingKind::PathAndId(interaction_id.into()));
452
453 Command { application_id: ApplicationId, command_id: CommandId },
454 api!("/applications/{}/commands/{}", application_id, command_id),
455 Some(RatelimitingKind::PathAndId(application_id.into()));
456
457 Commands { application_id: ApplicationId },
458 api!("/applications/{}/commands", application_id),
459 Some(RatelimitingKind::PathAndId(application_id.into()));
460
461 GuildCommand { application_id: ApplicationId, guild_id: GuildId, command_id: CommandId },
462 api!("/applications/{}/guilds/{}/commands/{}", application_id, guild_id, command_id),
463 Some(RatelimitingKind::PathAndId(application_id.into()));
464
465 GuildCommandPermissions { application_id: ApplicationId, guild_id: GuildId, command_id: CommandId },
466 api!("/applications/{}/guilds/{}/commands/{}/permissions", application_id, guild_id, command_id),
467 Some(RatelimitingKind::PathAndId(application_id.into()));
468
469 GuildCommands { application_id: ApplicationId, guild_id: GuildId },
470 api!("/applications/{}/guilds/{}/commands", application_id, guild_id),
471 Some(RatelimitingKind::PathAndId(application_id.into()));
472
473 GuildCommandsPermissions { application_id: ApplicationId, guild_id: GuildId },
474 api!("/applications/{}/guilds/{}/commands/permissions", application_id, guild_id),
475 Some(RatelimitingKind::PathAndId(application_id.into()));
476
477 Skus { application_id: ApplicationId },
478 api!("/applications/{}/skus", application_id),
479 Some(RatelimitingKind::PathAndId(application_id.into()));
480
481 Emoji { application_id: ApplicationId, emoji_id: EmojiId },
482 api!("/applications/{}/emojis/{}", application_id, emoji_id),
483 Some(RatelimitingKind::PathAndId(application_id.into()));
484
485 Emojis { application_id: ApplicationId },
486 api!("/applications/{}/emojis", application_id),
487 Some(RatelimitingKind::PathAndId(application_id.into()));
488
489 Entitlement { application_id: ApplicationId, entitlement_id: EntitlementId },
490 api!("/applications/{}/entitlements/{}", application_id, entitlement_id),
491 Some(RatelimitingKind::PathAndId(application_id.into()));
492
493 Entitlements { application_id: ApplicationId },
494 api!("/applications/{}/entitlements", application_id),
495 Some(RatelimitingKind::PathAndId(application_id.into()));
496
497 StageInstances,
498 api!("/stage-instances"),
499 Some(RatelimitingKind::Path);
500
501 StageInstance { channel_id: ChannelId },
502 api!("/stage-instances/{}", channel_id),
503 Some(RatelimitingKind::Path);
504});